many changes
All checks were successful
Deploy to Dokku / docker (ubuntu-latest) (push) Successful in 35s

This commit is contained in:
Lee 2024-06-29 22:38:53 +01:00
parent b0949d17e6
commit 86c7afac42
11 changed files with 314 additions and 201 deletions

@ -14,6 +14,9 @@ public class Emojis {
public static final Emoji CHECK_MARK_EMOJI; public static final Emoji CHECK_MARK_EMOJI;
public static final Emoji CROSS_MARK_EMOJI; public static final Emoji CROSS_MARK_EMOJI;
public static final Emoji SAD_FACE_EMOJI; public static final Emoji SAD_FACE_EMOJI;
public static final Emoji PAUSE_EMOJI;
public static final Emoji PLAY_EMOJI;
public static final Emoji SKIP_EMOJI;
/** /**
* Presence Status Emojis * Presence Status Emojis
@ -34,6 +37,9 @@ public class Emojis {
IDLE_EMOJI = jda.getEmojiById("1256662632203685991"); IDLE_EMOJI = jda.getEmojiById("1256662632203685991");
DND_EMOJI = jda.getEmojiById("1256662572933845032"); DND_EMOJI = jda.getEmojiById("1256662572933845032");
OFFLINE_EMOJI = jda.getEmojiById("1256662679402053662"); OFFLINE_EMOJI = jda.getEmojiById("1256662679402053662");
PAUSE_EMOJI = Emoji.fromUnicode("");
PLAY_EMOJI = Emoji.fromUnicode("");
SKIP_EMOJI = Emoji.fromUnicode("");
log.info("Loaded emojis!"); log.info("Loaded emojis!");
} }
} }

@ -0,0 +1,69 @@
package cc.fascinated.bat.common;
import cc.fascinated.bat.model.BatUser;
import cc.fascinated.bat.service.SpotifyService;
import lombok.NonNull;
import lombok.extern.log4j.Log4j2;
import se.michaelthelin.spotify.model_objects.miscellaneous.CurrentlyPlaying;
import se.michaelthelin.spotify.model_objects.specification.Track;
/**
* @author Fascinated (fascinated7)
*/
@Log4j2
public class SpotifyUtils {
/**
* Gets the URL of the track that is currently playing.
*
* @param currentlyPlaying The currently playing object.
* @return The URL of the track that is currently playing.
*/
public static String getTrackUrl(CurrentlyPlaying currentlyPlaying) {
return "https://open.spotify.com/track/" + currentlyPlaying.getItem().getId();
}
/**
* Gets the formatted time of the currently playing track
*
* @param currentlyPlaying the currently playing track
* @return the formatted time
*/
public static String getFormattedTime(@NonNull CurrentlyPlaying currentlyPlaying) {
Track track = (Track) currentlyPlaying.getItem();
int currentMinutes = currentlyPlaying.getProgress_ms() / 1000 / 60;
int currentSeconds = currentlyPlaying.getProgress_ms() / 1000 % 60;
int totalMinutes = track.getDurationMs() / 1000 / 60;
int totalSeconds = track.getDurationMs() / 1000 % 60;
return "`%02d:%02d`/`%02d:%02d`".formatted(currentMinutes, currentSeconds, totalMinutes, totalSeconds);
}
/**
* 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 static CurrentlyPlaying getNewTrack(@NonNull SpotifyService spotifyService, @NonNull BatUser user, @NonNull 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 currentlyPlaying;
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
}

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

@ -3,10 +3,25 @@ package cc.fascinated.bat.features.spotify;
import cc.fascinated.bat.Emojis; import cc.fascinated.bat.Emojis;
import cc.fascinated.bat.command.Category; import cc.fascinated.bat.command.Category;
import cc.fascinated.bat.common.EmbedUtils; import cc.fascinated.bat.common.EmbedUtils;
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.spotify.profile.SpotifyProfile;
import cc.fascinated.bat.model.BatUser;
import cc.fascinated.bat.service.SpotifyService;
import com.fasterxml.jackson.datatype.jsr310.deser.JSR310DateTimeDeserializerBase;
import lombok.NonNull;
import lombok.SneakyThrows;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction;
import net.dv8tion.jda.api.interactions.components.ComponentInteraction;
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.AlbumSimplified;
import se.michaelthelin.spotify.model_objects.specification.Image;
import se.michaelthelin.spotify.model_objects.specification.Track;
/** /**
* @author Fascinated (fascinated7) * @author Fascinated (fascinated7)
@ -19,13 +34,146 @@ public class SpotifyFeature extends Feature {
} }
/** /**
* The embed for when a user needs to link their Spotify account. * Gets the currently playing song.
* *
* @return The embed. * @param spotifyService The Spotify service.
* @param user The user.
*/ */
public static MessageEmbed linkAccountEmbed() { @SneakyThrows
public static EmbedBuilder currentSong(@NonNull SpotifyService spotifyService, @NonNull BatUser user) {
SpotifyProfile profile = user.getProfile(SpotifyProfile.class);
if (!profile.hasLinkedAccount()) {
throw new BatException("%s You need to link your Spotify account before you can use this command.".formatted(Emojis.CROSS_MARK_EMOJI));
}
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);
Track track = (Track) currentlyPlaying.getItem();
AlbumSimplified album = track.getAlbum();
String trackUrl = SpotifyUtils.getTrackUrl(currentlyPlaying);
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(", ");
}
}
Image albumCover = album.getImages()[0];
return EmbedUtils.genericEmbed() return EmbedUtils.genericEmbed()
.setDescription("%s You need to link your Spotify account before you can use this command.".formatted(Emojis.CROSS_MARK_EMOJI)) .setAuthor("Listening to %s | %s".formatted(track.getName(), track.getArtists()[0].getName()), trackUrl)
.build(); .setThumbnail(albumCover.getUrl())
.setDescription("""
Song: **[%s](%s)**
Album: **[%s](%s)**
Artist%s: %s
Position: %s
""".formatted(
track.getName(), trackUrl,
album.getName(), albumUrl,
track.getArtists().length > 1 ? "s" : "", artists,
SpotifyUtils.getFormattedTime(currentlyPlaying)
));
}
/**
* Skips the current song.
*
* @param spotifyService The Spotify service.
* @param user The user.
*/
@SneakyThrows
public static EmbedBuilder skipSong(@NonNull SpotifyService spotifyService, @NonNull BatUser user) {
SpotifyProfile profile = user.getProfile(SpotifyProfile.class);
if (!profile.hasLinkedAccount()) {
throw new BatException("%s You need to link your Spotify account before you can use this command.".formatted(Emojis.CROSS_MARK_EMOJI));
}
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);
Track track = (Track) currentlyPlaying.getItem();
String trackName = track.getName();
spotifyService.skipTrack(user);
CurrentlyPlaying newCurrentlyPlaying = SpotifyUtils.getNewTrack(spotifyService, user, trackName);
if (newCurrentlyPlaying == null) {
return EmbedUtils.errorEmbed()
.setDescription("%s There are no more tracks in the queue.".formatted(Emojis.CROSS_MARK_EMOJI));
}
Track newTrack = (Track) newCurrentlyPlaying.getItem();
return EmbedUtils.successEmbed()
.setDescription("""
:track_next: Skipped the track: **[%s | %s](%s)**
%s New Track: **[%s | %s](%s)**
""".formatted(
trackName, track.getArtists()[0].getName(), SpotifyUtils.getTrackUrl(currentlyPlaying),
Emojis.CHECK_MARK_EMOJI, newTrack.getName(), newTrack.getArtists()[0].getName(), SpotifyUtils.getTrackUrl(newCurrentlyPlaying)
));
}
/**
* Pauses the current song.
*
* @param spotifyService The Spotify service.
* @param user The user.
*/
@SneakyThrows
public static EmbedBuilder pauseSong(@NonNull SpotifyService spotifyService, @NonNull BatUser user) {
SpotifyProfile profile = user.getProfile(SpotifyProfile.class);
if (!profile.hasLinkedAccount()) {
throw new BatException("%s You need to link your Spotify account before you can use this command.".formatted(Emojis.CROSS_MARK_EMOJI));
}
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);
Track track = (Track) currentlyPlaying.getItem();
return EmbedUtils.successEmbed()
.setDescription(didPause ? ":pause_button: Paused the track **[%s | %s](%s)**".formatted(
track.getName(),
track.getArtists()[0].getName(),
SpotifyUtils.getTrackUrl(currentlyPlaying))
: "%s The current track is already paused.".formatted(Emojis.CROSS_MARK_EMOJI));
}
/**
* Resumes the current song.
*
* @param spotifyService The Spotify service.
* @param user The user.
*/
@SneakyThrows
public static EmbedBuilder resumeSong(@NonNull SpotifyService spotifyService, @NonNull BatUser user) {
SpotifyProfile profile = user.getProfile(SpotifyProfile.class);
if (!profile.hasLinkedAccount()) {
throw new BatException("%s You need to link your Spotify account before you can use this command.".formatted(Emojis.CROSS_MARK_EMOJI));
}
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);
Track track = (Track) currentlyPlaying.getItem();
return EmbedUtils.successEmbed()
.setDescription(didResume ? ":play_pause: Resumed the track **[%s | %s](%s)**".formatted(
track.getName(),
track.getArtists()[0].getName(),
SpotifyUtils.getTrackUrl(currentlyPlaying))
: "%s The current track is already playing.".formatted(Emojis.CROSS_MARK_EMOJI));
} }
} }

