clean spotify commands
All checks were successful
Deploy to Dokku / docker (ubuntu-latest) (push) Successful in 1m17s

This commit is contained in:
Lee 2024-07-05 23:59:27 +01:00
parent ec54d5427e
commit 8cc465e53d
6 changed files with 89 additions and 128 deletions

@ -1,10 +0,0 @@
package cc.fascinated.bat.exception;
/**
* @author Fascinated (fascinated7)
*/
public class BatException extends Exception {
public BatException(String message) {
super(message);
}
}

@ -3,7 +3,6 @@ package cc.fascinated.bat.features.spotify;
import cc.fascinated.bat.Emojis; import cc.fascinated.bat.Emojis;
import cc.fascinated.bat.common.EmbedUtils; import cc.fascinated.bat.common.EmbedUtils;
import cc.fascinated.bat.common.SpotifyUtils; import cc.fascinated.bat.common.SpotifyUtils;
import cc.fascinated.bat.exception.BatException;
import cc.fascinated.bat.features.Feature; import cc.fascinated.bat.features.Feature;
import cc.fascinated.bat.features.FeatureProfile; import cc.fascinated.bat.features.FeatureProfile;
import cc.fascinated.bat.features.spotify.command.SpotifyCommand; import cc.fascinated.bat.features.spotify.command.SpotifyCommand;
@ -17,6 +16,7 @@ import net.dv8tion.jda.api.EmbedBuilder;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import se.michaelthelin.spotify.enums.Action;
import se.michaelthelin.spotify.model_objects.miscellaneous.CurrentlyPlaying; import se.michaelthelin.spotify.model_objects.miscellaneous.CurrentlyPlaying;
import se.michaelthelin.spotify.model_objects.specification.AlbumSimplified; import se.michaelthelin.spotify.model_objects.specification.AlbumSimplified;
import se.michaelthelin.spotify.model_objects.specification.Image; import se.michaelthelin.spotify.model_objects.specification.Image;
@ -34,6 +34,25 @@ public class SpotifyFeature extends Feature {
super.registerCommand(commandService, context.getBean(SpotifyCommand.class)); super.registerCommand(commandService, context.getBean(SpotifyCommand.class));
} }
/**
* Pre-checks for Spotify commands.
*
* @param spotifyService The Spotify service.
* @param user The user.
*/
public static EmbedBuilder checkSpotify(@NonNull SpotifyService spotifyService, @NonNull BatUser user) {
SpotifyProfile profile = user.getProfile(SpotifyProfile.class);
if (!profile.hasLinkedAccount()) {
return EmbedUtils.errorEmbed()
.setDescription("%s You need to link your Spotify account before you can use this command.".formatted(Emojis.CROSS_MARK_EMOJI));
}
if (!spotifyService.hasTrackPlaying(user)) {
return EmbedUtils.errorEmbed()
.setDescription("%s You need to have Spotify Premium to use this command.".formatted(Emojis.CROSS_MARK_EMOJI));
}
return null;
}
/** /**
* Gets the currently playing song. * Gets the currently playing song.
* *
@ -43,12 +62,8 @@ public class SpotifyFeature extends Feature {
@SneakyThrows @SneakyThrows
public static EmbedBuilder currentSong(@NonNull SpotifyService spotifyService, @NonNull BatUser user) { public static EmbedBuilder currentSong(@NonNull SpotifyService spotifyService, @NonNull BatUser user) {
SpotifyProfile profile = user.getProfile(SpotifyProfile.class); SpotifyProfile profile = user.getProfile(SpotifyProfile.class);
if (!profile.hasLinkedAccount()) { if (!profile.hasLinkedAccount() || !spotifyService.hasTrackPlaying(user)) {
throw new BatException("%s You need to link your Spotify account before you can use this command.".formatted(Emojis.CROSS_MARK_EMOJI)); return checkSpotify(spotifyService, user);
}
if (!spotifyService.hasTrackPlaying(user)) {
throw new BatException("%s You are not currently playing a track.".formatted(Emojis.CROSS_MARK_EMOJI));
} }
CurrentlyPlaying currentlyPlaying = spotifyService.getCurrentlyPlayingTrack(user); CurrentlyPlaying currentlyPlaying = spotifyService.getCurrentlyPlayingTrack(user);
@ -92,12 +107,8 @@ public class SpotifyFeature extends Feature {
@SneakyThrows @SneakyThrows
public static EmbedBuilder skipSong(@NonNull SpotifyService spotifyService, @NonNull BatUser user) { public static EmbedBuilder skipSong(@NonNull SpotifyService spotifyService, @NonNull BatUser user) {
SpotifyProfile profile = user.getProfile(SpotifyProfile.class); SpotifyProfile profile = user.getProfile(SpotifyProfile.class);
if (!profile.hasLinkedAccount()) { if (!profile.hasLinkedAccount() || !spotifyService.hasTrackPlaying(user)) {
throw new BatException("%s You need to link your Spotify account before you can use this command.".formatted(Emojis.CROSS_MARK_EMOJI)); return checkSpotify(spotifyService, user);
}
if (!spotifyService.hasTrackPlaying(user)) {
throw new BatException("%s You are not currently playing a track.".formatted(Emojis.CROSS_MARK_EMOJI));
} }
CurrentlyPlaying currentlyPlaying = spotifyService.getCurrentlyPlayingTrack(user); CurrentlyPlaying currentlyPlaying = spotifyService.getCurrentlyPlayingTrack(user);
@ -131,23 +142,26 @@ public class SpotifyFeature extends Feature {
@SneakyThrows @SneakyThrows
public static EmbedBuilder pauseSong(@NonNull SpotifyService spotifyService, @NonNull BatUser user) { public static EmbedBuilder pauseSong(@NonNull SpotifyService spotifyService, @NonNull BatUser user) {
SpotifyProfile profile = user.getProfile(SpotifyProfile.class); SpotifyProfile profile = user.getProfile(SpotifyProfile.class);
if (!profile.hasLinkedAccount()) { if (!profile.hasLinkedAccount() || !spotifyService.hasTrackPlaying(user)) {
throw new BatException("%s You need to link your Spotify account before you can use this command.".formatted(Emojis.CROSS_MARK_EMOJI)); return checkSpotify(spotifyService, user);
} }
if (!spotifyService.hasTrackPlaying(user)) {
throw new BatException("%s You are not currently playing a track.".formatted(Emojis.CROSS_MARK_EMOJI));
}
boolean didPause = spotifyService.pausePlayback(user);
CurrentlyPlaying currentlyPlaying = spotifyService.getCurrentlyPlayingTrack(user); CurrentlyPlaying currentlyPlaying = spotifyService.getCurrentlyPlayingTrack(user);
for (Action action : currentlyPlaying.getActions().getDisallows().getDisallowedActions()) {
if (action.equals(Action.PAUSING)) {
return EmbedUtils.errorEmbed()
.setDescription("%s This track is already paused.".formatted(Emojis.CROSS_MARK_EMOJI));
}
}
spotifyService.pausePlayback(user);
Track track = (Track) currentlyPlaying.getItem(); Track track = (Track) currentlyPlaying.getItem();
return EmbedUtils.successEmbed() return EmbedUtils.successEmbed()
.setDescription(didPause ? ":pause_button: Paused the track **[%s | %s](%s)**".formatted( .setDescription("%s Paused the track **[%s | %s](%s)**".formatted(
Emojis.CHECK_MARK_EMOJI,
track.getName(), track.getName(),
track.getArtists()[0].getName(), track.getArtists()[0].getName(),
SpotifyUtils.getTrackUrl(currentlyPlaying)) SpotifyUtils.getTrackUrl(currentlyPlaying)
: "%s The current track is already paused.".formatted(Emojis.CROSS_MARK_EMOJI)); ));
} }
/** /**
@ -159,22 +173,25 @@ public class SpotifyFeature extends Feature {
@SneakyThrows @SneakyThrows
public static EmbedBuilder resumeSong(@NonNull SpotifyService spotifyService, @NonNull BatUser user) { public static EmbedBuilder resumeSong(@NonNull SpotifyService spotifyService, @NonNull BatUser user) {
SpotifyProfile profile = user.getProfile(SpotifyProfile.class); SpotifyProfile profile = user.getProfile(SpotifyProfile.class);
if (!profile.hasLinkedAccount()) { if (!profile.hasLinkedAccount() || !spotifyService.hasTrackPlaying(user)) {
throw new BatException("%s You need to link your Spotify account before you can use this command.".formatted(Emojis.CROSS_MARK_EMOJI)); return checkSpotify(spotifyService, user);
} }
if (!spotifyService.hasTrackPlaying(user)) {
throw new BatException("%s You are not currently playing a track.".formatted(Emojis.CROSS_MARK_EMOJI));
}
boolean didResume = spotifyService.resumePlayback(user);
CurrentlyPlaying currentlyPlaying = spotifyService.getCurrentlyPlayingTrack(user); CurrentlyPlaying currentlyPlaying = spotifyService.getCurrentlyPlayingTrack(user);
for (Action action : currentlyPlaying.getActions().getDisallows().getDisallowedActions()) {
if (action.equals(Action.RESUMING)) {
return EmbedUtils.errorEmbed()
.setDescription("%s This track is already playing.".formatted(Emojis.CROSS_MARK_EMOJI));
}
}
spotifyService.resumePlayback(user);
Track track = (Track) currentlyPlaying.getItem(); Track track = (Track) currentlyPlaying.getItem();
return EmbedUtils.successEmbed() return EmbedUtils.successEmbed()
.setDescription(didResume ? ":play_pause: Resumed the track **[%s | %s](%s)**".formatted( .setDescription("%s Resumed the track **[%s | %s](%s)**".formatted(
Emojis.CHECK_MARK_EMOJI,
track.getName(), track.getName(),
track.getArtists()[0].getName(), track.getArtists()[0].getName(),
SpotifyUtils.getTrackUrl(currentlyPlaying)) SpotifyUtils.getTrackUrl(currentlyPlaying)
: "%s The current track is already playing.".formatted(Emojis.CROSS_MARK_EMOJI)); ));
} }
} }

@ -1,30 +1,20 @@
package cc.fascinated.bat.features.spotify.command; package cc.fascinated.bat.features.spotify.command;
import cc.fascinated.bat.Emojis;
import cc.fascinated.bat.command.BatCommand; import cc.fascinated.bat.command.BatCommand;
import cc.fascinated.bat.command.CommandInfo; import cc.fascinated.bat.command.CommandInfo;
import cc.fascinated.bat.event.EventListener; import cc.fascinated.bat.event.EventListener;
import cc.fascinated.bat.exception.BatException;
import cc.fascinated.bat.features.spotify.SpotifyFeature; import cc.fascinated.bat.features.spotify.SpotifyFeature;
import cc.fascinated.bat.model.BatGuild; import cc.fascinated.bat.model.BatGuild;
import cc.fascinated.bat.model.BatUser; import cc.fascinated.bat.model.BatUser;
import cc.fascinated.bat.service.SpotifyService; import cc.fascinated.bat.service.SpotifyService;
import lombok.NonNull; import lombok.NonNull;
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction; import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction;
import net.dv8tion.jda.api.interactions.components.ActionRow;
import net.dv8tion.jda.api.interactions.components.LayoutComponent;
import net.dv8tion.jda.api.interactions.components.buttons.Button;
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 java.util.ArrayList;
import java.util.concurrent.TimeUnit;
/** /**
* @author Fascinated (fascinated7) * @author Fascinated (fascinated7)
*/ */
@ -41,64 +31,6 @@ public class CurrentSubCommand extends BatCommand implements EventListener {
@Override @Override
public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction event) { public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction event) {
event.replyEmbeds(SpotifyFeature.currentSong(spotifyService, user).build()).addComponents(createActions()).queue(message -> { event.replyEmbeds(SpotifyFeature.currentSong(spotifyService, user).build()).queue();
message.editOriginalComponents(new ArrayList<>()).queueAfter(5, TimeUnit.MINUTES); // Remove the buttons after 5 minutes
});
}
@Override @SneakyThrows
public void onButtonInteraction(BatGuild guild, @NonNull BatUser user, @NonNull ButtonInteractionEvent event) {
if (guild == null) {
return;
}
if (!event.getUser().getId().equals(user.getId())) {
throw new BatException("%s You cannot interact with this message.".formatted(Emojis.CROSS_MARK_EMOJI));
}
if (!event.getComponentId().equals("pause")
&& !event.getComponentId().equals("resume")
&& !event.getComponentId().equals("skip")) {
return;
}
switch (event.getComponentId()) {
case "pause" -> {
event.replyEmbeds(SpotifyFeature.pauseSong(spotifyService, user)
.appendDescription("\nThis message will disappear <t:%s:R>".formatted(System.currentTimeMillis() / 1000 + 15)).build())
.queue(message -> {
message.deleteOriginal().queueAfter(15, TimeUnit.SECONDS);
});
}
case "resume" -> {
event.replyEmbeds(SpotifyFeature.resumeSong(spotifyService, user)
.appendDescription("\nThis message will disappear <t:%s:R>".formatted(System.currentTimeMillis() / 1000 + 15)).build())
.queue(message -> {
message.deleteOriginal().queueAfter(15, TimeUnit.SECONDS);
});
}
case "skip" -> {
event.replyEmbeds(SpotifyFeature.skipSong(spotifyService, user)
.appendDescription("\nThis message will disappear <t:%s:R>".formatted(System.currentTimeMillis() / 1000 + 15)).build())
.queue(then -> {
then.deleteOriginal().queueAfter(15, TimeUnit.SECONDS);
event.getInteraction().getMessage().editMessageEmbeds(SpotifyFeature.currentSong(spotifyService, user).build()).queue();
});
}
}
}
/**
* Creates the action buttons for the currently playing track
*
* @return The action buttons
*/
private LayoutComponent[] createActions() {
return new LayoutComponent[]{
ActionRow.of(
Button.primary("pause", "Pause").withEmoji(Emojis.PAUSE_EMOJI),
Button.primary("resume", "Resume").withEmoji(Emojis.PLAY_EMOJI),
Button.primary("skip", "Skip").withEmoji(Emojis.SKIP_EMOJI)
)
};
} }
} }