@ -4,22 +4,27 @@ import cc.fascinated.bat.Emojis;
import cc.fascinated.bat.command.BatSubCommand; import cc.fascinated.bat.command.BatSubCommand;
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.exception.BatException;
import cc.fascinated.bat.features.spotify.SpotifyFeature; 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 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 se.michaelthelin.spotify.model_objects.miscellaneous.CurrentlyPlaying;
import se.michaelthelin.spotify.model_objects.specification.AlbumSimplified; import java.util.concurrent.TimeUnit;
import se.michaelthelin.spotify.model_objects.specification.Image;
import se.michaelthelin.spotify.model_objects.specification.Track;
/** /**
* @author Fascinated (fascinated7) * @author Fascinated (fascinated7)
@ -27,7 +32,7 @@ import se.michaelthelin.spotify.model_objects.specification.Track;
@Component @Component
@Log4j2 @Log4j2
@CommandInfo(name = "current", description = "Gets the currently playing Spotify track") @CommandInfo(name = "current", description = "Gets the currently playing Spotify track")
public class CurrentSubCommand extends BatSubCommand { public class CurrentSubCommand extends BatSubCommand implements EventListener {
private final SpotifyService spotifyService; private final SpotifyService spotifyService;
@Autowired @Autowired
@ -37,61 +42,59 @@ public class CurrentSubCommand extends BatSubCommand {
@Override @Override
public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction interaction) { public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction interaction) {
SpotifyProfile profile = user.getProfile(SpotifyProfile.class); interaction.replyEmbeds(SpotifyFeature.currentSong(spotifyService, user).build()).addComponents(createActions()).queue();
if (!profile.hasLinkedAccount()) { }
interaction.replyEmbeds(SpotifyFeature.linkAccountEmbed()).queue();
@Override @SneakyThrows
public void onButtonInteraction(BatGuild guild, @NonNull BatUser user, @NonNull ButtonInteractionEvent event) {
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; return;
} }
if (!spotifyService.hasTrackPlaying(user)) { switch (event.getComponentId()) {
interaction.replyEmbeds(EmbedUtils.errorEmbed() case "pause" -> {
.setDescription("%s You are not currently playing a track.".formatted(Emojis.CROSS_MARK_EMOJI)) event.replyEmbeds(SpotifyFeature.pauseSong(spotifyService, user)
.build()) .appendDescription("\nThis message will disappear <t:%s:R>".formatted(System.currentTimeMillis() / 1000 + 15)).build())
.queue(); .queue(message -> {
return; message.deleteOriginal().queueAfter(15, TimeUnit.SECONDS);
});
} }
CurrentlyPlaying currentlyPlaying = spotifyService.getCurrentlyPlayingTrack(user); case "resume" -> {
Track track = (Track) currentlyPlaying.getItem(); event.replyEmbeds(SpotifyFeature.resumeSong(spotifyService, user)
AlbumSimplified album = track.getAlbum(); .appendDescription("\nThis message will disappear <t:%s:R>".formatted(System.currentTimeMillis() / 1000 + 15)).build())
String trackUrl = "https://open.spotify.com/track/" + track.getId(); .queue(message -> {
String albumUrl = "https://open.spotify.com/album/" + album.getId(); message.deleteOriginal().queueAfter(15, TimeUnit.SECONDS);
});
StringBuilder artists = new StringBuilder(); }
for (int i = 0; i < track.getArtists().length; i++) { case "skip" -> {
artists.append("**[%s](%s)**".formatted(track.getArtists()[i].getName(), "https://open.spotify.com/artist/" + track.getArtists()[i].getId())); event.replyEmbeds(SpotifyFeature.skipSong(spotifyService, user)
if (i != track.getArtists().length - 1) { .appendDescription("\nThis message will disappear <t:%s:R>".formatted(System.currentTimeMillis() / 1000 + 15)).build())
artists.append(", "); .queue(then -> {
then.deleteOriginal().queueAfter(15, TimeUnit.SECONDS);
event.getInteraction().getMessage().editMessageEmbeds(SpotifyFeature.currentSong(spotifyService, user).build()).queue();
});
} }
} }
String description =
"➜ Song: **[%s](%s)**\n".formatted(track.getName(), trackUrl) +
"➜ 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 | %s".formatted(track.getName(), track.getArtists()[0].getName()), trackUrl)
.setThumbnail(albumCover.getUrl())
.setDescription(description)
.build()).queue();
} }
/** /**
* Gets the formatted time of the currently playing track * Creates the action buttons for the currently playing track
* *
* @param currentlyPlaying the currently playing track * @return The action buttons
* @return the formatted time
*/ */
private String getFormattedTime(@NonNull CurrentlyPlaying currentlyPlaying) { private LayoutComponent[] createActions() {
Track track = (Track) currentlyPlaying.getItem(); return new LayoutComponent[]{
int currentMinutes = currentlyPlaying.getProgress_ms() / 1000 / 60; ActionRow.of(
int currentSeconds = currentlyPlaying.getProgress_ms() / 1000 % 60; Button.primary("pause", "Pause").withEmoji(Emojis.PAUSE_EMOJI),
int totalMinutes = track.getDurationMs() / 1000 / 60; Button.primary("resume", "Resume").withEmoji(Emojis.PLAY_EMOJI),
int totalSeconds = track.getDurationMs() / 1000 % 60; Button.primary("skip", "Skip").withEmoji(Emojis.SKIP_EMOJI)
)
return "`%02d:%02d`/`%02d:%02d`".formatted(currentMinutes, currentSeconds, totalMinutes, totalSeconds); };
} }
} }

@ -6,11 +6,13 @@ import cc.fascinated.bat.command.BatSubCommand;
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.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 cc.fascinated.bat.service.SpotifyService;
import lombok.NonNull; import lombok.NonNull;
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.events.interaction.ModalInteractionEvent; import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent;
@ -38,25 +40,17 @@ public class LinkSubCommand extends BatSubCommand implements EventListener {
this.spotifyService = spotifyService; this.spotifyService = spotifyService;
} }
@Override @Override @SneakyThrows
public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction interaction) { public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction interaction) {
if (!user.getId().equals(Consts.BOT_OWNER)) { if (!user.getId().equals(Consts.BOT_OWNER)) {
interaction.replyEmbeds(EmbedUtils.genericEmbed() throw new BatException("""
.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()).queue();
return;
} }
SpotifyProfile profile = user.getProfile(SpotifyProfile.class); SpotifyProfile profile = user.getProfile(SpotifyProfile.class);
if (profile.hasLinkedAccount()) { if (profile.hasLinkedAccount()) {
interaction.replyEmbeds(EmbedUtils.errorEmbed() throw new BatException("%s You have already linked your Spotify account.".formatted(Emojis.CROSS_MARK_EMOJI));
.setDescription("%s You have already linked your Spotify account!".formatted(Emojis.CROSS_MARK_EMOJI))
.build())
.setEphemeral(true)
.queue();
return;
} }
interaction.replyEmbeds(EmbedUtils.genericEmbed() interaction.replyEmbeds(EmbedUtils.genericEmbed()
@ -88,7 +82,7 @@ public class LinkSubCommand extends BatSubCommand implements EventListener {
event.replyModal(modal).queue(); event.replyModal(modal).queue();
} }
@Override @Override @SneakyThrows
public void onModalInteraction(BatGuild guild, @NonNull BatUser user, @NonNull ModalInteractionEvent event) { public void onModalInteraction(BatGuild guild, @NonNull BatUser user, @NonNull ModalInteractionEvent event) {
if (!event.getModalId().equals("link_modal")) { if (!event.getModalId().equals("link_modal")) {
return; return;
@ -100,11 +94,7 @@ public class LinkSubCommand extends BatSubCommand implements EventListener {
} }
String code = codeMapping.getAsString(); String code = codeMapping.getAsString();
if (!spotifyService.isValidLinkCode(code)) { if (!spotifyService.isValidLinkCode(code)) {
event.replyEmbeds(EmbedUtils.errorEmbed() throw new BatException("%s The link code you provided is invalid.".formatted(Emojis.CROSS_MARK_EMOJI));
.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);

@ -4,6 +4,7 @@ import cc.fascinated.bat.Emojis;
import cc.fascinated.bat.command.BatSubCommand; import cc.fascinated.bat.command.BatSubCommand;
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.common.SpotifyUtils;
import cc.fascinated.bat.features.spotify.SpotifyFeature; 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;
@ -33,31 +34,6 @@ public class PauseSubCommand extends BatSubCommand {
@Override @Override
public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction interaction) { public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction interaction) {
SpotifyProfile profile = user.getProfile(SpotifyProfile.class); interaction.replyEmbeds(SpotifyFeature.pauseSong(spotifyService, user).build()).queue();
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 pause it.".formatted(Emojis.CROSS_MARK_EMOJI))
.build())
.queue();
return;
}
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 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();
} }
} }

@ -4,6 +4,7 @@ import cc.fascinated.bat.Emojis;
import cc.fascinated.bat.command.BatSubCommand; import cc.fascinated.bat.command.BatSubCommand;
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.common.SpotifyUtils;
import cc.fascinated.bat.features.spotify.SpotifyFeature; 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;
@ -33,32 +34,6 @@ public class ResumeSubCommand extends BatSubCommand {
@Override @Override
public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction interaction) { public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction interaction) {
SpotifyProfile profile = user.getProfile(SpotifyProfile.class); interaction.replyEmbeds(SpotifyFeature.resumeSong(spotifyService, user).build()).queue();
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 resume it.".formatted(Emojis.CROSS_MARK_EMOJI))
.build())
.queue();
return;
}
boolean didPause = spotifyService.resumePlayback(user);
CurrentlyPlaying currentlyPlaying = spotifyService.getCurrentlyPlayingTrack(user);
Track track = (Track) currentlyPlaying.getItem();
interaction.replyEmbeds(EmbedUtils.successEmbed()
.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();
} }
} }

@ -4,6 +4,7 @@ import cc.fascinated.bat.Emojis;
import cc.fascinated.bat.command.BatSubCommand; import cc.fascinated.bat.command.BatSubCommand;
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.common.SpotifyUtils;
import cc.fascinated.bat.features.spotify.SpotifyFeature; 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;
@ -17,6 +18,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
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.IPlaylistItem;
import se.michaelthelin.spotify.model_objects.miscellaneous.CurrentlyPlaying; import se.michaelthelin.spotify.model_objects.miscellaneous.CurrentlyPlaying;
import se.michaelthelin.spotify.model_objects.specification.Track; import se.michaelthelin.spotify.model_objects.specification.Track;
@ -36,68 +38,6 @@ public class SkipSubCommand extends BatSubCommand {
@Override @Override
public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction interaction) { public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction interaction) {
SpotifyProfile profile = user.getProfile(SpotifyProfile.class); interaction.replyEmbeds(SpotifyFeature.skipSong(spotifyService, user).build()).queue();
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;
} }
} }

@ -5,11 +5,13 @@ import cc.fascinated.bat.command.BatSubCommand;
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.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.UserService; import cc.fascinated.bat.service.UserService;
import lombok.NonNull; import lombok.NonNull;
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;
@ -29,16 +31,11 @@ public class UnlinkSubCommand extends BatSubCommand implements EventListener {
this.userService = userService; this.userService = userService;
} }
@Override @Override @SneakyThrows
public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction interaction) { public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction interaction) {
SpotifyProfile profile = user.getProfile(SpotifyProfile.class); SpotifyProfile profile = user.getProfile(SpotifyProfile.class);
if (!profile.hasLinkedAccount()) { if (!profile.hasLinkedAccount()) {
interaction.replyEmbeds(EmbedUtils.errorEmbed() throw new BatException("%s You do not have a linked Spotify account.".formatted(Emojis.CROSS_MARK_EMOJI));
.setDescription("%s You do not have a linked Spotify account.".formatted(Emojis.CROSS_MARK_EMOJI))
.build())
.setEphemeral(true)
.queue();
return;
} }
profile.reset(); profile.reset();

@ -213,11 +213,10 @@ public class CommandService extends ListenerAdapter {
executor.execute(guild, user, ranInsideGuild ? event.getChannel().asTextChannel() : event.getChannel().asPrivateChannel(), executor.execute(guild, user, ranInsideGuild ? event.getChannel().asTextChannel() : event.getChannel().asPrivateChannel(),
event.getMember(), event.getInteraction()); event.getMember(), event.getInteraction());
} catch (Exception ex) { } catch (Exception ex) {
log.error("An error occurred while executing command \"{}\"", commandName, ex); event.replyEmbeds(EmbedUtils.errorEmbed()
.setDescription(ex.getLocalizedMessage())
event.replyEmbeds(EmbedUtils.successEmbed()
.setDescription("An error occurred while executing the command:\n```java%s```".formatted(ex.getLocalizedMessage()))
.build()) .build())
.setEphemeral(true)
.queue(); .queue();
} }
} }