@ -6,7 +6,7 @@ import cc.fascinated.bat.command.BatCommand;
import cc.fascinated.bat.command.CommandInfo; import cc.fascinated.bat.command.CommandInfo;
import cc.fascinated.bat.common.EmbedUtils; import cc.fascinated.bat.common.EmbedUtils;
import cc.fascinated.bat.event.EventListener; import cc.fascinated.bat.event.EventListener;
import cc.fascinated.bat.exception.BatException; import cc.fascinated.bat.features.spotify.SpotifyFeature;
import cc.fascinated.bat.features.spotify.profile.SpotifyProfile; import cc.fascinated.bat.features.spotify.profile.SpotifyProfile;
import cc.fascinated.bat.model.BatGuild; import cc.fascinated.bat.model.BatGuild;
import cc.fascinated.bat.model.BatUser; import cc.fascinated.bat.model.BatUser;
@ -43,14 +43,20 @@ public class LinkSubCommand extends BatCommand implements EventListener {
@Override @SneakyThrows @Override @SneakyThrows
public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction event) { public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction event) {
if (!user.getId().equals(Consts.BOT_OWNER)) { if (!user.getId().equals(Consts.BOT_OWNER)) {
throw new BatException(""" event.replyEmbeds(EmbedUtils.errorEmbed()
.setDescription("""
%s We are currently awaiting Spotify's approval for our application. Please check back later. %s We are currently awaiting Spotify's approval for our application. Please check back later.
Submitted on: <t:1719583353> Submitted on: <t:1719583353>
""".formatted(Emojis.CROSS_MARK_EMOJI)); """.formatted(Emojis.CROSS_MARK_EMOJI))
.build())
.setEphemeral(true)
.queue();
return;
} }
SpotifyProfile profile = user.getProfile(SpotifyProfile.class); SpotifyProfile profile = user.getProfile(SpotifyProfile.class);
if (profile.hasLinkedAccount()) { if (!profile.hasLinkedAccount() || !spotifyService.hasTrackPlaying(user)) {
throw new BatException("%s You have already linked your Spotify account.".formatted(Emojis.CROSS_MARK_EMOJI)); event.replyEmbeds(SpotifyFeature.checkSpotify(spotifyService, user).build()).queue();
return;
} }
event.replyEmbeds(EmbedUtils.genericEmbed() event.replyEmbeds(EmbedUtils.genericEmbed()
@ -97,7 +103,12 @@ public class LinkSubCommand extends BatCommand implements EventListener {
} }
String code = codeMapping.getAsString(); String code = codeMapping.getAsString();
if (!spotifyService.isValidLinkCode(code)) { if (!spotifyService.isValidLinkCode(code)) {
throw new BatException("%s The link code you provided is invalid.".formatted(Emojis.CROSS_MARK_EMOJI)); event.replyEmbeds(EmbedUtils.errorEmbed()
.setDescription("%s The link code you provided is invalid.".formatted(Emojis.CROSS_MARK_EMOJI))
.build())
.setEphemeral(true)
.queue();
return;
} }
spotifyService.linkAccount(user, code); spotifyService.linkAccount(user, code);

@ -5,15 +5,17 @@ import cc.fascinated.bat.command.BatCommand;
import cc.fascinated.bat.command.CommandInfo; import cc.fascinated.bat.command.CommandInfo;
import cc.fascinated.bat.common.EmbedUtils; import cc.fascinated.bat.common.EmbedUtils;
import cc.fascinated.bat.event.EventListener; import cc.fascinated.bat.event.EventListener;
import cc.fascinated.bat.exception.BatException; import cc.fascinated.bat.features.spotify.SpotifyFeature;
import cc.fascinated.bat.features.spotify.profile.SpotifyProfile; import cc.fascinated.bat.features.spotify.profile.SpotifyProfile;
import cc.fascinated.bat.model.BatGuild; import cc.fascinated.bat.model.BatGuild;
import cc.fascinated.bat.model.BatUser; import cc.fascinated.bat.model.BatUser;
import cc.fascinated.bat.service.SpotifyService;
import lombok.NonNull; import lombok.NonNull;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; 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.stereotype.Component; import org.springframework.stereotype.Component;
/** /**
@ -22,11 +24,19 @@ import org.springframework.stereotype.Component;
@Component @Component
@CommandInfo(name = "unlink", description = "Unlink your Spotify account") @CommandInfo(name = "unlink", description = "Unlink your Spotify account")
public class UnlinkSubCommand extends BatCommand implements EventListener { public class UnlinkSubCommand extends BatCommand implements EventListener {
private final SpotifyService spotifyService;
@Autowired
public UnlinkSubCommand(@NonNull SpotifyService spotifyService) {
this.spotifyService = spotifyService;
}
@Override @SneakyThrows @Override @SneakyThrows
public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction event) { public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction event) {
SpotifyProfile profile = user.getProfile(SpotifyProfile.class); SpotifyProfile profile = user.getProfile(SpotifyProfile.class);
if (!profile.hasLinkedAccount()) { if (!profile.hasLinkedAccount() || !spotifyService.hasTrackPlaying(user)) {
throw new BatException("%s You do not have a linked Spotify account.".formatted(Emojis.CROSS_MARK_EMOJI)); event.replyEmbeds(SpotifyFeature.checkSpotify(spotifyService, user).build()).queue();
return;
} }
profile.reset(); profile.reset();

@ -119,9 +119,8 @@ public class SpotifyService {
* @return if the playback was paused * @return if the playback was paused
*/ */
@SneakyThrows @SneakyThrows
public boolean pausePlayback(BatUser user) { public void pausePlayback(BatUser user) {
getSpotifyApi(user).pauseUsersPlayback().build().execute(); getSpotifyApi(user).pauseUsersPlayback().build().execute();
return true;
} }
/** /**
@ -131,9 +130,8 @@ public class SpotifyService {
* @return if the playback was paused * @return if the playback was paused
*/ */
@SneakyThrows @SneakyThrows
public boolean resumePlayback(BatUser user) { public void resumePlayback(BatUser user) {
getSpotifyApi(user).startResumeUsersPlayback().build().execute(); getSpotifyApi(user).startResumeUsersPlayback().build().execute();
return true;
} }
/** /**
@ -173,12 +171,15 @@ public class SpotifyService {
/** /**
* Ensures the user has a valid Spotify access token. * Ensures the user has a valid Spotify access token.
* <p>
* If the token is expired, it will be refreshed.
* </p>
* *
* @param user the user to get the token for * @param user the user to get the token for
*/ */
@SneakyThrows @SneakyThrows
public void ensureValidToken(SpotifyProfile profile, BatUser user) { public void ensureValidToken(SpotifyProfile profile, BatUser user) {
if (profile.getExpiresAt() == null || profile.getExpiresAt() > System.currentTimeMillis()) { if (profile.getExpiresAt() == null || profile.getExpiresAt() < System.currentTimeMillis()) {
return; return;
} }
SpotifyApi api = new SpotifyApi.Builder() SpotifyApi api = new SpotifyApi.Builder()