From 5f099a97f0400ed26815933139f433cfacf1c2e0 Mon Sep 17 00:00:00 2001 From: Lee Date: Fri, 27 Dec 2024 12:50:13 +0000 Subject: [PATCH] remove some things and bump depends --- pom.xml | 37 +-- .../cc/fascinated/bat/BatApplication.java | 2 - .../cc/fascinated/bat/command/BatCommand.java | 6 +- .../bat/common/InteractionBuilder.java | 13 - .../fascinated/bat/common/SpotifyUtils.java | 71 ----- .../bat/controller/SpotifyController.java | 33 --- .../features/minecraft/MinecraftFeature.java | 48 ---- .../features/minecraft/MinecraftProfile.java | 143 ---------- .../bat/features/minecraft/ServerWatcher.java | 54 ---- .../minecraft/LookupPlayerSubCommand.java | 75 ------ .../minecraft/LookupServerSubCommand.java | 104 ------- .../command/minecraft/MinecraftCommand.java | 29 -- .../command/serverwatcher/AddSubCommand.java | 101 ------- .../command/serverwatcher/ListSubCommand.java | 61 ----- .../serverwatcher/RemoveSubCommand.java | 63 ----- .../serverwatcher/ServerWatcherCommand.java | 31 --- .../NumberOneScoreFeedListener.java | 73 ----- .../scoresaber/ScoreSaberFeature.java | 82 ------ .../scoresaber/UserScoreFeedListener.java | 59 ---- .../command/numberone/ChannelSubCommand.java | 64 ----- .../numberone/NumberOneFeedCommand.java | 23 -- .../command/numberone/ResetSubCommand.java | 31 --- .../command/scoresaber/LinkSubCommand.java | 72 ----- .../command/scoresaber/MeSubCommand.java | 33 --- .../command/scoresaber/ResetSubCommand.java | 33 --- .../command/scoresaber/ScoreSaberCommand.java | 144 ---------- .../scoresaber/ScoresSummarySubCommand.java | 122 --------- .../command/scoresaber/UserSubCommand.java | 53 ---- .../command/userfeed/ChannelSubCommand.java | 64 ----- .../command/userfeed/ResetSubCommand.java | 30 --- .../command/userfeed/UserFeedCommand.java | 24 -- .../command/userfeed/UserSubCommand.java | 86 ------ .../guild/NumberOneScoreFeedProfile.java | 49 ---- .../profile/guild/UserScoreFeedProfile.java | 109 -------- .../profile/user/ScoreSaberProfile.java | 38 --- .../bat/features/spotify/SpotifyFeature.java | 196 -------------- .../spotify/command/CurrentSubCommand.java | 37 --- .../spotify/command/LinkSubCommand.java | 110 -------- .../spotify/command/PauseSubCommand.java | 34 --- .../spotify/command/ResumeSubCommand.java | 34 --- .../spotify/command/SkipSubCommand.java | 37 --- .../spotify/command/SpotifyCommand.java | 28 -- .../spotify/command/UnlinkSubCommand.java | 50 ---- .../spotify/profile/SpotifyProfile.java | 59 ---- .../MongockSuccessEventListener.java | 16 -- .../cc/fascinated/bat/model/BatGuild.java | 12 - .../java/cc/fascinated/bat/model/BatUser.java | 10 - .../bat/service/InteractionService.java | 3 - .../bat/service/ScoreSaberService.java | 254 ------------------ .../bat/service/SpotifyService.java | 239 ---------------- 50 files changed, 5 insertions(+), 3174 deletions(-) delete mode 100644 src/main/java/cc/fascinated/bat/common/SpotifyUtils.java delete mode 100644 src/main/java/cc/fascinated/bat/controller/SpotifyController.java delete mode 100644 src/main/java/cc/fascinated/bat/features/minecraft/MinecraftFeature.java delete mode 100644 src/main/java/cc/fascinated/bat/features/minecraft/MinecraftProfile.java delete mode 100644 src/main/java/cc/fascinated/bat/features/minecraft/ServerWatcher.java delete mode 100644 src/main/java/cc/fascinated/bat/features/minecraft/command/minecraft/LookupPlayerSubCommand.java delete mode 100644 src/main/java/cc/fascinated/bat/features/minecraft/command/minecraft/LookupServerSubCommand.java delete mode 100644 src/main/java/cc/fascinated/bat/features/minecraft/command/minecraft/MinecraftCommand.java delete mode 100644 src/main/java/cc/fascinated/bat/features/minecraft/command/serverwatcher/AddSubCommand.java delete mode 100644 src/main/java/cc/fascinated/bat/features/minecraft/command/serverwatcher/ListSubCommand.java delete mode 100644 src/main/java/cc/fascinated/bat/features/minecraft/command/serverwatcher/RemoveSubCommand.java delete mode 100644 src/main/java/cc/fascinated/bat/features/minecraft/command/serverwatcher/ServerWatcherCommand.java delete mode 100644 src/main/java/cc/fascinated/bat/features/scoresaber/NumberOneScoreFeedListener.java delete mode 100644 src/main/java/cc/fascinated/bat/features/scoresaber/ScoreSaberFeature.java delete mode 100644 src/main/java/cc/fascinated/bat/features/scoresaber/UserScoreFeedListener.java delete mode 100644 src/main/java/cc/fascinated/bat/features/scoresaber/command/numberone/ChannelSubCommand.java delete mode 100644 src/main/java/cc/fascinated/bat/features/scoresaber/command/numberone/NumberOneFeedCommand.java delete mode 100644 src/main/java/cc/fascinated/bat/features/scoresaber/command/numberone/ResetSubCommand.java delete mode 100644 src/main/java/cc/fascinated/bat/features/scoresaber/command/scoresaber/LinkSubCommand.java delete mode 100644 src/main/java/cc/fascinated/bat/features/scoresaber/command/scoresaber/MeSubCommand.java delete mode 100644 src/main/java/cc/fascinated/bat/features/scoresaber/command/scoresaber/ResetSubCommand.java delete mode 100644 src/main/java/cc/fascinated/bat/features/scoresaber/command/scoresaber/ScoreSaberCommand.java delete mode 100644 src/main/java/cc/fascinated/bat/features/scoresaber/command/scoresaber/ScoresSummarySubCommand.java delete mode 100644 src/main/java/cc/fascinated/bat/features/scoresaber/command/scoresaber/UserSubCommand.java delete mode 100644 src/main/java/cc/fascinated/bat/features/scoresaber/command/userfeed/ChannelSubCommand.java delete mode 100644 src/main/java/cc/fascinated/bat/features/scoresaber/command/userfeed/ResetSubCommand.java delete mode 100644 src/main/java/cc/fascinated/bat/features/scoresaber/command/userfeed/UserFeedCommand.java delete mode 100644 src/main/java/cc/fascinated/bat/features/scoresaber/command/userfeed/UserSubCommand.java delete mode 100644 src/main/java/cc/fascinated/bat/features/scoresaber/profile/guild/NumberOneScoreFeedProfile.java delete mode 100644 src/main/java/cc/fascinated/bat/features/scoresaber/profile/guild/UserScoreFeedProfile.java delete mode 100644 src/main/java/cc/fascinated/bat/features/scoresaber/profile/user/ScoreSaberProfile.java delete mode 100644 src/main/java/cc/fascinated/bat/features/spotify/SpotifyFeature.java delete mode 100644 src/main/java/cc/fascinated/bat/features/spotify/command/CurrentSubCommand.java delete mode 100644 src/main/java/cc/fascinated/bat/features/spotify/command/LinkSubCommand.java delete mode 100644 src/main/java/cc/fascinated/bat/features/spotify/command/PauseSubCommand.java delete mode 100644 src/main/java/cc/fascinated/bat/features/spotify/command/ResumeSubCommand.java delete mode 100644 src/main/java/cc/fascinated/bat/features/spotify/command/SkipSubCommand.java delete mode 100644 src/main/java/cc/fascinated/bat/features/spotify/command/SpotifyCommand.java delete mode 100644 src/main/java/cc/fascinated/bat/features/spotify/command/UnlinkSubCommand.java delete mode 100644 src/main/java/cc/fascinated/bat/features/spotify/profile/SpotifyProfile.java delete mode 100644 src/main/java/cc/fascinated/bat/migrations/MongockSuccessEventListener.java delete mode 100644 src/main/java/cc/fascinated/bat/service/ScoreSaberService.java delete mode 100644 src/main/java/cc/fascinated/bat/service/SpotifyService.java diff --git a/pom.xml b/pom.xml index f3dbd22..5c941a7 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 3.3.5 + 3.4.1 @@ -75,11 +75,6 @@ jitpack.io https://jitpack.io - - fascinated-repo-public - Fascinated's Repository - https://repo.fascinated.cc/public - @@ -101,25 +96,6 @@ sentry-spring-boot-starter-jakarta 7.16.0 - - io.mongock - mongock-bom - 5.5.0 - pom - import - - - io.mongock - mongock-springboot-v3 - 5.5.0 - compile - - - io.mongock - mongodb-springdata-v4-driver - 5.5.0 - compile - @@ -167,12 +143,6 @@ net.jodah expiringmap 0.5.11 - compile - - - se.michaelthelin.spotify - spotify-web-api-java - 8.4.1 compile @@ -180,11 +150,6 @@ commons-text 1.12.0 - - xyz.mcutils - mcutils-java-library - 1.2.4 - com.github.Steppschuh Java-Markdown-Generator diff --git a/src/main/java/cc/fascinated/bat/BatApplication.java b/src/main/java/cc/fascinated/bat/BatApplication.java index da73e4c..179bdf4 100644 --- a/src/main/java/cc/fascinated/bat/BatApplication.java +++ b/src/main/java/cc/fascinated/bat/BatApplication.java @@ -5,7 +5,6 @@ import cc.fascinated.bat.event.EventListener; import cc.fascinated.bat.service.EventService; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import io.mongock.runner.springboot.EnableMongock; import lombok.NonNull; import lombok.SneakyThrows; import lombok.extern.log4j.Log4j2; @@ -21,7 +20,6 @@ import java.util.Objects; @EnableScheduling @SpringBootApplication -@EnableMongock @Log4j2(topic = "Bat") public class BatApplication { public static Gson GSON = new GsonBuilder().create(); diff --git a/src/main/java/cc/fascinated/bat/command/BatCommand.java b/src/main/java/cc/fascinated/bat/command/BatCommand.java index 3ecd2b7..0c5c1e8 100644 --- a/src/main/java/cc/fascinated/bat/command/BatCommand.java +++ b/src/main/java/cc/fascinated/bat/command/BatCommand.java @@ -14,10 +14,12 @@ import net.dv8tion.jda.api.interactions.commands.OptionMapping; import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction; import net.dv8tion.jda.api.interactions.commands.build.OptionData; import net.dv8tion.jda.api.interactions.commands.build.SubcommandData; -import net.dv8tion.jda.api.requests.restaction.MessageCreateAction; import net.dv8tion.jda.internal.interactions.CommandDataImpl; -import java.util.*; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; /** * @author Braydon diff --git a/src/main/java/cc/fascinated/bat/common/InteractionBuilder.java b/src/main/java/cc/fascinated/bat/common/InteractionBuilder.java index fdb2435..7964579 100644 --- a/src/main/java/cc/fascinated/bat/common/InteractionBuilder.java +++ b/src/main/java/cc/fascinated/bat/common/InteractionBuilder.java @@ -1,25 +1,14 @@ package cc.fascinated.bat.common; -import cc.fascinated.bat.event.EventListener; -import cc.fascinated.bat.model.BatGuild; -import cc.fascinated.bat.model.BatUser; import cc.fascinated.bat.service.InteractionService; -import lombok.AllArgsConstructor; import lombok.Getter; -import lombok.NonNull; import net.dv8tion.jda.api.entities.emoji.Emoji; import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; import net.dv8tion.jda.api.events.interaction.component.StringSelectInteractionEvent; -import net.dv8tion.jda.api.interactions.components.ActionComponent; import net.dv8tion.jda.api.interactions.components.ActionRow; -import net.dv8tion.jda.api.interactions.components.ComponentInteraction; -import net.dv8tion.jda.api.interactions.components.ItemComponent; import net.dv8tion.jda.api.interactions.components.buttons.Button; -import net.dv8tion.jda.api.interactions.components.selections.SelectMenu; import net.dv8tion.jda.api.interactions.components.selections.SelectOption; import net.dv8tion.jda.api.interactions.components.selections.StringSelectMenu; -import net.dv8tion.jda.api.utils.data.SerializableData; -import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.HashMap; @@ -27,8 +16,6 @@ import java.util.List; import java.util.Map; import java.util.function.Consumer; -import static net.dv8tion.jda.api.interactions.components.Component.Type.STRING_SELECT; - /** * @author Fascinated (fascinated7) */ diff --git a/src/main/java/cc/fascinated/bat/common/SpotifyUtils.java b/src/main/java/cc/fascinated/bat/common/SpotifyUtils.java deleted file mode 100644 index 5793234..0000000 --- a/src/main/java/cc/fascinated/bat/common/SpotifyUtils.java +++ /dev/null @@ -1,71 +0,0 @@ -package cc.fascinated.bat.common; - -import cc.fascinated.bat.model.BatUser; -import cc.fascinated.bat.service.SpotifyService; -import lombok.NonNull; -import lombok.experimental.UtilityClass; -import lombok.extern.log4j.Log4j2; -import se.michaelthelin.spotify.model_objects.miscellaneous.CurrentlyPlaying; -import se.michaelthelin.spotify.model_objects.specification.Track; - -/** - * @author Fascinated (fascinated7) - */ -@UtilityClass -@Log4j2(topic = "Spotify Utils") -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 {} check{}", track.getName(), checks, checks == 1 ? "" : "s"); - return currentlyPlaying; - } - } - } catch (InterruptedException e) { - e.printStackTrace(); - } - return null; - } -} diff --git a/src/main/java/cc/fascinated/bat/controller/SpotifyController.java b/src/main/java/cc/fascinated/bat/controller/SpotifyController.java deleted file mode 100644 index 0901f3d..0000000 --- a/src/main/java/cc/fascinated/bat/controller/SpotifyController.java +++ /dev/null @@ -1,33 +0,0 @@ -package cc.fascinated.bat.controller; - -import cc.fascinated.bat.service.SpotifyService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -/** - * @author Fascinated (fascinated7) - */ -@RestController -@RequestMapping(value = "/spotify") -public class SpotifyController { - private final SpotifyService spotifyService; - - @Autowired - public SpotifyController(SpotifyService spotifyService) { - this.spotifyService = spotifyService; - } - - /** - * A GET request to authorize the user with Spotify. - * - * @return the response entity - */ - @GetMapping(value = "/callback") - public ResponseEntity authorizationCallback(@RequestParam(required = false) String code) { - return ResponseEntity.ok(spotifyService.authorize(code)); - } -} diff --git a/src/main/java/cc/fascinated/bat/features/minecraft/MinecraftFeature.java b/src/main/java/cc/fascinated/bat/features/minecraft/MinecraftFeature.java deleted file mode 100644 index 9960672..0000000 --- a/src/main/java/cc/fascinated/bat/features/minecraft/MinecraftFeature.java +++ /dev/null @@ -1,48 +0,0 @@ -package cc.fascinated.bat.features.minecraft; - -import cc.fascinated.bat.features.Feature; -import cc.fascinated.bat.features.FeatureProfile; -import cc.fascinated.bat.features.minecraft.command.minecraft.MinecraftCommand; -import cc.fascinated.bat.features.minecraft.command.serverwatcher.ServerWatcherCommand; -import cc.fascinated.bat.model.BatGuild; -import cc.fascinated.bat.service.CommandService; -import cc.fascinated.bat.service.DiscordService; -import cc.fascinated.bat.service.GuildService; -import lombok.NonNull; -import net.dv8tion.jda.api.entities.Guild; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; - -/** - * @author Fascinated (fascinated7) - */ -@Component -public class MinecraftFeature extends Feature { - private final GuildService guildService; - - @Autowired - public MinecraftFeature(@NonNull ApplicationContext context, @NonNull CommandService commandService, @NonNull GuildService guildService) { - super("Minecraft", FeatureProfile.FeatureState.DISABLED, true); - this.guildService = guildService; - - super.registerCommand(commandService, context.getBean(MinecraftCommand.class)); - super.registerCommand(commandService, context.getBean(ServerWatcherCommand.class)); - } - - /** - * Check servers every minute - */ - @Scheduled(cron = "0 * * * * *") - public void checkServers() { - for (Guild guild : DiscordService.JDA.getGuilds()) { - BatGuild batGuild = guildService.getGuild(guild.getId()); - if (batGuild.getFeatureProfile().isFeatureDisabled(this)) { // Check if the feature is disabled - continue; - } - - batGuild.getMinecraftProfile().checkServers(); - } - } -} diff --git a/src/main/java/cc/fascinated/bat/features/minecraft/MinecraftProfile.java b/src/main/java/cc/fascinated/bat/features/minecraft/MinecraftProfile.java deleted file mode 100644 index ff94965..0000000 --- a/src/main/java/cc/fascinated/bat/features/minecraft/MinecraftProfile.java +++ /dev/null @@ -1,143 +0,0 @@ -package cc.fascinated.bat.features.minecraft; - -import cc.fascinated.bat.Emojis; -import cc.fascinated.bat.common.*; -import com.google.gson.Gson; -import lombok.Getter; -import net.dv8tion.jda.api.EmbedBuilder; -import org.bson.Document; -import xyz.mcutils.McUtilsAPI; -import xyz.mcutils.models.server.MinecraftServer; -import xyz.mcutils.models.server.ServerPlatform; - -import java.util.ArrayList; -import java.util.List; - -/** - * @author Fascinated (fascinated7) - */ -@Getter -public class MinecraftProfile extends Serializable { - /** - * The servers that are getting their status watched - */ - private final List serverWatchers = new ArrayList<>(); - - /** - * Adds a server watcher - * - * @param serverWatcher - The server watcher to add - */ - public void addServerWatcher(ServerWatcher serverWatcher) { - serverWatchers.add(serverWatcher); - } - - /** - * Removes a server watcher - * - * @param serverWatcher - The server watcher to remove - */ - public void removeServerWatcher(ServerWatcher serverWatcher) { - serverWatchers.remove(serverWatcher); - } - - /** - * Gets a server watcher by hostname - * - * @param hostname the hostname of the server - * @param platform the platform of the server - * @return the server watcher - */ - public ServerWatcher getServerWatcher(String hostname, ServerPlatform platform) { - for (ServerWatcher serverWatcher : serverWatchers) { - if (serverWatcher.getHostname().equalsIgnoreCase(hostname) && serverWatcher.getPlatform() == platform) { - return serverWatcher; - } - } - return null; - } - - public void checkServers() { - for (ServerWatcher server : serverWatchers) { - int platformDefaultPort = server.getPlatform() == ServerPlatform.JAVA ? 25565 : 19132; - String hostname = server.getHostname() + (server.getPort() != platformDefaultPort ? ":" + server.getPort() : ""); - boolean isOnline = true; - - MinecraftServer minecraftServer = null; - switch (server.getPlatform()) { - case JAVA -> { - try { - minecraftServer = McUtilsAPI.getJavaServer(hostname); - } catch (Exception e) { - isOnline = false; - } - } - case BEDROCK -> { - try { - minecraftServer = McUtilsAPI.getBedrockServer(hostname); - } catch (Exception e) { - isOnline = false; - } - } - } - - if (isOnline == server.isLastState()) { - continue; - } - server.setLastState(isOnline); - EmbedBuilder embedBuilder = isOnline ? EmbedUtils.successEmbed() : EmbedUtils.errorEmbed(); - DescriptionBuilder description = new DescriptionBuilder("Server Watcher"); - description.appendLine("%s %s server `%s` is now **%s**".formatted( - isOnline ? Emojis.CHECK_MARK_EMOJI : Emojis.CROSS_MARK_EMOJI, - EnumUtils.getEnumName(server.getPlatform()), - hostname, - isOnline ? "online" : "offline" - ), false); - if (minecraftServer != null) { - description.appendLine("Players: `%s/%s`".formatted( - NumberFormatter.simpleFormat(minecraftServer.getPlayers().getOnline()), - NumberFormatter.simpleFormat(minecraftServer.getPlayers().getMax()) - ), true); - } - server.getChannel().sendMessageEmbeds(embedBuilder - .setDescription(description.build()) - .setThumbnail("https://api.mcutils.xyz/server/icon/%s".formatted(hostname)) - .build()).queue(); - } - } - - @Override - public void load(Document document, Gson gson) { - for (Document watcherDocument : document.getList("serverWatchers", Document.class, new ArrayList<>())) { - serverWatchers.add(new ServerWatcher( - watcherDocument.getString("hostname"), - watcherDocument.getInteger("port"), - ServerPlatform.valueOf(watcherDocument.getString("platform")), - watcherDocument.getString("channelId"), - watcherDocument.getBoolean("lastState", false) - )); - } - } - - @Override - public Document serialize(Gson gson) { - Document document = new Document(); - List watcherDocuments = new ArrayList<>(); - for (ServerWatcher serverWatcher : serverWatchers) { - Document watcherDocument = new Document(); - watcherDocument.append("hostname", serverWatcher.getHostname()); - watcherDocument.append("port", serverWatcher.getPort()); - watcherDocument.append("platform", serverWatcher.getPlatform().name()); - watcherDocument.append("channelId", serverWatcher.getChannelId()); - watcherDocument.append("lastState", serverWatcher.isLastState()); - watcherDocuments.add(watcherDocument); - } - document.append("serverWatchers", watcherDocuments); - return document; - } - - @Override - public void reset() { - serverWatchers.clear(); - } -} diff --git a/src/main/java/cc/fascinated/bat/features/minecraft/ServerWatcher.java b/src/main/java/cc/fascinated/bat/features/minecraft/ServerWatcher.java deleted file mode 100644 index efe84f4..0000000 --- a/src/main/java/cc/fascinated/bat/features/minecraft/ServerWatcher.java +++ /dev/null @@ -1,54 +0,0 @@ -package cc.fascinated.bat.features.minecraft; - -import cc.fascinated.bat.common.ChannelUtils; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.Setter; -import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; -import xyz.mcutils.models.server.ServerPlatform; - -/** - * @author Fascinated (fascinated7) - */ -@AllArgsConstructor -@Getter -@Setter -public class ServerWatcher { - /** - * The hostname of the server - */ - private final String hostname; - - /** - * The port of the server - */ - private final int port; - - /** - * The platform of the server - */ - private final ServerPlatform platform; - - /** - * The channel id to send notifications in - */ - private final String channelId; - - /** - * The last state of the server - *

- * true = online - * false = offline - *

- */ - private boolean lastState; - - /** - * Gets the channel - * - * @return - The channel - */ - public TextChannel getChannel() { - return ChannelUtils.getTextChannel(channelId); - } -} diff --git a/src/main/java/cc/fascinated/bat/features/minecraft/command/minecraft/LookupPlayerSubCommand.java b/src/main/java/cc/fascinated/bat/features/minecraft/command/minecraft/LookupPlayerSubCommand.java deleted file mode 100644 index 2673000..0000000 --- a/src/main/java/cc/fascinated/bat/features/minecraft/command/minecraft/LookupPlayerSubCommand.java +++ /dev/null @@ -1,75 +0,0 @@ -package cc.fascinated.bat.features.minecraft.command.minecraft; - -import cc.fascinated.bat.command.BatCommand; -import cc.fascinated.bat.command.CommandInfo; -import cc.fascinated.bat.common.DescriptionBuilder; -import cc.fascinated.bat.common.EmbedUtils; -import cc.fascinated.bat.model.BatGuild; -import cc.fascinated.bat.model.BatUser; -import lombok.NonNull; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.Message; -import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; -import net.dv8tion.jda.api.interactions.commands.OptionMapping; -import net.dv8tion.jda.api.interactions.commands.OptionType; -import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction; -import net.dv8tion.jda.api.interactions.commands.build.OptionData; -import org.springframework.stereotype.Component; -import xyz.mcutils.McUtilsAPI; -import xyz.mcutils.exception.ErrorResponse; -import xyz.mcutils.models.cache.CachedPlayer; -import xyz.mcutils.models.player.Skin; - -/** - * @author Fascinated (fascinated7) - */ -@Component -@CommandInfo( - name = "lookup-player", - description = "Lookup a Minecraft player" -) -public class LookupPlayerSubCommand extends BatCommand { - public LookupPlayerSubCommand() { - super.addOptions( - new OptionData(OptionType.STRING, "player", "The player to lookup", true) - ); - } - - @Override - public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, Message commandMessage, String[] arguments, SlashCommandInteraction event) { - OptionMapping playerOption = event.getOption("player"); - assert playerOption != null; - String player = playerOption.getAsString(); - - // Check if the player id is valid - if (player.length() > 16 || player.contains(" ")) { - event.reply("The player id `%s` is invalid".formatted(player)).queue(); - return; - } - - // Fetch the player from the API - CachedPlayer cachedPlayer = null; - try { - cachedPlayer = McUtilsAPI.getPlayer(player); - } catch (ErrorResponse ignored) { } // The error response is handled below - if (cachedPlayer == null) { - event.reply("The player `%s` could not be found".formatted(player)).queue(); - return; - } - - String headUrl = cachedPlayer.getSkin() != null ? cachedPlayer.getSkin().getParts().get(Skin.SkinPart.HEAD.getName()) : null; - DescriptionBuilder description = new DescriptionBuilder("Player Lookup") - .appendLine("Username: `%s`".formatted(cachedPlayer.getUsername()), true) - .appendLine("UUID: `%s`".formatted(cachedPlayer.getUniqueId().toString()), true); - if (cachedPlayer.getSkin() != null) { - description.appendLine("Skin: [Click Here](%s)".formatted(headUrl), true); - } - if (cachedPlayer.getCape() != null) { - description.appendLine("Cape: [Click Here](%s)".formatted(cachedPlayer.getCape().getUrl()), true); - } - event.replyEmbeds(EmbedUtils.successEmbed() - .setDescription(description.build()) - .setThumbnail(headUrl) - .build()).queue(); - } -} diff --git a/src/main/java/cc/fascinated/bat/features/minecraft/command/minecraft/LookupServerSubCommand.java b/src/main/java/cc/fascinated/bat/features/minecraft/command/minecraft/LookupServerSubCommand.java deleted file mode 100644 index eb7c8be..0000000 --- a/src/main/java/cc/fascinated/bat/features/minecraft/command/minecraft/LookupServerSubCommand.java +++ /dev/null @@ -1,104 +0,0 @@ -package cc.fascinated.bat.features.minecraft.command.minecraft; - -import cc.fascinated.bat.command.BatCommand; -import cc.fascinated.bat.command.CommandInfo; -import cc.fascinated.bat.common.DescriptionBuilder; -import cc.fascinated.bat.common.EmbedUtils; -import cc.fascinated.bat.model.BatGuild; -import cc.fascinated.bat.model.BatUser; -import lombok.NonNull; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.Message; -import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; -import net.dv8tion.jda.api.interactions.commands.OptionMapping; -import net.dv8tion.jda.api.interactions.commands.OptionType; -import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction; -import net.dv8tion.jda.api.interactions.commands.build.OptionData; -import org.springframework.stereotype.Component; -import xyz.mcutils.McUtilsAPI; -import xyz.mcutils.models.cache.CachedBedrockMinecraftServer; -import xyz.mcutils.models.cache.CachedJavaMinecraftServer; -import xyz.mcutils.models.server.MinecraftServer; - -/** - * @author Fascinated (fascinated7) - */ -@Component -@CommandInfo( - name = "lookup-server", - description = "Lookup a Minecraft server" -) -public class LookupServerSubCommand extends BatCommand { - public LookupServerSubCommand() { - super.addOptions( - new OptionData(OptionType.STRING, "platform", "The platform of the server to lookup", true) - .addChoice("Java", "java") - .addChoice("Bedrock", "bedrock"), - new OptionData(OptionType.STRING, "host", "The host/ip of the server to lookup", true) - ); - } - - @Override - public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, Message commandMessage, String[] arguments, SlashCommandInteraction event) { - OptionMapping platformOption = event.getOption("platform"); - assert platformOption != null; - OptionMapping hostOption = event.getOption("host"); - assert hostOption != null; - - String platform = platformOption.getAsString(); - String host = hostOption.getAsString(); - MinecraftServer server; - try { - if (platform.equalsIgnoreCase("java")) { - server = McUtilsAPI.getJavaServer(host); - } else { - server = McUtilsAPI.getBedrockServer(host); - } - } catch (Exception ex) { - event.replyEmbeds(EmbedUtils.errorEmbed() - .setDescription("The server `%s` is invalid or offline".formatted(host)) - .build()).queue(); - return; - } - - int platformDefaultPort = platform.equalsIgnoreCase("java") ? 25565 : 19132; - String hostname = server.getHostname() + (server.getPort() != platformDefaultPort ? ":" + server.getPort() : ""); - DescriptionBuilder description = new DescriptionBuilder("Server Lookup [(Raw Data)](%s)".formatted( - "https://api.mcutils.xyz/server/%s/%s".formatted(platform, hostname) - )); - description.appendLine("Host: `%s`".formatted(hostname), true); - - MinecraftServer.GeoLocation location = server.getLocation(); - if (server instanceof CachedJavaMinecraftServer javaServer) { - description.appendLine("Version: `%s`".formatted(javaServer.getVersion().getName()), true); - if (javaServer.getForgeData() != null) { - description.appendLine("Forge Mods: `%s`".formatted(javaServer.getForgeData().getMods().length), true); - } - if (javaServer.isMojangBlocked()) { - description.appendLine("Mojang Blocked: `Yes`", true); - } - if (javaServer.isEnforcesSecureChat()) { - description.appendLine("Enforces Secure Chat: `Yes`", true); - } - if (javaServer.isPreventsChatReports()) { - description.appendLine("Prevents Chat Reports: `Yes`", true); - } - } - if (server instanceof CachedBedrockMinecraftServer bedrockServer) { - description.appendLine("Version: `%s`".formatted(bedrockServer.getVersion().getName()), true); - } - if (location != null) { - description.appendLine("Location: [%s](%s)".formatted( - (location.getCity() == null ? "" : location.getCity() + ", ") + location.getCountry(), - "https://www.google.com/maps/search/?api=1&query=%s,%s".formatted(location.getLatitude(), location.getLongitude()) - ), true); - } - - event.replyEmbeds(EmbedUtils.successEmbed() - .setDescription(description.build()) - .setThumbnail("https://api.mcutils.xyz/server/icon/%s".formatted(hostname)) - .setImage(server.getMotd().getPreview()) - .setFooter("Powered by mcutils.xyz") - .build()).queue(); - } -} diff --git a/src/main/java/cc/fascinated/bat/features/minecraft/command/minecraft/MinecraftCommand.java b/src/main/java/cc/fascinated/bat/features/minecraft/command/minecraft/MinecraftCommand.java deleted file mode 100644 index ddc49b2..0000000 --- a/src/main/java/cc/fascinated/bat/features/minecraft/command/minecraft/MinecraftCommand.java +++ /dev/null @@ -1,29 +0,0 @@ -package cc.fascinated.bat.features.minecraft.command.minecraft; - -import cc.fascinated.bat.command.BatCommand; -import cc.fascinated.bat.command.Category; -import cc.fascinated.bat.command.CommandInfo; -import lombok.NonNull; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.stereotype.Component; - -/** - * @author Fascinated (fascinated7) - */ -@Component -@CommandInfo( - name = "minecraft", - description = "Minecraft related commands", - userInstall = true, - category = Category.UTILITY -) -public class MinecraftCommand extends BatCommand { - @Autowired - public MinecraftCommand(@NonNull ApplicationContext context) { - super.addSubCommands( - context.getBean(LookupPlayerSubCommand.class), - context.getBean(LookupServerSubCommand.class) - ); - } -} diff --git a/src/main/java/cc/fascinated/bat/features/minecraft/command/serverwatcher/AddSubCommand.java b/src/main/java/cc/fascinated/bat/features/minecraft/command/serverwatcher/AddSubCommand.java deleted file mode 100644 index 7f952c1..0000000 --- a/src/main/java/cc/fascinated/bat/features/minecraft/command/serverwatcher/AddSubCommand.java +++ /dev/null @@ -1,101 +0,0 @@ -package cc.fascinated.bat.features.minecraft.command.serverwatcher; - -import cc.fascinated.bat.command.BatCommand; -import cc.fascinated.bat.command.CommandInfo; -import cc.fascinated.bat.common.EmbedUtils; -import cc.fascinated.bat.features.minecraft.MinecraftProfile; -import cc.fascinated.bat.features.minecraft.ServerWatcher; -import cc.fascinated.bat.model.BatGuild; -import cc.fascinated.bat.model.BatUser; -import lombok.NonNull; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.Message; -import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; -import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; -import net.dv8tion.jda.api.entities.channel.unions.GuildChannelUnion; -import net.dv8tion.jda.api.interactions.commands.OptionMapping; -import net.dv8tion.jda.api.interactions.commands.OptionType; -import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction; -import net.dv8tion.jda.api.interactions.commands.build.OptionData; -import org.springframework.stereotype.Component; -import xyz.mcutils.McUtilsAPI; -import xyz.mcutils.models.server.MinecraftServer; -import xyz.mcutils.models.server.ServerPlatform; - -/** - * @author Fascinated (fascinated7) - */ -@Component("minecraft-server-watcher.add:sub") -@CommandInfo( - name = "add", - description = "Add a server to the server watcher" -) -public class AddSubCommand extends BatCommand { - public AddSubCommand() { - super.addOptions( - new OptionData(OptionType.CHANNEL, "channel", "The channel to send the server watcher notifications", true), - new OptionData(OptionType.STRING, "platform", "The platform of the server to lookup", true) - .addChoice("Java", "java") - .addChoice("Bedrock", "bedrock"), - new OptionData(OptionType.STRING, "host", "The host/ip of the server to add", true) - ); - } - - @Override - public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, Message commandMessage, String[] arguments, SlashCommandInteraction event) { - OptionMapping platformOption = event.getOption("platform"); - assert platformOption != null; - OptionMapping hostOption = event.getOption("host"); - assert hostOption != null; - OptionMapping channelOption = event.getOption("channel"); - assert channelOption != null; - - String platform = platformOption.getAsString(); - String host = hostOption.getAsString(); - GuildChannelUnion channelUnion = channelOption.getAsChannel(); - TextChannel textChannel = channelUnion.asTextChannel(); - MinecraftServer server; - try { - if (platform.equalsIgnoreCase("java")) { - server = McUtilsAPI.getJavaServer(host); - } else { - server = McUtilsAPI.getBedrockServer(host); - } - } catch (Exception ex) { - event.replyEmbeds(EmbedUtils.errorEmbed() - .setDescription("The server `%s` is invalid or offline".formatted(host)) - .build()).queue(); - return; - } - - MinecraftProfile profile = guild.getMinecraftProfile(); - if (profile.getServerWatchers().size() >= 10) { - event.replyEmbeds(EmbedUtils.errorEmbed() - .setDescription("You can only have a maximum of `10` server watchers") - .build()).queue(); - return; - } - - if (profile.getServerWatcher(host, ServerPlatform.valueOf(platform.toUpperCase())) != null) { - event.replyEmbeds(EmbedUtils.errorEmbed() - .setDescription("The server `%s` is already being watched".formatted(host)) - .build()).queue(); - return; - } - - profile.addServerWatcher(new ServerWatcher( - server.getHostname(), - server.getPort(), - ServerPlatform.valueOf(platform.toUpperCase()), - textChannel.getId(), - false - )); - profile.checkServers(); // Force check the servers - - event.replyEmbeds(EmbedUtils.successEmbed() - .setDescription("Setup the server watcher for `%s` in %s".formatted( - server.getHostname(), - textChannel.getAsMention() - )).build()).queue(); - } -} diff --git a/src/main/java/cc/fascinated/bat/features/minecraft/command/serverwatcher/ListSubCommand.java b/src/main/java/cc/fascinated/bat/features/minecraft/command/serverwatcher/ListSubCommand.java deleted file mode 100644 index 60dd541..0000000 --- a/src/main/java/cc/fascinated/bat/features/minecraft/command/serverwatcher/ListSubCommand.java +++ /dev/null @@ -1,61 +0,0 @@ -package cc.fascinated.bat.features.minecraft.command.serverwatcher; - -import cc.fascinated.bat.command.BatCommand; -import cc.fascinated.bat.command.CommandInfo; -import cc.fascinated.bat.common.DescriptionBuilder; -import cc.fascinated.bat.common.EmbedUtils; -import cc.fascinated.bat.common.EnumUtils; -import cc.fascinated.bat.features.minecraft.MinecraftProfile; -import cc.fascinated.bat.features.minecraft.ServerWatcher; -import cc.fascinated.bat.model.BatGuild; -import cc.fascinated.bat.model.BatUser; -import lombok.NonNull; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.Message; -import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; -import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction; -import org.springframework.stereotype.Component; -import xyz.mcutils.models.server.ServerPlatform; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * @author Fascinated (fascinated7) - */ -@Component("minecraft-server-watcher.list:sub") -@CommandInfo( - name = "list", - description = "Shows a list of all the servers being watched" -) -public class ListSubCommand extends BatCommand { - @Override - public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, Message commandMessage, String[] arguments, SlashCommandInteraction event) { - MinecraftProfile profile = guild.getMinecraftProfile(); - - Map> watchers = new HashMap<>(); - for (ServerWatcher server : profile.getServerWatchers()) { - watchers.computeIfAbsent(server.getPlatform(), k -> new ArrayList<>()).add(server); - } - DescriptionBuilder description = new DescriptionBuilder("Server Watcher"); - description.appendLine("Here is a list of all the servers being watched", false); - description.emptyLine(); - - for (Map.Entry> entry : watchers.entrySet()) { - description.appendLine("**%s**".formatted(EnumUtils.getEnumName(entry.getKey())), false); - for (ServerWatcher server : entry.getValue()) { - description.appendLine("`%s` - %s".formatted( - server.getHostname(), - server.getChannel().getAsMention() - ), true); - } - description.emptyLine(); - } - - event.replyEmbeds(EmbedUtils.successEmbed() - .setDescription(description.build()) - .build()).queue(); - } -} diff --git a/src/main/java/cc/fascinated/bat/features/minecraft/command/serverwatcher/RemoveSubCommand.java b/src/main/java/cc/fascinated/bat/features/minecraft/command/serverwatcher/RemoveSubCommand.java deleted file mode 100644 index 314a2a3..0000000 --- a/src/main/java/cc/fascinated/bat/features/minecraft/command/serverwatcher/RemoveSubCommand.java +++ /dev/null @@ -1,63 +0,0 @@ -package cc.fascinated.bat.features.minecraft.command.serverwatcher; - -import cc.fascinated.bat.command.BatCommand; -import cc.fascinated.bat.command.CommandInfo; -import cc.fascinated.bat.common.EmbedUtils; -import cc.fascinated.bat.features.minecraft.MinecraftProfile; -import cc.fascinated.bat.features.minecraft.ServerWatcher; -import cc.fascinated.bat.model.BatGuild; -import cc.fascinated.bat.model.BatUser; -import lombok.NonNull; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.Message; -import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; -import net.dv8tion.jda.api.interactions.commands.OptionMapping; -import net.dv8tion.jda.api.interactions.commands.OptionType; -import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction; -import net.dv8tion.jda.api.interactions.commands.build.OptionData; -import org.springframework.stereotype.Component; -import xyz.mcutils.models.server.ServerPlatform; - -/** - * @author Fascinated (fascinated7) - */ -@Component("minecraft-server-watcher.remove:sub") -@CommandInfo( - name = "remove", - description = "Remove a server from the server watcher" -) -public class RemoveSubCommand extends BatCommand { - public RemoveSubCommand() { - super.addOptions( - new OptionData(OptionType.STRING, "platform", "The platform of the server to lookup", true) - .addChoice("Java", "java") - .addChoice("Bedrock", "bedrock"), - new OptionData(OptionType.STRING, "host", "The host/ip of the server to remove", true) - ); - } - - @Override - public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, Message commandMessage, String[] arguments, SlashCommandInteraction event) { - OptionMapping platformOption = event.getOption("platform"); - assert platformOption != null; - OptionMapping hostOption = event.getOption("host"); - assert hostOption != null; - - String platform = platformOption.getAsString(); - String host = hostOption.getAsString(); - - MinecraftProfile profile = guild.getMinecraftProfile(); - ServerWatcher serverWatcher = profile.getServerWatcher(host, ServerPlatform.valueOf(platform.toUpperCase())); - if (serverWatcher == null) { - event.replyEmbeds(EmbedUtils.errorEmbed() - .setDescription("The server `%s` is not being watched".formatted(host)) - .build()).queue(); - return; - } - - profile.removeServerWatcher(serverWatcher); - event.replyEmbeds(EmbedUtils.successEmbed() - .setDescription("The server `%s` has been removed from the server watcher".formatted(host)) - .build()).queue(); - } -} diff --git a/src/main/java/cc/fascinated/bat/features/minecraft/command/serverwatcher/ServerWatcherCommand.java b/src/main/java/cc/fascinated/bat/features/minecraft/command/serverwatcher/ServerWatcherCommand.java deleted file mode 100644 index 51c1b75..0000000 --- a/src/main/java/cc/fascinated/bat/features/minecraft/command/serverwatcher/ServerWatcherCommand.java +++ /dev/null @@ -1,31 +0,0 @@ -package cc.fascinated.bat.features.minecraft.command.serverwatcher; - -import cc.fascinated.bat.command.BatCommand; -import cc.fascinated.bat.command.Category; -import cc.fascinated.bat.command.CommandInfo; -import lombok.NonNull; -import net.dv8tion.jda.api.Permission; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.stereotype.Component; - -/** - * @author Fascinated (fascinated7) - */ -@Component -@CommandInfo( - name = "minecraft-server-watcher", - description = "Configure the server watcher for Minecraft servers", - requiredPermissions = Permission.MANAGE_SERVER, - category = Category.UTILITY -) -public class ServerWatcherCommand extends BatCommand { - @Autowired - public ServerWatcherCommand(@NonNull ApplicationContext context) { - super.addSubCommands( - context.getBean(AddSubCommand.class), - context.getBean(RemoveSubCommand.class), - context.getBean(ListSubCommand.class) - ); - } -} diff --git a/src/main/java/cc/fascinated/bat/features/scoresaber/NumberOneScoreFeedListener.java b/src/main/java/cc/fascinated/bat/features/scoresaber/NumberOneScoreFeedListener.java deleted file mode 100644 index 9d93e1f..0000000 --- a/src/main/java/cc/fascinated/bat/features/scoresaber/NumberOneScoreFeedListener.java +++ /dev/null @@ -1,73 +0,0 @@ -package cc.fascinated.bat.features.scoresaber; - -import cc.fascinated.bat.common.NumberFormatter; -import cc.fascinated.bat.event.EventListener; -import cc.fascinated.bat.features.scoresaber.profile.guild.NumberOneScoreFeedProfile; -import cc.fascinated.bat.model.BatGuild; -import cc.fascinated.bat.model.token.beatsaber.scoresaber.ScoreSaberLeaderboardToken; -import cc.fascinated.bat.model.token.beatsaber.scoresaber.ScoreSaberPlayerScoreToken; -import cc.fascinated.bat.model.token.beatsaber.scoresaber.ScoreSaberScoreToken; -import cc.fascinated.bat.service.DiscordService; -import cc.fascinated.bat.service.FeatureService; -import cc.fascinated.bat.service.GuildService; -import lombok.NonNull; -import lombok.extern.log4j.Log4j2; -import net.dv8tion.jda.api.entities.Guild; -import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; -import org.jetbrains.annotations.NotNull; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -/** - * @author Fascinated (fascinated7) - */ -@Component -@Log4j2(topic = "NumberOneScoreFeed Listener") -public class NumberOneScoreFeedListener implements EventListener { - private final GuildService guildService; - private final FeatureService featureService; - - @Autowired - public NumberOneScoreFeedListener(@NonNull GuildService guildService, @NonNull FeatureService featureService) { - this.guildService = guildService; - this.featureService = featureService; - } - - @Override - public void onScoresaberScoreReceived(@NotNull ScoreSaberPlayerScoreToken score, @NotNull ScoreSaberLeaderboardToken leaderboard, - @NotNull ScoreSaberScoreToken.LeaderboardPlayerInfo player) { - if (score.getScore().getRank() != 1) { // Only send if the score is a #1 score - return; - } - if (!leaderboard.isRanked()) { // Only send if the leaderboard is ranked - return; - } - log.info("A new #1 score has been set by {} on {} ({})!", - player.getName(), - leaderboard.getSongName(), - "%s⭐".formatted(NumberFormatter.simpleFormat(leaderboard.getStars())) - ); - - for (Guild guild : DiscordService.JDA.getGuilds()) { - BatGuild batGuild = guildService.getGuild(guild.getId()); - if (batGuild == null) { - continue; - } - ScoreSaberFeature scoreSaberFeature = featureService.getFeature(ScoreSaberFeature.class); - if (!batGuild.getFeatureProfile().isFeatureEnabled(scoreSaberFeature)) { // Check if the feature is enabled - return; - } - - NumberOneScoreFeedProfile profile = batGuild.getProfile(NumberOneScoreFeedProfile.class); - if (profile == null || profile.getChannelId() == null) { - continue; - } - - TextChannel channel = profile.getTextChannel(); - if (channel == null) { - continue; - } - channel.sendMessageEmbeds(ScoreSaberFeature.buildScoreEmbed(score)).queue(); - } - } -} diff --git a/src/main/java/cc/fascinated/bat/features/scoresaber/ScoreSaberFeature.java b/src/main/java/cc/fascinated/bat/features/scoresaber/ScoreSaberFeature.java deleted file mode 100644 index 523fdfb..0000000 --- a/src/main/java/cc/fascinated/bat/features/scoresaber/ScoreSaberFeature.java +++ /dev/null @@ -1,82 +0,0 @@ -package cc.fascinated.bat.features.scoresaber; - -import cc.fascinated.bat.common.DateUtils; -import cc.fascinated.bat.common.EmbedUtils; -import cc.fascinated.bat.common.NumberFormatter; -import cc.fascinated.bat.common.ScoreSaberUtils; -import cc.fascinated.bat.features.Feature; -import cc.fascinated.bat.features.FeatureProfile; -import cc.fascinated.bat.features.scoresaber.command.numberone.NumberOneFeedCommand; -import cc.fascinated.bat.features.scoresaber.command.scoresaber.ScoreSaberCommand; -import cc.fascinated.bat.features.scoresaber.command.userfeed.UserFeedCommand; -import cc.fascinated.bat.model.token.beatsaber.scoresaber.ScoreSaberLeaderboardToken; -import cc.fascinated.bat.model.token.beatsaber.scoresaber.ScoreSaberPlayerScoreToken; -import cc.fascinated.bat.model.token.beatsaber.scoresaber.ScoreSaberScoreToken; -import cc.fascinated.bat.service.CommandService; -import lombok.NonNull; -import net.dv8tion.jda.api.entities.MessageEmbed; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.stereotype.Component; - -/** - * @author Fascinated (fascinated7) - */ -@Component -public class ScoreSaberFeature extends Feature { - @Autowired - public ScoreSaberFeature(@NonNull ApplicationContext context, @NonNull CommandService commandService) { - super("ScoreSaber", FeatureProfile.FeatureState.DISABLED, true); - - registerCommand(commandService, context.getBean(ScoreSaberCommand.class)); - registerCommand(commandService, context.getBean(UserFeedCommand.class)); - registerCommand(commandService, context.getBean(NumberOneFeedCommand.class)); - } - - /** - * Builds an embed for a score. - * - * @param score The score. - * @return The embed. - */ - public static MessageEmbed buildScoreEmbed(ScoreSaberPlayerScoreToken score) { - ScoreSaberScoreToken scoreToken = score.getScore(); - ScoreSaberLeaderboardToken leaderboardToken = score.getLeaderboard(); - ScoreSaberScoreToken.LeaderboardPlayerInfo playerInfo = scoreToken.getLeaderboardPlayerInfo(); - - String thumbnailUrl = String.format("https://cdn.scoresaber.com/covers/%s.png", leaderboardToken.getSongHash()); - String authorUrl = String.format("https://scoresaber.com/u/%s", playerInfo.getId()); - String description = String.format("**%s** (%s%s)\n[[Map Link]](%s) [[SS Profile]](%s)", - leaderboardToken.getSongName(), - ScoreSaberUtils.getFormattedDifficulty(leaderboardToken.getDifficulty().getDifficulty()), - leaderboardToken.isRanked() ? " " + leaderboardToken.getStars() + " ⭐" : "", - String.format("https://scoresaber.com/leaderboard/%s", leaderboardToken.getId()), - authorUrl - ); - - String accuracy = leaderboardToken.getMaxScore() == 0 ? "N/A" : - String.format("%s%%", NumberFormatter.simpleFormat(((double) scoreToken.getBaseScore() / leaderboardToken.getMaxScore()) * 100)); - - String rawPp = scoreToken.getPp() == 0 ? "Unranked" : NumberFormatter.simpleFormat(scoreToken.getPp()); - String rank = String.format("#%s", NumberFormatter.simpleFormat(scoreToken.getRank())); - String misses = String.format("%s", scoreToken.getMissedNotes()); - String badCuts = String.format("%s", scoreToken.getBadCuts()); - String maxCombo = String.format("%s %s", - scoreToken.getMaxCombo(), - scoreToken.getMaxCombo() == leaderboardToken.getMaxScore() ? "(FC)" : "" - ); - - return EmbedUtils.genericEmbed() - .setThumbnail(thumbnailUrl) - .setAuthor(playerInfo.getName() + " just set a new score!", authorUrl, playerInfo.getProfilePicture()) - .setDescription(description) - .addField("Accuracy", accuracy, true) - .addField("Raw PP", rawPp, true) - .addField("Rank", rank, true) - .addField("Misses", misses, true) - .addField("Bad Cuts", badCuts, true) - .addField("Max Combo", maxCombo, true) - .setTimestamp(DateUtils.getDateFromString(scoreToken.getTimeSet()).toInstant()) - .build(); - } -} diff --git a/src/main/java/cc/fascinated/bat/features/scoresaber/UserScoreFeedListener.java b/src/main/java/cc/fascinated/bat/features/scoresaber/UserScoreFeedListener.java deleted file mode 100644 index 485c2b9..0000000 --- a/src/main/java/cc/fascinated/bat/features/scoresaber/UserScoreFeedListener.java +++ /dev/null @@ -1,59 +0,0 @@ -package cc.fascinated.bat.features.scoresaber; - -import cc.fascinated.bat.event.EventListener; -import cc.fascinated.bat.features.scoresaber.profile.guild.UserScoreFeedProfile; -import cc.fascinated.bat.model.BatGuild; -import cc.fascinated.bat.model.token.beatsaber.scoresaber.ScoreSaberLeaderboardToken; -import cc.fascinated.bat.model.token.beatsaber.scoresaber.ScoreSaberPlayerScoreToken; -import cc.fascinated.bat.model.token.beatsaber.scoresaber.ScoreSaberScoreToken; -import cc.fascinated.bat.service.DiscordService; -import cc.fascinated.bat.service.FeatureService; -import cc.fascinated.bat.service.GuildService; -import lombok.NonNull; -import lombok.extern.log4j.Log4j2; -import net.dv8tion.jda.api.entities.Guild; -import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; -import org.jetbrains.annotations.NotNull; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -/** - * @author Fascinated (fascinated7) - */ -@Component -@Log4j2(topic = "UserScoreFeed Listener") -public class UserScoreFeedListener implements EventListener { - private final GuildService guildService; - private final FeatureService featureService; - - @Autowired - public UserScoreFeedListener(@NonNull GuildService guildService, @NonNull FeatureService featureService) { - this.guildService = guildService; - this.featureService = featureService; - } - - @Override - public void onScoresaberScoreReceived(@NotNull ScoreSaberPlayerScoreToken score, @NotNull ScoreSaberLeaderboardToken leaderboard, - @NotNull ScoreSaberScoreToken.LeaderboardPlayerInfo player) { - for (Guild guild : DiscordService.JDA.getGuilds()) { - BatGuild batGuild = guildService.getGuild(guild.getId()); - if (batGuild == null) { - continue; - } - ScoreSaberFeature scoreSaberFeature = featureService.getFeature(ScoreSaberFeature.class); - if (!batGuild.getFeatureProfile().isFeatureEnabled(scoreSaberFeature)) { // Check if the feature is enabled - return; - } - UserScoreFeedProfile profile = batGuild.getProfile(UserScoreFeedProfile.class); - if (profile == null || profile.getChannelId() == null || !profile.getTrackedUsers().contains(player.getId())) { - continue; - } - - TextChannel channel = profile.getTextChannel(); - if (channel == null) { - continue; - } - channel.sendMessageEmbeds(ScoreSaberFeature.buildScoreEmbed(score)).queue(); - } - } -} diff --git a/src/main/java/cc/fascinated/bat/features/scoresaber/command/numberone/ChannelSubCommand.java b/src/main/java/cc/fascinated/bat/features/scoresaber/command/numberone/ChannelSubCommand.java deleted file mode 100644 index abbbdd9..0000000 --- a/src/main/java/cc/fascinated/bat/features/scoresaber/command/numberone/ChannelSubCommand.java +++ /dev/null @@ -1,64 +0,0 @@ -package cc.fascinated.bat.features.scoresaber.command.numberone; - -import cc.fascinated.bat.command.BatCommand; -import cc.fascinated.bat.command.CommandInfo; -import cc.fascinated.bat.common.EmbedUtils; -import cc.fascinated.bat.common.TextChannelUtils; -import cc.fascinated.bat.features.scoresaber.profile.guild.NumberOneScoreFeedProfile; -import cc.fascinated.bat.model.BatGuild; -import cc.fascinated.bat.model.BatUser; -import lombok.NonNull; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.Message; -import net.dv8tion.jda.api.entities.channel.ChannelType; -import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; -import net.dv8tion.jda.api.entities.channel.unions.GuildChannelUnion; -import net.dv8tion.jda.api.interactions.commands.OptionMapping; -import net.dv8tion.jda.api.interactions.commands.OptionType; -import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction; -import net.dv8tion.jda.api.interactions.commands.build.OptionData; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -/** - * @author Fascinated (fascinated7) - */ -@Component("scoresaber-number-one-feed:channel.sub") -@CommandInfo(name = "channel", description = "Sets the feed channel") -public class ChannelSubCommand extends BatCommand { - @Autowired - public ChannelSubCommand() { - super.addOptions(new OptionData(OptionType.CHANNEL, "channel", "The channel scores are sent in", false)); - } - - @Override - public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, Message commandMessage, String[] arguments, SlashCommandInteraction event) { - NumberOneScoreFeedProfile profile = guild.getProfile(NumberOneScoreFeedProfile.class); - OptionMapping option = event.getOption("channel"); - if (option == null) { - if (!TextChannelUtils.isValidChannel(profile.getChannelId())) { - event.replyEmbeds(EmbedUtils.errorEmbed() - .setDescription("There is no channel set for the feed notifications.") - .build()).queue(); - return; - } - event.replyEmbeds(EmbedUtils.genericEmbed() - .setDescription("The current feed channel is %s".formatted(TextChannelUtils.getChannelMention(profile.getChannelId()))) - .build()).queue(); - return; - } - - GuildChannelUnion targetChannel = option.getAsChannel(); - if (targetChannel.getType() != ChannelType.TEXT) { - event.replyEmbeds(EmbedUtils.errorEmbed() - .setDescription("Invalid channel type, please provide a text channel") - .build()).queue(); - return; - } - - profile.setChannelId(targetChannel.getId()); - event.replyEmbeds(EmbedUtils.successEmbed() - .setDescription("Successfully set the feed channel to %s".formatted(targetChannel.asTextChannel().getAsMention())) - .build()).queue(); - } -} diff --git a/src/main/java/cc/fascinated/bat/features/scoresaber/command/numberone/NumberOneFeedCommand.java b/src/main/java/cc/fascinated/bat/features/scoresaber/command/numberone/NumberOneFeedCommand.java deleted file mode 100644 index 38c7f4e..0000000 --- a/src/main/java/cc/fascinated/bat/features/scoresaber/command/numberone/NumberOneFeedCommand.java +++ /dev/null @@ -1,23 +0,0 @@ -package cc.fascinated.bat.features.scoresaber.command.numberone; - -import cc.fascinated.bat.command.BatCommand; -import cc.fascinated.bat.command.Category; -import cc.fascinated.bat.command.CommandInfo; -import lombok.NonNull; -import net.dv8tion.jda.api.Permission; -import org.springframework.context.ApplicationContext; -import org.springframework.stereotype.Component; - -/** - * @author Fascinated (fascinated7) - */ -@Component("scoresaber-number-one-feed") -@CommandInfo(name = "scoresaber-number-one-feed", description = "Modifies the settings for the feed.", requiredPermissions = Permission.MANAGE_SERVER, category = Category.BEAT_SABER) -public class NumberOneFeedCommand extends BatCommand { - public NumberOneFeedCommand(@NonNull ApplicationContext context) { - super.addSubCommands( - context.getBean(ChannelSubCommand.class), - context.getBean(ResetSubCommand.class) - ); - } -} diff --git a/src/main/java/cc/fascinated/bat/features/scoresaber/command/numberone/ResetSubCommand.java b/src/main/java/cc/fascinated/bat/features/scoresaber/command/numberone/ResetSubCommand.java deleted file mode 100644 index 8602b73..0000000 --- a/src/main/java/cc/fascinated/bat/features/scoresaber/command/numberone/ResetSubCommand.java +++ /dev/null @@ -1,31 +0,0 @@ -package cc.fascinated.bat.features.scoresaber.command.numberone; - -import cc.fascinated.bat.command.BatCommand; -import cc.fascinated.bat.command.CommandInfo; -import cc.fascinated.bat.common.EmbedUtils; -import cc.fascinated.bat.features.scoresaber.profile.guild.NumberOneScoreFeedProfile; -import cc.fascinated.bat.model.BatGuild; -import cc.fascinated.bat.model.BatUser; -import lombok.NonNull; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.Message; -import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; -import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction; -import org.springframework.stereotype.Component; - -/** - * @author Fascinated (fascinated7) - */ -@Component("scoresaber-number-one-feed:reset.sub") -@CommandInfo(name = "reset", description = "Resets the settings") -public class ResetSubCommand extends BatCommand { - @Override - public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, Message commandMessage, String[] arguments, SlashCommandInteraction event) { - NumberOneScoreFeedProfile profile = guild.getProfile(NumberOneScoreFeedProfile.class); - profile.reset(); - - event.replyEmbeds(EmbedUtils.successEmbed() - .setDescription("Successfully reset the settings.") - .build()).queue(); - } -} diff --git a/src/main/java/cc/fascinated/bat/features/scoresaber/command/scoresaber/LinkSubCommand.java b/src/main/java/cc/fascinated/bat/features/scoresaber/command/scoresaber/LinkSubCommand.java deleted file mode 100644 index f9bbf39..0000000 --- a/src/main/java/cc/fascinated/bat/features/scoresaber/command/scoresaber/LinkSubCommand.java +++ /dev/null @@ -1,72 +0,0 @@ -package cc.fascinated.bat.features.scoresaber.command.scoresaber; - -import cc.fascinated.bat.command.BatCommand; -import cc.fascinated.bat.command.CommandInfo; -import cc.fascinated.bat.common.EmbedUtils; -import cc.fascinated.bat.model.BatGuild; -import cc.fascinated.bat.model.BatUser; -import cc.fascinated.bat.model.token.beatsaber.scoresaber.ScoreSaberAccountToken; -import cc.fascinated.bat.service.ScoreSaberService; -import lombok.NonNull; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.Message; -import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; -import net.dv8tion.jda.api.interactions.commands.OptionMapping; -import net.dv8tion.jda.api.interactions.commands.OptionType; -import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction; -import net.dv8tion.jda.api.interactions.commands.build.OptionData; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -/** - * @author Fascinated (fascinated7) - */ -@Component("scoresaber:link.sub") -@CommandInfo(name = "link", description = "Links your ScoreSaber profile") -public class LinkSubCommand extends BatCommand { - private final ScoreSaberService scoreSaberService; - - @Autowired - public LinkSubCommand(@NonNull ScoreSaberService scoreSaberService) { - super.addOptions(new OptionData(OptionType.STRING, "link", "Link your ScoreSaber profile", true)); - this.scoreSaberService = scoreSaberService; - } - - @Override - public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, Message commandMessage, String[] arguments, SlashCommandInteraction event) { - OptionMapping linkOption = event.getOption("link"); - assert linkOption != null; - - String link = linkOption.getAsString(); - if (!link.contains("scoresaber.com/u/")) { - event.replyEmbeds(EmbedUtils.errorEmbed() - .setDescription("Invalid ScoreSaber profile link") - .build()) - .setEphemeral(true) - .queue(); - return; - } - - String id = link.split("scoresaber.com/u/")[1]; - if (id.contains("/")) { - id = id.split("/")[0]; - } - - ScoreSaberAccountToken account = scoreSaberService.getAccount(id); - if (account == null) { - event.replyEmbeds(EmbedUtils.errorEmbed() - .setDescription("Invalid ScoreSaber profile link") - .build()) - .setEphemeral(true) - .queue(); - return; - } - - user.getScoreSaberProfile().setAccountId(id); - event.replyEmbeds(EmbedUtils.successEmbed() - .setDescription("Successfully linked your [ScoreSaber](%s) profile".formatted("https://scoresaber.com/u/%s".formatted(id))) - .build()) - .setEphemeral(true) - .queue(); - } -} diff --git a/src/main/java/cc/fascinated/bat/features/scoresaber/command/scoresaber/MeSubCommand.java b/src/main/java/cc/fascinated/bat/features/scoresaber/command/scoresaber/MeSubCommand.java deleted file mode 100644 index 78100e6..0000000 --- a/src/main/java/cc/fascinated/bat/features/scoresaber/command/scoresaber/MeSubCommand.java +++ /dev/null @@ -1,33 +0,0 @@ -package cc.fascinated.bat.features.scoresaber.command.scoresaber; - -import cc.fascinated.bat.command.BatCommand; -import cc.fascinated.bat.command.CommandInfo; -import cc.fascinated.bat.model.BatGuild; -import cc.fascinated.bat.model.BatUser; -import cc.fascinated.bat.service.ScoreSaberService; -import lombok.NonNull; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.Message; -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; - -/** - * @author Fascinated (fascinated7) - */ -@Component("scoresaber:me.sub") -@CommandInfo(name = "me", description = "Gets your ScoreSaber profile") -public class MeSubCommand extends BatCommand { - private final ScoreSaberService scoreSaberService; - - @Autowired - public MeSubCommand(@NonNull ScoreSaberService scoreSaberService) { - this.scoreSaberService = scoreSaberService; - } - - @Override - public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, Message commandMessage, String[] arguments, SlashCommandInteraction event) { - ScoreSaberCommand.sendProfileEmbed(true, user, scoreSaberService, event); - } -} diff --git a/src/main/java/cc/fascinated/bat/features/scoresaber/command/scoresaber/ResetSubCommand.java b/src/main/java/cc/fascinated/bat/features/scoresaber/command/scoresaber/ResetSubCommand.java deleted file mode 100644 index 843b532..0000000 --- a/src/main/java/cc/fascinated/bat/features/scoresaber/command/scoresaber/ResetSubCommand.java +++ /dev/null @@ -1,33 +0,0 @@ -package cc.fascinated.bat.features.scoresaber.command.scoresaber; - -import cc.fascinated.bat.command.BatCommand; -import cc.fascinated.bat.command.CommandInfo; -import cc.fascinated.bat.common.EmbedUtils; -import cc.fascinated.bat.features.scoresaber.profile.user.ScoreSaberProfile; -import cc.fascinated.bat.model.BatGuild; -import cc.fascinated.bat.model.BatUser; -import lombok.NonNull; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.Message; -import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; -import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction; -import org.springframework.stereotype.Component; - -/** - * @author Fascinated (fascinated7) - */ -@Component("scoresaber:reset.sub") -@CommandInfo(name = "reset", description = "Reset your settings") -public class ResetSubCommand extends BatCommand { - @Override - public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, Message commandMessage, String[] arguments, SlashCommandInteraction event) { - ScoreSaberProfile profile = user.getScoreSaberProfile(); - profile.reset(); - - event.replyEmbeds(EmbedUtils.successEmbed() - .setDescription("Successfully reset your settings.") - .build()) - .setEphemeral(true) - .queue(); - } -} diff --git a/src/main/java/cc/fascinated/bat/features/scoresaber/command/scoresaber/ScoreSaberCommand.java b/src/main/java/cc/fascinated/bat/features/scoresaber/command/scoresaber/ScoreSaberCommand.java deleted file mode 100644 index 1aa83d6..0000000 --- a/src/main/java/cc/fascinated/bat/features/scoresaber/command/scoresaber/ScoreSaberCommand.java +++ /dev/null @@ -1,144 +0,0 @@ -package cc.fascinated.bat.features.scoresaber.command.scoresaber; - -import cc.fascinated.bat.command.BatCommand; -import cc.fascinated.bat.command.Category; -import cc.fascinated.bat.command.CommandInfo; -import cc.fascinated.bat.common.*; -import cc.fascinated.bat.features.scoresaber.profile.user.ScoreSaberProfile; -import cc.fascinated.bat.model.BatUser; -import cc.fascinated.bat.model.token.beatsaber.scoresaber.ScoreSaberAccountToken; -import cc.fascinated.bat.service.ScoreSaberService; -import lombok.NonNull; -import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.stereotype.Component; - -/** - * @author Fascinated (fascinated7) - */ -@Component -@CommandInfo(name = "scoresaber", description = "General ScoreSaber commands", userInstall = true, category = Category.BEAT_SABER) -public class ScoreSaberCommand extends BatCommand { - @Autowired - public ScoreSaberCommand(@NonNull ApplicationContext context) { - super.addSubCommands( - context.getBean(LinkSubCommand.class), - context.getBean(UserSubCommand.class), - context.getBean(MeSubCommand.class), - context.getBean(ResetSubCommand.class), - context.getBean(ScoresSummarySubCommand.class) - ); - } - - /** - * Builds the profile embed for the ScoreSaber profile - * - * @param user The user to build the profile embed for - * @param scoreSaberService The ScoreSaber service - * @param event The interaction - */ - public static void sendProfileEmbed(boolean isSelf, BatUser user, ScoreSaberService scoreSaberService, SlashCommandInteraction event) { - ScoreSaberProfile profile = user.getScoreSaberProfile(); - if (profile.getAccountId() == null) { - if (!isSelf) { - event.replyEmbeds(EmbedUtils.errorEmbed() - .setDescription("%s does not have a linked ScoreSaber account".formatted(user.getDiscordUser().getAsMention())) - .build()) - .setEphemeral(true) - .queue(); - } - event.replyEmbeds(EmbedUtils.errorEmbed() - .setDescription("You do not have a linked ScoreSaber account") - .build()) - .setEphemeral(true) - .queue(); - return; - } - - long before = System.currentTimeMillis(); - ScoreSaberAccountToken account = scoreSaberService.getAccount(profile.getAccountId()); - if (account == null) { - if (!isSelf) { - event.replyEmbeds(EmbedUtils.errorEmbed() - .setDescription("%s has an invalid ScoreSaber account linked, please ask them to re-link their account" - .formatted(user.getDiscordUser().getAsMention())) - .build()) - .setEphemeral(true) - .queue(); - } - event.replyEmbeds(EmbedUtils.errorEmbed() - .setDescription("You have an invalid ScoreSaber account linked, please re-link your account") - .build()) - .setEphemeral(true) - .queue(); - return; - } - - if (profile.getAccountId() == null) { - if (!isSelf) { - event.replyEmbeds(EmbedUtils.errorEmbed() - .setDescription("%s does not have a linked ScoreSaber account".formatted(user.getDiscordUser().getAsMention())) - .build()) - .setEphemeral(true) - .queue(); - } - event.replyEmbeds(EmbedUtils.errorEmbed() - .setDescription("You do not have a linked ScoreSaber account") - .build()) - .setEphemeral(true) - .queue(); - return; - } - - event.replyEmbeds(EmbedUtils.genericEmbed() - .setDescription("Loading profile for %s...".formatted(user.getDiscordUser().getAsMention())) - .build()).queue(message -> { - ScoreSaberService.RawPerGlobal rawPerGlobal = scoreSaberService.getRawPerGlobal(profile, (callback) -> { - int currentPage = callback.getCurrentPage(); - int totalPages = callback.getTotalPages(); - // Only update every 5 pages, but show the first page - if (currentPage % 5 != 0 && currentPage != 1) { - return; - } - message.editOriginalEmbeds(EmbedUtils.genericEmbed() - .setDescription(""" - Loading profile for %s... - Page `%s`/`%s` - """.formatted( - user.getDiscordUser().getAsMention(), - currentPage, - totalPages - )) - .build()).queue(); - }); - - String name = account.getName(); - String country = account.getCountry(); - String rank = NumberFormatter.simpleFormat(account.getRank()); - String countryRank = NumberFormatter.simpleFormat(account.getCountryRank()); - String pp = NumberFormatter.simpleFormat(account.getPp()); - String ppPerRawGlobal = NumberFormatter.simpleFormat(rawPerGlobal.getRawPerGlobal()); - long joinedTimeInSeconds = DateUtils.getDateFromString(account.getFirstSeen()).toInstant().getEpochSecond(); - String timeTaken = TimeUtils.format(System.currentTimeMillis() - before, TimeUtils.BatTimeFormat.FIT, true); - - message.editOriginalEmbeds(EmbedUtils.successEmbed() - .setDescription(new DescriptionBuilder("%s's ScoreSaber Account".formatted(user.getDiscordUser().getAsMention())) - .appendLine("Name: `%s`".formatted(name), true) - .appendLine("Country: `%s`".formatted(country), true) - .appendLine("Rank: `#%s`".formatted(rank), true) - .appendLine("Country Rank: `#%s`".formatted(countryRank), true) - .appendLine("PP: `%spp`".formatted(pp), true) - .appendLine("Raw PP Per Global: `%spp`".formatted(ppPerRawGlobal), true) - .appendLine("Joined: ".formatted(joinedTimeInSeconds), true) - .build()) - .setThumbnail(account.getProfilePicture()) - .setFooter("Took %s (%s/%s cached pages)".formatted( - timeTaken, - rawPerGlobal.getCachedPages(), - rawPerGlobal.getTotalPages() - ), null) - .build()).queue(); - }); - } -} diff --git a/src/main/java/cc/fascinated/bat/features/scoresaber/command/scoresaber/ScoresSummarySubCommand.java b/src/main/java/cc/fascinated/bat/features/scoresaber/command/scoresaber/ScoresSummarySubCommand.java deleted file mode 100644 index f2fa6c6..0000000 --- a/src/main/java/cc/fascinated/bat/features/scoresaber/command/scoresaber/ScoresSummarySubCommand.java +++ /dev/null @@ -1,122 +0,0 @@ -package cc.fascinated.bat.features.scoresaber.command.scoresaber; - -import cc.fascinated.bat.command.BatCommand; -import cc.fascinated.bat.command.CommandInfo; -import cc.fascinated.bat.common.EmbedUtils; -import cc.fascinated.bat.common.NumberFormatter; -import cc.fascinated.bat.features.scoresaber.profile.user.ScoreSaberProfile; -import cc.fascinated.bat.model.BatGuild; -import cc.fascinated.bat.model.BatUser; -import cc.fascinated.bat.model.token.beatsaber.scoresaber.ScoreSaberLeaderboardToken; -import cc.fascinated.bat.model.token.beatsaber.scoresaber.ScoreSaberPlayerScoreToken; -import cc.fascinated.bat.model.token.beatsaber.scoresaber.ScoreSaberScoreToken; -import cc.fascinated.bat.service.ScoreSaberService; -import lombok.NonNull; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.Message; -import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; -import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction; -import net.dv8tion.jda.api.utils.FileUpload; -import net.steppschuh.markdowngenerator.table.Table; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * @author Fascinated (fascinated7) - */ -@Component("scoresaber:scores-summary.sub") -@CommandInfo(name = "scores-summary", description = "Generate a summary of your scores") -public class ScoresSummarySubCommand extends BatCommand { - private final ScoreSaberService scoreSaberService; - - @Autowired - public ScoresSummarySubCommand(@NonNull ScoreSaberService scoreSaberService) { - this.scoreSaberService = scoreSaberService; - } - - @Override - public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, Message commandMessage, String[] arguments, SlashCommandInteraction event) { - ScoreSaberProfile profile = user.getScoreSaberProfile(); - - event.replyEmbeds(EmbedUtils.genericEmbed() - .setDescription("Loading profile for %s...".formatted(user.getDiscordUser().getAsMention())) - .build()) - .queue(message -> { - List pages = scoreSaberService.getScores(profile, (currentPage -> { - // Only update every 5 pages, but show the first page - if (currentPage.getCurrentPage() % 5 != 0 && currentPage.getCurrentPage() != 1) { - return; - } - - message.editOriginalEmbeds(EmbedUtils.genericEmbed() - .setDescription("Loading profile for %s... (Page %s/%s)".formatted( - user.getDiscordUser().getAsMention(), - currentPage.getCurrentPage(), - currentPage.getTotalPages() - )) - .build()).queue(); - })); - - Table.Builder tableBuilder = new Table.Builder() - .withAlignments(Table.ALIGN_LEFT, Table.ALIGN_LEFT, Table.ALIGN_LEFT, Table.ALIGN_LEFT) - .addRow("Song", "PP", "Accuracy", "Rank"); - - int totalRankedScores = 0; - List scores = new ArrayList<>(); - for (ScoreSaberService.CachedPage page : pages) { - Collections.addAll(scores, page.getPage().getPlayerScores()); - } - - // Sort by highest PP first - scores.sort((score1, score2) -> { - if (score1.getScore().getPp() > score2.getScore().getPp()) { - return -1; - } else if (score1.getScore().getPp() < score2.getScore().getPp()) { - return 1; - } - return 0; - }); - - // Add the scores to the table - for (ScoreSaberPlayerScoreToken scoreToken : scores) { - ScoreSaberScoreToken score = scoreToken.getScore(); - ScoreSaberLeaderboardToken leaderboard = scoreToken.getLeaderboard(); - double acc = leaderboard.getMaxScore() == 0 ? 0 : ((double) score.getBaseScore() / leaderboard.getMaxScore()) * 100; - - tableBuilder.addRow( - "[%s](https://scoresaber.com/leaderboard/%s)".formatted( - leaderboard.getSongName(), - leaderboard.getId() - ), - NumberFormatter.simpleFormat(score.getPp()) + "pp", - NumberFormatter.simpleFormat(acc) + "%", - "#" + NumberFormatter.simpleFormat(score.getRank()) - ); - - if (score.getPp() != 0) { - totalRankedScores++; - } - } - - message.editOriginalEmbeds(EmbedUtils.genericEmbed() - .setDescription(""" - **Scores Summary** - Here is a summary of score for %s - \s - Total Scores: `%s` - Total Ranked Scores: `%s` - """.formatted( - user.getDiscordUser().getAsMention(), - NumberFormatter.simpleFormat(scores.size()), - NumberFormatter.simpleFormat(totalRankedScores) - )) - .build()) - .setFiles(FileUpload.fromData(tableBuilder.build().toString().getBytes(), "scores-summary.md")) - .queue(); - }); - } -} diff --git a/src/main/java/cc/fascinated/bat/features/scoresaber/command/scoresaber/UserSubCommand.java b/src/main/java/cc/fascinated/bat/features/scoresaber/command/scoresaber/UserSubCommand.java deleted file mode 100644 index 1cc2bd5..0000000 --- a/src/main/java/cc/fascinated/bat/features/scoresaber/command/scoresaber/UserSubCommand.java +++ /dev/null @@ -1,53 +0,0 @@ -package cc.fascinated.bat.features.scoresaber.command.scoresaber; - -import cc.fascinated.bat.command.BatCommand; -import cc.fascinated.bat.command.CommandInfo; -import cc.fascinated.bat.common.EmbedUtils; -import cc.fascinated.bat.model.BatGuild; -import cc.fascinated.bat.model.BatUser; -import cc.fascinated.bat.service.ScoreSaberService; -import cc.fascinated.bat.service.UserService; -import lombok.NonNull; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.Message; -import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; -import net.dv8tion.jda.api.interactions.commands.OptionMapping; -import net.dv8tion.jda.api.interactions.commands.OptionType; -import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction; -import net.dv8tion.jda.api.interactions.commands.build.OptionData; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -/** - * @author Fascinated (fascinated7) - */ -@Component("scoresaber:user.sub") -@CommandInfo(name = "user", description = "Gets a ScoreSaber profile") -public class UserSubCommand extends BatCommand { - private final ScoreSaberService scoreSaberService; - private final UserService userService; - - @Autowired - public UserSubCommand(@NonNull ScoreSaberService scoreSaberService, @NonNull UserService userService) { - super.addOptions(new OptionData(OptionType.USER, "user", "The user to view the ScoreSaber profile of", true)); - this.scoreSaberService = scoreSaberService; - this.userService = userService; - } - - @Override - public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, Message commandMessage, String[] arguments, SlashCommandInteraction event) { - OptionMapping userOption = event.getOption("user"); - assert userOption != null; - - BatUser target = userService.getUser(userOption.getAsUser().getId()); - if (target == null) { - event.replyEmbeds(EmbedUtils.errorEmbed() - .setDescription("Unknown user") - .build()) - .setEphemeral(true) - .queue(); - return; - } - ScoreSaberCommand.sendProfileEmbed(false, target, scoreSaberService, event); - } -} diff --git a/src/main/java/cc/fascinated/bat/features/scoresaber/command/userfeed/ChannelSubCommand.java b/src/main/java/cc/fascinated/bat/features/scoresaber/command/userfeed/ChannelSubCommand.java deleted file mode 100644 index 7be8ce3..0000000 --- a/src/main/java/cc/fascinated/bat/features/scoresaber/command/userfeed/ChannelSubCommand.java +++ /dev/null @@ -1,64 +0,0 @@ -package cc.fascinated.bat.features.scoresaber.command.userfeed; - -import cc.fascinated.bat.command.BatCommand; -import cc.fascinated.bat.command.CommandInfo; -import cc.fascinated.bat.common.EmbedUtils; -import cc.fascinated.bat.common.TextChannelUtils; -import cc.fascinated.bat.features.scoresaber.profile.guild.UserScoreFeedProfile; -import cc.fascinated.bat.model.BatGuild; -import cc.fascinated.bat.model.BatUser; -import lombok.NonNull; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.Message; -import net.dv8tion.jda.api.entities.channel.ChannelType; -import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; -import net.dv8tion.jda.api.entities.channel.unions.GuildChannelUnion; -import net.dv8tion.jda.api.interactions.commands.OptionMapping; -import net.dv8tion.jda.api.interactions.commands.OptionType; -import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction; -import net.dv8tion.jda.api.interactions.commands.build.OptionData; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -/** - * @author Fascinated (fascinated7) - */ -@Component("scoresaber-user-feed:channel.sub") -@CommandInfo(name = "channel", description = "Sets the feed channel") -public class ChannelSubCommand extends BatCommand { - @Autowired - public ChannelSubCommand() { - super.addOptions(new OptionData(OptionType.CHANNEL, "channel", "The channel scores are sent in", false)); - } - - @Override - public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, Message commandMessage, String[] arguments, SlashCommandInteraction event) { - UserScoreFeedProfile profile = guild.getProfile(UserScoreFeedProfile.class); - OptionMapping option = event.getOption("channel"); - if (option == null) { - if (!TextChannelUtils.isValidChannel(profile.getChannelId())) { - event.replyEmbeds(EmbedUtils.errorEmbed() - .setDescription("There is no channel set for the feed notifications.") - .build()).queue(); - return; - } - event.replyEmbeds(EmbedUtils.genericEmbed() - .setDescription("The current feed channel is %s".formatted(TextChannelUtils.getChannelMention(profile.getChannelId()))) - .build()).queue(); - return; - } - - GuildChannelUnion targetChannel = option.getAsChannel(); - if (targetChannel.getType() != ChannelType.TEXT) { - event.replyEmbeds(EmbedUtils.errorEmbed() - .setDescription("Invalid channel type, please provide a text channel") - .build()).queue(); - return; - } - - profile.setChannelId(targetChannel.getId()); - event.replyEmbeds(EmbedUtils.successEmbed() - .setDescription("Successfully set the feed channel to %s".formatted(targetChannel.asTextChannel().getAsMention())) - .build()).queue(); - } -} diff --git a/src/main/java/cc/fascinated/bat/features/scoresaber/command/userfeed/ResetSubCommand.java b/src/main/java/cc/fascinated/bat/features/scoresaber/command/userfeed/ResetSubCommand.java deleted file mode 100644 index 7bff16f..0000000 --- a/src/main/java/cc/fascinated/bat/features/scoresaber/command/userfeed/ResetSubCommand.java +++ /dev/null @@ -1,30 +0,0 @@ -package cc.fascinated.bat.features.scoresaber.command.userfeed; - -import cc.fascinated.bat.command.BatCommand; -import cc.fascinated.bat.command.CommandInfo; -import cc.fascinated.bat.common.EmbedUtils; -import cc.fascinated.bat.features.scoresaber.profile.guild.UserScoreFeedProfile; -import cc.fascinated.bat.model.BatGuild; -import cc.fascinated.bat.model.BatUser; -import lombok.NonNull; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.Message; -import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; -import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction; -import org.springframework.stereotype.Component; - -/** - * @author Fascinated (fascinated7) - */ -@Component("scoresaber-user-feed:reset.sub") -@CommandInfo(name = "reset", description = "Resets the settings") -public class ResetSubCommand extends BatCommand { - @Override - public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, Message commandMessage, String[] arguments, SlashCommandInteraction event) { - UserScoreFeedProfile profile = guild.getProfile(UserScoreFeedProfile.class); - profile.reset(); - event.replyEmbeds(EmbedUtils.successEmbed() - .setDescription("Successfully reset the settings.") - .build()).queue(); - } -} diff --git a/src/main/java/cc/fascinated/bat/features/scoresaber/command/userfeed/UserFeedCommand.java b/src/main/java/cc/fascinated/bat/features/scoresaber/command/userfeed/UserFeedCommand.java deleted file mode 100644 index 75d945f..0000000 --- a/src/main/java/cc/fascinated/bat/features/scoresaber/command/userfeed/UserFeedCommand.java +++ /dev/null @@ -1,24 +0,0 @@ -package cc.fascinated.bat.features.scoresaber.command.userfeed; - -import cc.fascinated.bat.command.BatCommand; -import cc.fascinated.bat.command.Category; -import cc.fascinated.bat.command.CommandInfo; -import lombok.NonNull; -import net.dv8tion.jda.api.Permission; -import org.springframework.context.ApplicationContext; -import org.springframework.stereotype.Component; - -/** - * @author Fascinated (fascinated7) - */ -@Component("scoresaber-user-feed.command") -@CommandInfo(name = "scoresaber-user-feed", description = "Modifies the settings for the feed.", requiredPermissions = Permission.MANAGE_CHANNEL, category = Category.BEAT_SABER) -public class UserFeedCommand extends BatCommand { - public UserFeedCommand(@NonNull ApplicationContext context) { - super.addSubCommands( - context.getBean(UserSubCommand.class), - context.getBean(ChannelSubCommand.class), - context.getBean(ResetSubCommand.class) - ); - } -} diff --git a/src/main/java/cc/fascinated/bat/features/scoresaber/command/userfeed/UserSubCommand.java b/src/main/java/cc/fascinated/bat/features/scoresaber/command/userfeed/UserSubCommand.java deleted file mode 100644 index 966e946..0000000 --- a/src/main/java/cc/fascinated/bat/features/scoresaber/command/userfeed/UserSubCommand.java +++ /dev/null @@ -1,86 +0,0 @@ -package cc.fascinated.bat.features.scoresaber.command.userfeed; - -import cc.fascinated.bat.command.BatCommand; -import cc.fascinated.bat.command.CommandInfo; -import cc.fascinated.bat.common.EmbedUtils; -import cc.fascinated.bat.features.scoresaber.profile.guild.UserScoreFeedProfile; -import cc.fascinated.bat.features.scoresaber.profile.user.ScoreSaberProfile; -import cc.fascinated.bat.model.BatGuild; -import cc.fascinated.bat.model.BatUser; -import cc.fascinated.bat.service.UserService; -import lombok.NonNull; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.Message; -import net.dv8tion.jda.api.entities.User; -import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; -import net.dv8tion.jda.api.interactions.commands.OptionMapping; -import net.dv8tion.jda.api.interactions.commands.OptionType; -import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction; -import net.dv8tion.jda.api.interactions.commands.build.OptionData; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -/** - * @author Fascinated (fascinated7) - */ -@Component("scoresaber-user-feed:user.sub") -@CommandInfo(name = "user", description = "Adds or removes a user from the feed") -public class UserSubCommand extends BatCommand { - private final UserService userService; - - @Autowired - public UserSubCommand(UserService userService) { - super.addOptions(new OptionData(OptionType.USER, "user", "Add or remove a user from the score feed", false)); - this.userService = userService; - } - - @Override - public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, Message commandMessage, String[] arguments, SlashCommandInteraction event) { - UserScoreFeedProfile profile = guild.getProfile(UserScoreFeedProfile.class); - OptionMapping option = event.getOption("user"); - if (option == null) { - if (profile.getTrackedUsers().isEmpty()) { - event.replyEmbeds(EmbedUtils.errorEmbed() - .setDescription("There are no users being tracked in the feed") - .build()).queue(); - return; - } - StringBuilder stringBuilder = new StringBuilder(); - for (String accountId : profile.getTrackedUsers()) { - stringBuilder.append("[%s](%s)".formatted( - accountId, - "https://scoresaber.com/u/%s".formatted(accountId) - )); - } - event.replyEmbeds(EmbedUtils.genericEmbed() - .setDescription("The current users being tracked in the feed are:\n%s".formatted(stringBuilder.toString())) - .build()).queue(); - return; - } - - User target = option.getAsUser(); - BatUser targetUser = userService.getUser(target.getId()); - ScoreSaberProfile targetProfile = targetUser.getScoreSaberProfile(); - if (targetProfile.getAccountId() == null) { - event.replyEmbeds(EmbedUtils.errorEmbed() - .setDescription("The user you are trying to track does not have a linked ScoreSaber profile") - .build()).queue(); - return; - } - - boolean added = false; - if (profile.isUserTracked(targetProfile.getAccountId())) { - profile.removeTrackedUser(targetProfile.getAccountId()); - } else { - profile.addTrackedUser(targetProfile.getAccountId()); - added = true; - } - - event.replyEmbeds(EmbedUtils.successEmbed() - .setDescription("Successfully %s %s from the feed".formatted( - added ? "added" : "removed", - target.getAsMention() - )) - .build()).queue(); - } -} diff --git a/src/main/java/cc/fascinated/bat/features/scoresaber/profile/guild/NumberOneScoreFeedProfile.java b/src/main/java/cc/fascinated/bat/features/scoresaber/profile/guild/NumberOneScoreFeedProfile.java deleted file mode 100644 index 0d70ea0..0000000 --- a/src/main/java/cc/fascinated/bat/features/scoresaber/profile/guild/NumberOneScoreFeedProfile.java +++ /dev/null @@ -1,49 +0,0 @@ -package cc.fascinated.bat.features.scoresaber.profile.guild; - -import cc.fascinated.bat.common.Serializable; -import cc.fascinated.bat.service.DiscordService; -import com.google.gson.Gson; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; -import org.bson.Document; - -/** - * @author Fascinated (fascinated7) - */ -@Getter -@Setter -@NoArgsConstructor -public class NumberOneScoreFeedProfile extends Serializable { - /** - * The channel ID of the score feed - */ - private String channelId; - - /** - * Gets the channel as a TextChannel - * - * @return the channel as a TextChannel - */ - public TextChannel getTextChannel() { - return DiscordService.JDA.getTextChannelById(channelId); - } - - @Override - public void reset() { - this.channelId = null; - } - - @Override - public void load(Document document, Gson gson) { - this.channelId = document.getString("channelId"); - } - - @Override - public Document serialize(Gson gson) { - Document document = new Document(); - document.put("channelId", this.channelId); - return document; - } -} diff --git a/src/main/java/cc/fascinated/bat/features/scoresaber/profile/guild/UserScoreFeedProfile.java b/src/main/java/cc/fascinated/bat/features/scoresaber/profile/guild/UserScoreFeedProfile.java deleted file mode 100644 index d8b5b32..0000000 --- a/src/main/java/cc/fascinated/bat/features/scoresaber/profile/guild/UserScoreFeedProfile.java +++ /dev/null @@ -1,109 +0,0 @@ -package cc.fascinated.bat.features.scoresaber.profile.guild; - -import cc.fascinated.bat.common.ChannelUtils; -import cc.fascinated.bat.common.Serializable; -import com.google.gson.Gson; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; -import org.bson.Document; - -import java.util.ArrayList; -import java.util.List; - -/** - * @author Fascinated (fascinated7) - */ -@Getter -@Setter -@NoArgsConstructor -public class UserScoreFeedProfile extends Serializable { - /** - * The channel ID of the score feed - */ - private String channelId; - - /** - * The users that are being tracked - */ - private List trackedUsers; - - /** - * Gets the tracked users - * - * @return the tracked users - */ - public List getTrackedUsers() { - if (this.trackedUsers == null) { - this.trackedUsers = new ArrayList<>(); - } - return trackedUsers; - } - - /** - * Checks if a user is being tracked - * - * @param userId the user ID to check - * @return if the user is being tracked - */ - public boolean isUserTracked(String userId) { - if (this.trackedUsers == null) { - this.trackedUsers = new ArrayList<>(); - } - return trackedUsers.contains(userId); - } - - /** - * Adds a user to be tracked - * - * @param userId the user ID to add - */ - public void addTrackedUser(String userId) { - if (this.trackedUsers == null) { - this.trackedUsers = new ArrayList<>(); - } - trackedUsers.add(userId); - } - - /** - * Removes a user from being tracked - * - * @param userId the user ID to remove - */ - public void removeTrackedUser(String userId) { - if (this.trackedUsers == null) { - this.trackedUsers = new ArrayList<>(); - } - trackedUsers.remove(userId); - } - - /** - * Gets the channel as a TextChannel - * - * @return the channel as a TextChannel - */ - public TextChannel getTextChannel() { - return ChannelUtils.getTextChannel(channelId); - } - - @Override - public void reset() { - this.channelId = null; - this.trackedUsers = null; - } - - @Override - public void load(Document document, Gson gson) { - this.channelId = document.getString("channelId"); - this.trackedUsers = document.getList("trackedUsers", String.class, new ArrayList<>()); - } - - @Override - public Document serialize(Gson gson) { - Document document = new Document(); - document.put("channelId", this.channelId); - document.put("trackedUsers", this.trackedUsers); - return document; - } -} diff --git a/src/main/java/cc/fascinated/bat/features/scoresaber/profile/user/ScoreSaberProfile.java b/src/main/java/cc/fascinated/bat/features/scoresaber/profile/user/ScoreSaberProfile.java deleted file mode 100644 index 5a5cfbd..0000000 --- a/src/main/java/cc/fascinated/bat/features/scoresaber/profile/user/ScoreSaberProfile.java +++ /dev/null @@ -1,38 +0,0 @@ -package cc.fascinated.bat.features.scoresaber.profile.user; - -import cc.fascinated.bat.common.Serializable; -import com.google.gson.Gson; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import org.bson.Document; - -/** - * @author Fascinated (fascinated7) - */ -@Setter -@Getter -@NoArgsConstructor -public class ScoreSaberProfile extends Serializable { - /** - * The Account ID of the ScoreSaber profile - */ - private String accountId; - - @Override - public void reset() { - this.accountId = null; - } - - @Override - public void load(Document document, Gson gson) { - this.accountId = document.getString("accountId"); - } - - @Override - public Document serialize(Gson gson) { - Document document = new Document(); - document.put("accountId", this.accountId); - return document; - } -} diff --git a/src/main/java/cc/fascinated/bat/features/spotify/SpotifyFeature.java b/src/main/java/cc/fascinated/bat/features/spotify/SpotifyFeature.java deleted file mode 100644 index c3c747e..0000000 --- a/src/main/java/cc/fascinated/bat/features/spotify/SpotifyFeature.java +++ /dev/null @@ -1,196 +0,0 @@ -package cc.fascinated.bat.features.spotify; - -import cc.fascinated.bat.Emojis; -import cc.fascinated.bat.common.EmbedUtils; -import cc.fascinated.bat.common.SpotifyUtils; -import cc.fascinated.bat.features.Feature; -import cc.fascinated.bat.features.FeatureProfile; -import cc.fascinated.bat.features.spotify.command.SpotifyCommand; -import cc.fascinated.bat.features.spotify.profile.SpotifyProfile; -import cc.fascinated.bat.model.BatUser; -import cc.fascinated.bat.service.CommandService; -import cc.fascinated.bat.service.SpotifyService; -import lombok.NonNull; -import lombok.SneakyThrows; -import net.dv8tion.jda.api.EmbedBuilder; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -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.specification.AlbumSimplified; -import se.michaelthelin.spotify.model_objects.specification.Image; -import se.michaelthelin.spotify.model_objects.specification.Track; - -/** - * @author Fascinated (fascinated7) - */ -@Component -public class SpotifyFeature extends Feature { - @Autowired - public SpotifyFeature(@NonNull ApplicationContext context, @NonNull CommandService commandService) { - super("Spotify", FeatureProfile.FeatureState.DISABLED, true); - - 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. - * - * @param spotifyService The Spotify service. - * @param user The user. - */ - @SneakyThrows - public static EmbedBuilder currentSong(@NonNull SpotifyService spotifyService, @NonNull BatUser user) { - SpotifyProfile profile = user.getProfile(SpotifyProfile.class); - if (!profile.hasLinkedAccount() || !spotifyService.hasTrackPlaying(user)) { - return checkSpotify(spotifyService, user); - } - - 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() - .setAuthor("Listening to %s | %s".formatted(track.getName(), track.getArtists()[0].getName()), trackUrl) - .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() || !spotifyService.hasTrackPlaying(user)) { - return checkSpotify(spotifyService, user); - } - CurrentlyPlaying currentlyPlaying = spotifyService.getCurrentlyPlayingTrack(user); - Track track = (Track) currentlyPlaying.getItem(); - String trackName = track.getName(); - - spotifyService.skipTrack(user); // Skip the track - // Get the new track - 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() || !spotifyService.hasTrackPlaying(user)) { - return checkSpotify(spotifyService, 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(); - return EmbedUtils.successEmbed() - .setDescription("%s Paused the track **[%s | %s](%s)**".formatted( - Emojis.CHECK_MARK_EMOJI, - track.getName(), - track.getArtists()[0].getName(), - SpotifyUtils.getTrackUrl(currentlyPlaying) - )); - } - - /** - * 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() || !spotifyService.hasTrackPlaying(user)) { - return checkSpotify(spotifyService, 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(); - return EmbedUtils.successEmbed() - .setDescription("%s Resumed the track **[%s | %s](%s)**".formatted( - Emojis.CHECK_MARK_EMOJI, - track.getName(), - track.getArtists()[0].getName(), - SpotifyUtils.getTrackUrl(currentlyPlaying) - )); - } -} 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 deleted file mode 100644 index 7ea2d7a..0000000 --- a/src/main/java/cc/fascinated/bat/features/spotify/command/CurrentSubCommand.java +++ /dev/null @@ -1,37 +0,0 @@ -package cc.fascinated.bat.features.spotify.command; - -import cc.fascinated.bat.command.BatCommand; -import cc.fascinated.bat.command.CommandInfo; -import cc.fascinated.bat.event.EventListener; -import cc.fascinated.bat.features.spotify.SpotifyFeature; -import cc.fascinated.bat.model.BatGuild; -import cc.fascinated.bat.model.BatUser; -import cc.fascinated.bat.service.SpotifyService; -import lombok.NonNull; -import lombok.extern.log4j.Log4j2; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.Message; -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; - -/** - * @author Fascinated (fascinated7) - */ -@Component -@Log4j2(topic = "Spotify Current Command") -@CommandInfo(name = "current", description = "Gets the currently playing Spotify track") -public class CurrentSubCommand extends BatCommand implements EventListener { - private final SpotifyService spotifyService; - - @Autowired - public CurrentSubCommand(@NonNull SpotifyService spotifyService) { - this.spotifyService = spotifyService; - } - - @Override - public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, Message commandMessage, String[] arguments, SlashCommandInteraction event) { - event.replyEmbeds(SpotifyFeature.currentSong(spotifyService, user).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 deleted file mode 100644 index 3132529..0000000 --- a/src/main/java/cc/fascinated/bat/features/spotify/command/LinkSubCommand.java +++ /dev/null @@ -1,110 +0,0 @@ -package cc.fascinated.bat.features.spotify.command; - -import cc.fascinated.bat.Emojis; -import cc.fascinated.bat.command.BatCommand; -import cc.fascinated.bat.command.CommandInfo; -import cc.fascinated.bat.common.EmbedUtils; -import cc.fascinated.bat.event.EventListener; -import cc.fascinated.bat.model.BatGuild; -import cc.fascinated.bat.model.BatUser; -import cc.fascinated.bat.service.SpotifyService; -import lombok.NonNull; -import lombok.SneakyThrows; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.Message; -import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; -import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent; -import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; -import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction; -import net.dv8tion.jda.api.interactions.components.ActionRow; -import net.dv8tion.jda.api.interactions.components.buttons.Button; -import net.dv8tion.jda.api.interactions.components.text.TextInput; -import net.dv8tion.jda.api.interactions.components.text.TextInputStyle; -import net.dv8tion.jda.api.interactions.modals.Modal; -import net.dv8tion.jda.api.interactions.modals.ModalMapping; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -/** - * @author Fascinated (fascinated7) - */ -@Component -@CommandInfo(name = "link", description = "Link your Spotify account") -public class LinkSubCommand extends BatCommand implements EventListener { - private final SpotifyService spotifyService; - - @Autowired - public LinkSubCommand(@NonNull SpotifyService spotifyService) { - this.spotifyService = spotifyService; - } - - @Override @SneakyThrows - public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, Message commandMessage, String[] arguments, SlashCommandInteraction event) { -// if (!user.getId().equals(Consts.BOT_OWNER)) { -// event.replyEmbeds(EmbedUtils.errorEmbed() -// .setDescription(""" -// %s We are currently awaiting Spotify's approval for our application. Please check back later. -// Submitted on: -// """.formatted(Emojis.CROSS_MARK_EMOJI)) -// .build()) -// .setEphemeral(true) -// .queue(); -// return; -// } - event.replyEmbeds(EmbedUtils.genericEmbed() - .setDescription("%s You can link your Spotify account by clicking [here](%s)".formatted( - Emojis.SPOTIFY_EMOJI.getFormatted(), - spotifyService.getAuthorizationUrl() - )).build()) - .addComponents(ActionRow.of(Button.primary("spotify_link", "Link Code").withEmoji(Emojis.SPOTIFY_EMOJI))) - .setEphemeral(true) - .queue(); - } - - @Override - public void onButtonInteraction(BatGuild guild, @NonNull BatUser user, @NonNull ButtonInteractionEvent event) { - if (!event.getComponentId().equals("spotify_link")) { - return; - } - - TextInput code = TextInput.create("code", "Link Code", TextInputStyle.SHORT) - .setPlaceholder("Your link code") - .setMinLength(0) - .setMaxLength(16) - .build(); - - Modal modal = Modal.create("link_modal", "Link Spotify Account") - .addComponents(ActionRow.of(code)) - .build(); - - event.replyModal(modal).queue(); - } - - @Override @SneakyThrows - public void onModalInteraction(BatGuild guild, @NonNull BatUser user, @NonNull ModalInteractionEvent event) { - if (!event.getModalId().equals("link_modal")) { - return; - } - - ModalMapping codeMapping = event.getValue("code"); - if (codeMapping == null) { - 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()) - .setEphemeral(true) - .queue(); - return; - } - - spotifyService.linkAccount(user, code); - event.replyEmbeds(EmbedUtils.successEmbed() - .setDescription("%s You have linked your Spotify account!".formatted(Emojis.CHECK_MARK_EMOJI.getFormatted())) - .build()) - .setEphemeral(true) - .queue(); - } -} 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 deleted file mode 100644 index 0674912..0000000 --- a/src/main/java/cc/fascinated/bat/features/spotify/command/PauseSubCommand.java +++ /dev/null @@ -1,34 +0,0 @@ -package cc.fascinated.bat.features.spotify.command; - -import cc.fascinated.bat.command.BatCommand; -import cc.fascinated.bat.command.CommandInfo; -import cc.fascinated.bat.features.spotify.SpotifyFeature; -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.Message; -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; - -/** - * @author Fascinated (fascinated7) - */ -@Component -@CommandInfo(name = "pause", description = "Pause the current Spotify track") -public class PauseSubCommand extends BatCommand { - private final SpotifyService spotifyService; - - @Autowired - public PauseSubCommand(@NonNull SpotifyService spotifyService) { - this.spotifyService = spotifyService; - } - - @Override - public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, Message commandMessage, String[] arguments, SlashCommandInteraction event) { - event.replyEmbeds(SpotifyFeature.pauseSong(spotifyService, user).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 deleted file mode 100644 index b4a6b19..0000000 --- a/src/main/java/cc/fascinated/bat/features/spotify/command/ResumeSubCommand.java +++ /dev/null @@ -1,34 +0,0 @@ -package cc.fascinated.bat.features.spotify.command; - -import cc.fascinated.bat.command.BatCommand; -import cc.fascinated.bat.command.CommandInfo; -import cc.fascinated.bat.features.spotify.SpotifyFeature; -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.Message; -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; - -/** - * @author Fascinated (fascinated7) - */ -@Component -@CommandInfo(name = "resume", description = "Resume the current Spotify track") -public class ResumeSubCommand extends BatCommand { - private final SpotifyService spotifyService; - - @Autowired - public ResumeSubCommand(@NonNull SpotifyService spotifyService) { - this.spotifyService = spotifyService; - } - - @Override - public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, Message commandMessage, String[] arguments, SlashCommandInteraction event) { - event.replyEmbeds(SpotifyFeature.resumeSong(spotifyService, user).build()).queue(); - } -} diff --git a/src/main/java/cc/fascinated/bat/features/spotify/command/SkipSubCommand.java b/src/main/java/cc/fascinated/bat/features/spotify/command/SkipSubCommand.java deleted file mode 100644 index e21f6ef..0000000 --- a/src/main/java/cc/fascinated/bat/features/spotify/command/SkipSubCommand.java +++ /dev/null @@ -1,37 +0,0 @@ -package cc.fascinated.bat.features.spotify.command; - -import cc.fascinated.bat.command.BatCommand; -import cc.fascinated.bat.command.CommandInfo; -import cc.fascinated.bat.features.spotify.SpotifyFeature; -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.Message; -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; - -/** - * @author Fascinated (fascinated7) - */ -@Component -@CommandInfo(name = "skip", description = "Skip the current Spotify track") -public class SkipSubCommand extends BatCommand { - 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, Message commandMessage, String[] arguments, SlashCommandInteraction event) { - event.replyEmbeds(SpotifyFeature.skipSong(spotifyService, user).build()).queue(); - } -} diff --git a/src/main/java/cc/fascinated/bat/features/spotify/command/SpotifyCommand.java b/src/main/java/cc/fascinated/bat/features/spotify/command/SpotifyCommand.java deleted file mode 100644 index 9ba118f..0000000 --- a/src/main/java/cc/fascinated/bat/features/spotify/command/SpotifyCommand.java +++ /dev/null @@ -1,28 +0,0 @@ -package cc.fascinated.bat.features.spotify.command; - -import cc.fascinated.bat.command.BatCommand; -import cc.fascinated.bat.command.Category; -import cc.fascinated.bat.command.CommandInfo; -import lombok.NonNull; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.stereotype.Component; - -/** - * @author Fascinated (fascinated7) - */ -@Component -@CommandInfo(name = "spotify", description = "Change your Spotify settings", guildOnly = false, userInstall = true, category = Category.MEDIA) -public class SpotifyCommand extends BatCommand { - @Autowired - public SpotifyCommand(@NonNull ApplicationContext context) { - super.addSubCommands( - context.getBean(LinkSubCommand.class), - context.getBean(UnlinkSubCommand.class), - context.getBean(PauseSubCommand.class), - context.getBean(ResumeSubCommand.class), - context.getBean(CurrentSubCommand.class), - context.getBean(SkipSubCommand.class) - ); - } -} 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 deleted file mode 100644 index e3eeaae..0000000 --- a/src/main/java/cc/fascinated/bat/features/spotify/command/UnlinkSubCommand.java +++ /dev/null @@ -1,50 +0,0 @@ -package cc.fascinated.bat.features.spotify.command; - -import cc.fascinated.bat.Emojis; -import cc.fascinated.bat.command.BatCommand; -import cc.fascinated.bat.command.CommandInfo; -import cc.fascinated.bat.common.EmbedUtils; -import cc.fascinated.bat.event.EventListener; -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 lombok.SneakyThrows; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.Message; -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; - -/** - * @author Fascinated (fascinated7) - */ -@Component -@CommandInfo(name = "unlink", description = "Unlink your Spotify account") -public class UnlinkSubCommand extends BatCommand implements EventListener { - private final SpotifyService spotifyService; - - @Autowired - public UnlinkSubCommand(@NonNull SpotifyService spotifyService) { - this.spotifyService = spotifyService; - } - - @Override @SneakyThrows - public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, Message commandMessage, String[] arguments, SlashCommandInteraction event) { - SpotifyProfile profile = user.getProfile(SpotifyProfile.class); - if (!profile.hasLinkedAccount() || !spotifyService.hasTrackPlaying(user)) { - event.replyEmbeds(SpotifyFeature.checkSpotify(spotifyService, user).build()).queue(); - return; - } - - profile.reset(); - event.replyEmbeds(EmbedUtils.successEmbed() - .setDescription("%s Successfully unlinked your Spotify account.".formatted(Emojis.CHECK_MARK_EMOJI)) - .build()) - .setEphemeral(true) - .queue(); - } -} diff --git a/src/main/java/cc/fascinated/bat/features/spotify/profile/SpotifyProfile.java b/src/main/java/cc/fascinated/bat/features/spotify/profile/SpotifyProfile.java deleted file mode 100644 index 319279d..0000000 --- a/src/main/java/cc/fascinated/bat/features/spotify/profile/SpotifyProfile.java +++ /dev/null @@ -1,59 +0,0 @@ -package cc.fascinated.bat.features.spotify.profile; - -import cc.fascinated.bat.common.Serializable; -import com.google.gson.Gson; -import lombok.Getter; -import lombok.Setter; -import org.bson.Document; - -/** - * @author Fascinated (fascinated7) - */ -@Getter @Setter -public class SpotifyProfile extends Serializable { - /** - * The access token - */ - private String accessToken; - - /** - * The refresh token - */ - private String refreshToken; - - /** - * When the access token expires - */ - private Long expiresAt; - - /** - * Checks if the account has a linked account - * - * @return if the account has a linked account - */ - public boolean hasLinkedAccount() { - return this.accessToken != null && this.refreshToken != null; - } - - @Override - public void reset() { - this.accessToken = null; - this.refreshToken = null; - } - - @Override - public void load(Document document, Gson gson) { - this.accessToken = document.getString("accessToken"); - this.refreshToken = document.getString("refreshToken"); - this.expiresAt = document.getLong("expiresAt"); - } - - @Override - public Document serialize(Gson gson) { - Document document = new Document(); - document.put("accessToken", this.accessToken); - document.put("refreshToken", this.refreshToken); - document.put("expiresAt", this.expiresAt); - return document; - } -} diff --git a/src/main/java/cc/fascinated/bat/migrations/MongockSuccessEventListener.java b/src/main/java/cc/fascinated/bat/migrations/MongockSuccessEventListener.java deleted file mode 100644 index 3f6b346..0000000 --- a/src/main/java/cc/fascinated/bat/migrations/MongockSuccessEventListener.java +++ /dev/null @@ -1,16 +0,0 @@ -package cc.fascinated.bat.migrations; - -import io.mongock.runner.spring.base.events.SpringMigrationSuccessEvent; -import lombok.extern.log4j.Log4j2; -import org.jetbrains.annotations.NotNull; -import org.springframework.context.ApplicationListener; -import org.springframework.stereotype.Component; - -@Component -@Log4j2(topic = "Mongock Listener") -public class MongockSuccessEventListener implements ApplicationListener { - @Override - public void onApplicationEvent(@NotNull SpringMigrationSuccessEvent event) { - log.info("Successfully ran Mongock migrations"); - } -} \ No newline at end of file diff --git a/src/main/java/cc/fascinated/bat/model/BatGuild.java b/src/main/java/cc/fascinated/bat/model/BatGuild.java index e65a974..94c22a8 100644 --- a/src/main/java/cc/fascinated/bat/model/BatGuild.java +++ b/src/main/java/cc/fascinated/bat/model/BatGuild.java @@ -8,7 +8,6 @@ import cc.fascinated.bat.features.birthday.profile.BirthdayProfile; import cc.fascinated.bat.features.counter.CounterProfile; import cc.fascinated.bat.features.leveling.LevelingProfile; import cc.fascinated.bat.features.logging.LogProfile; -import cc.fascinated.bat.features.minecraft.MinecraftProfile; import cc.fascinated.bat.features.moderation.punish.PunishmentProfile; import cc.fascinated.bat.features.namehistory.profile.guild.NameHistoryProfile; import cc.fascinated.bat.features.reminder.ReminderProfile; @@ -23,8 +22,6 @@ import lombok.NonNull; import lombok.Setter; import lombok.extern.log4j.Log4j2; import net.dv8tion.jda.api.entities.Guild; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.data.annotation.Id; import java.util.Date; @@ -182,15 +179,6 @@ public class BatGuild extends ProfileHolder { return getProfile(LevelingProfile.class); } - /** - * Gets the minecraft profile - * - * @return the minecraft profile - */ - public MinecraftProfile getMinecraftProfile() { - return getProfile(MinecraftProfile.class); - } - /** * Gets the stats channel profile * diff --git a/src/main/java/cc/fascinated/bat/model/BatUser.java b/src/main/java/cc/fascinated/bat/model/BatUser.java index bb887fd..e52d8e5 100644 --- a/src/main/java/cc/fascinated/bat/model/BatUser.java +++ b/src/main/java/cc/fascinated/bat/model/BatUser.java @@ -5,7 +5,6 @@ import cc.fascinated.bat.common.ProfileHolder; import cc.fascinated.bat.common.Serializable; import cc.fascinated.bat.common.UserUtils; import cc.fascinated.bat.features.namehistory.profile.user.NameHistoryProfile; -import cc.fascinated.bat.features.scoresaber.profile.user.ScoreSaberProfile; import cc.fascinated.bat.service.DiscordService; import cc.fascinated.bat.service.MongoService; import com.mongodb.client.model.ReplaceOptions; @@ -87,15 +86,6 @@ public class BatUser extends ProfileHolder { return user; } - /** - * Gets the user's ScoreSaber profile - * - * @return the user's ScoreSaber profile - */ - public ScoreSaberProfile getScoreSaberProfile() { - return getProfile(ScoreSaberProfile.class); - } - /** * Gets the user's name history profile * diff --git a/src/main/java/cc/fascinated/bat/service/InteractionService.java b/src/main/java/cc/fascinated/bat/service/InteractionService.java index 1965064..abe96a4 100644 --- a/src/main/java/cc/fascinated/bat/service/InteractionService.java +++ b/src/main/java/cc/fascinated/bat/service/InteractionService.java @@ -1,9 +1,6 @@ package cc.fascinated.bat.service; import cc.fascinated.bat.common.InteractionBuilder; -import cc.fascinated.bat.event.EventListener; -import cc.fascinated.bat.model.BatGuild; -import cc.fascinated.bat.model.BatUser; import lombok.NonNull; import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; import net.dv8tion.jda.api.events.interaction.component.StringSelectInteractionEvent; diff --git a/src/main/java/cc/fascinated/bat/service/ScoreSaberService.java b/src/main/java/cc/fascinated/bat/service/ScoreSaberService.java deleted file mode 100644 index 4c7245c..0000000 --- a/src/main/java/cc/fascinated/bat/service/ScoreSaberService.java +++ /dev/null @@ -1,254 +0,0 @@ -package cc.fascinated.bat.service; - -import cc.fascinated.bat.BatApplication; -import cc.fascinated.bat.common.DateUtils; -import cc.fascinated.bat.common.WebRequest; -import cc.fascinated.bat.common.beatsaber.leaderboard.impl.ScoreSaberLeaderboard; -import cc.fascinated.bat.event.EventListener; -import cc.fascinated.bat.exception.BadRequestException; -import cc.fascinated.bat.exception.ResourceNotFoundException; -import cc.fascinated.bat.features.scoresaber.profile.user.ScoreSaberProfile; -import cc.fascinated.bat.model.token.beatsaber.scoresaber.*; -import com.google.gson.JsonObject; -import lombok.*; -import lombok.extern.log4j.Log4j2; -import net.jodah.expiringmap.ExpiringMap; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.DependsOn; -import org.springframework.stereotype.Service; -import org.springframework.web.socket.CloseStatus; -import org.springframework.web.socket.TextMessage; -import org.springframework.web.socket.WebSocketSession; -import org.springframework.web.socket.client.standard.StandardWebSocketClient; -import org.springframework.web.socket.handler.TextWebSocketHandler; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; - -@Service -@Log4j2(topic = "ScoreSaber Service") -@DependsOn("discordService") -public class ScoreSaberService extends TextWebSocketHandler { - private static final String SCORESABER_API = "https://scoresaber.com/api/"; - private static final String GET_PLAYER_ENDPOINT = SCORESABER_API + "player/%s/full"; - private static final String GET_PLAYER_SCORES_ENDPOINT = SCORESABER_API + "player/%s/scores?limit=100&sort=%s&page=%s&withMetadata=true"; - - /** - * The cached accounts. - */ - private final Map cachedAccounts = ExpiringMap.builder() - .expiration(5, TimeUnit.MINUTES) - .build(); - - /** - * The cached score pages. - */ - private final Map cachedScorePages = ExpiringMap.builder() - .expiration(30, TimeUnit.MINUTES) - .build(); - - @Autowired - public ScoreSaberService() { - connectWebSocket(); - } - - /** - * Checks if the account is cached. - * - * @param id The id of the account. - * @return If the account is cached. - */ - public boolean isCached(String id) { - return cachedAccounts.containsKey(id); - } - - /** - * Gets the account from the ScoreSaber API. - * - * @param id The id of the account. - * @return The account. - * @throws ResourceNotFoundException If the account is not found. - * @throws cc.fascinated.bat.exception.RateLimitException If the ScoreSaber rate limit is reached. - */ - public ScoreSaberAccountToken getAccount(String id) { - if (cachedAccounts.containsKey(id)) { - return cachedAccounts.get(id); - } - - ScoreSaberAccountToken account = WebRequest.getAsEntity(String.format(GET_PLAYER_ENDPOINT, id), ScoreSaberAccountToken.class); - if (account == null) { // Check if the account doesn't exist. - log.info("Account with id '{}' not found.", id); - throw new ResourceNotFoundException("Account with id '%s' not found.".formatted(id)); - } - if (account.isBanned()) { - throw new BadRequestException("Account with id '%s' is banned.".formatted(id)); - } - if (account.isInactive()) { - throw new BadRequestException("Account with id '%s' is inactive.".formatted(id)); - } - cachedAccounts.put(id, account); - return account; - } - - /** - * Gets the scores for the account. - * - * @param profile The profile. - * @param page The page to get the scores from. - * @return The scores. - */ - public CachedPage getPageScores(ScoreSaberProfile profile, int page) { - String key = profile.getAccountId() + "." + page; - // Check if the page is cached, but don't cache the first page as it's the most likely to change. - CachedPage cachedPage = cachedScorePages.get(key); - if (cachedScorePages.containsKey(key) && page != 1) { - cachedPage.setCached(true); - return cachedPage; - } - - if (page == 1 || page % 5 == 0 || (cachedPage != null && cachedPage.getPage().getMetadata().getTotal() == page)) { - log.info("Fetching scores for account '{}' from page {}.", profile.getAccountId(), page); - } - ScoreSaberScoresPageToken pageToken = WebRequest.getAsEntity(String.format(GET_PLAYER_SCORES_ENDPOINT, profile.getAccountId(), "recent", page), ScoreSaberScoresPageToken.class); - if (pageToken == null) { // Check if the page doesn't exist. - return null; - } - // Sort the scores by newest time set. - pageToken.setPlayerScores(Arrays.stream(pageToken.getPlayerScores()) - .sorted((a, b) -> DateUtils.getDateFromString(b.getScore().getTimeSet()).compareTo(DateUtils.getDateFromString(a.getScore().getTimeSet()))) - .toArray(ScoreSaberPlayerScoreToken[]::new)); - cachedPage = new CachedPage(pageToken, false); - cachedScorePages.put(key, cachedPage); - return cachedPage; - } - - /** - * Gets the scores for the account. - * - * @param profile The profile. - * @return The scores. - */ - public List getScores(ScoreSaberProfile profile, Consumer currentPageCallback) { - List scores = new ArrayList<>(List.of(getPageScores(profile, 1))); - ScoreSaberPageMetadataToken metadata = scores.get(0).getPage().getMetadata(); - int totalPages = (int) Math.ceil((double) metadata.getTotal() / metadata.getItemsPerPage()); - log.info("Fetching {} pages of scores for account '{}'.", totalPages, profile.getAccountId()); - for (int i = 2; i <= totalPages; i++) { - scores.add(getPageScores(profile, i)); - currentPageCallback.accept(new CurrentPageCallback(i, totalPages)); - } - return scores; - } - - /** - * Gets the raw pp per global pp for the given account. - * - * @param account The account. - * @return The raw pp per global pp. - */ - public RawPerGlobal getRawPerGlobal(ScoreSaberProfile account, Consumer currentPageCallback) { - List scores = getScores(account, currentPageCallback); - List playerScores = new ArrayList<>(); - for (CachedPage score : scores) { - for (ScoreSaberPlayerScoreToken playerScore : score.getPage().getPlayerScores()) { - playerScores.add(playerScore.getScore()); - } - } - return new RawPerGlobal( - ScoreSaberLeaderboard.INSTANCE.getRawPerGlobalPP(playerScores, 1), - (int) scores.stream().filter(CachedPage::isCached).count(), - scores.size() - ); - } - - /** - * Connects to the ScoreSaber WebSocket. - */ - @SneakyThrows - private void connectWebSocket() { - log.info("Connecting to the ScoreSaber WSS."); - new StandardWebSocketClient().execute(this, "wss://scoresaber.com/ws").get(); - } - - @Override - public void afterConnectionEstablished(@NonNull WebSocketSession session) { - log.info("Connected to the ScoreSaber WSS."); - } - - @Override - public void afterConnectionClosed(@NonNull WebSocketSession session, @NonNull CloseStatus status) { - log.info("Disconnected from the ScoreSaber WSS."); - connectWebSocket(); // Reconnect to the WebSocket. - } - - @Override - @SneakyThrows - protected void handleTextMessage(@NonNull WebSocketSession session, @NonNull TextMessage message) { - // Ignore the connection message. - if (message.getPayload().equals("Connected to the ScoreSaber WSS")) { - return; - } - - try { - JsonObject json = BatApplication.GSON.fromJson(message.getPayload(), JsonObject.class); - String command = json.get("commandName").getAsString(); - JsonObject data = json.get("commandData").getAsJsonObject(); - - if (command.equals("score")) { - ScoreSaberPlayerScoreToken score = BatApplication.GSON.fromJson(data, ScoreSaberPlayerScoreToken.class); - ScoreSaberScoreToken.LeaderboardPlayerInfo playerInfo = score.getScore().getLeaderboardPlayerInfo(); - - for (EventListener listener : EventService.LISTENERS) { - listener.onScoresaberScoreReceived(score, score.getLeaderboard(), playerInfo); - } - } - } catch (Exception ex) { - log.error("An error occurred while handling the message.", ex); - } - } - - @AllArgsConstructor - @Getter - public static class CurrentPageCallback { - private int currentPage; - private int totalPages; - } - - @AllArgsConstructor - @Getter - @Setter - public static class CachedPage { - /** - * The page of scores. - */ - private ScoreSaberScoresPageToken page; - - /** - * Whether the page is cached. - */ - private boolean cached; - } - - @AllArgsConstructor - @Getter - public static class RawPerGlobal { - /** - * The raw pp per global pp. - */ - private double rawPerGlobal; - - /** - * The amount of pages that were cached. - */ - private int cachedPages; - - /** - * The total amount of pages. - */ - private int totalPages; - } -} diff --git a/src/main/java/cc/fascinated/bat/service/SpotifyService.java b/src/main/java/cc/fascinated/bat/service/SpotifyService.java deleted file mode 100644 index 8826111..0000000 --- a/src/main/java/cc/fascinated/bat/service/SpotifyService.java +++ /dev/null @@ -1,239 +0,0 @@ -package cc.fascinated.bat.service; - -import cc.fascinated.bat.common.StringUtils; -import cc.fascinated.bat.features.spotify.profile.SpotifyProfile; -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.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.DependsOn; -import org.springframework.stereotype.Service; -import se.michaelthelin.spotify.SpotifyApi; -import se.michaelthelin.spotify.enums.AuthorizationScope; -import se.michaelthelin.spotify.exceptions.SpotifyWebApiException; -import se.michaelthelin.spotify.model_objects.credentials.AuthorizationCodeCredentials; -import se.michaelthelin.spotify.model_objects.miscellaneous.CurrentlyPlaying; - -import java.net.URI; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -/** - * @author Fascinated (fascinated7) - */ -@Service -@Getter -@Log4j2(topic = "Spotify Service") -@DependsOn("discordService") -public class SpotifyService { - /** - * The access token map. - */ - private final Map accessToken = ExpiringMap.builder() - .expiration(30, TimeUnit.MINUTES) - .build(); - - /** - * A cache of the currently playing track for each user. - */ - private final Map currentlyPlayingCache = ExpiringMap.builder() - .expiration(30, TimeUnit.SECONDS) - .build(); - - /** - * The client ID. - */ - private final String clientId; - - /** - * The client secret. - */ - private final String clientSecret; - - /** - * The Spotify API instance. - */ - private final SpotifyApi spotifyApi; - - /** - * The user service. - */ - private final UserService userService; - - /** - * The authorization URL. - */ - private final String authorizationUrl; - - public SpotifyService(@Value("${spotify.client-id}") String clientId, @Value("${spotify.client-secret}") String clientSecret, - @Value("${spotify.redirect-uri}") String redirectUri, @NonNull UserService userService) { - this.clientId = clientId; - this.clientSecret = clientSecret; - this.userService = userService; - - this.spotifyApi = new SpotifyApi.Builder() - .setClientId(clientId) - .setClientSecret(clientSecret) - .setRedirectUri(URI.create(redirectUri)) - .build(); - this.authorizationUrl = spotifyApi.authorizationCodeUri() - .response_type("code") - .scope(AuthorizationScope.values()) - .build().execute().toString(); - } - - /** - * Starts playback for the user. - * - * @param user the user to start playback for - */ - @SneakyThrows - public void skipTrack(BatUser user) { - getSpotifyApi(user).skipUsersPlaybackToNextTrack().build().execute(); - } - - /** - * Gets the currently playing track for the user. - * - * @param user the user to check - * @return the currently playing track - */ - @SneakyThrows - public CurrentlyPlaying getCurrentlyPlayingTrack(BatUser user) { - CurrentlyPlaying currentlyPlaying = currentlyPlayingCache.get(user); - // If the track is still playing return the cache otherwise fetch the track - if (currentlyPlaying != null && currentlyPlaying.getTimestamp() + currentlyPlaying.getProgress_ms() < System.currentTimeMillis()) { - return currentlyPlaying; - } - currentlyPlaying = getSpotifyApi(user).getUsersCurrentlyPlayingTrack().build().execute(); - currentlyPlayingCache.put(user, currentlyPlaying); - return currentlyPlaying; - } - - /** - * Checks if the user has a track playing. - * - * @param user the user to check - * @return whether a track is playing - */ - public boolean hasTrackPlaying(BatUser user) { - return getCurrentlyPlayingTrack(user) != null; - } - - /** - * Pauses playback for the user. - * - * @param user the user to start playback for - */ - @SneakyThrows - public void pausePlayback(BatUser user) { - getSpotifyApi(user).pauseUsersPlayback().build().execute(); - } - - /** - * Pauses playback for the user. - * - * @param user the user to start playback for - */ - @SneakyThrows - public void resumePlayback(BatUser user) { - getSpotifyApi(user).startResumeUsersPlayback().build().execute(); - } - - /** - * Gets the authorization key to link the user's - * Spotify account with their Discord account. - * - * @param code the code to authorize with - * @return the authorization details - */ - @SneakyThrows - public String authorize(String code) { - if (code == null) { - return """ - Click here to authorize your Spotify account - """.formatted(authorizationUrl); - } - AuthorizationCodeCredentials credentials = spotifyApi.authorizationCode(code).build().execute(); - String key = StringUtils.randomString(16); - accessToken.put(key, credentials); - return """ -

Successfully authorized your Spotify account!

-

Your key is: %s

- """.formatted(key); - } - - /** - * Gets a new Spotify API instance. - * - * @return the Spotify API - */ - @SneakyThrows - public SpotifyApi getSpotifyApi(BatUser user) { - SpotifyProfile profile = user.getProfile(SpotifyProfile.class); - ensureValidToken(profile, user); - return new SpotifyApi.Builder().setAccessToken(profile.getAccessToken()).build(); - } - - /** - * Ensures the user has a valid Spotify access token. - *

- * If the token is expired, it will be refreshed. - *

- * - * @param user the user to get the token for - */ - @SneakyThrows - public void ensureValidToken(SpotifyProfile profile, BatUser user) { - // If the token is still valid, return - if (profile.getExpiresAt() > System.currentTimeMillis()) { - return; - } - SpotifyApi api = new SpotifyApi.Builder() - .setClientId(clientId) - .setClientSecret(clientSecret) - .setAccessToken(profile.getAccessToken()) - .setRefreshToken(profile.getRefreshToken()) - .build(); - try { - AuthorizationCodeCredentials credentials = api.authorizationCodeRefresh().build().execute(); - profile.setAccessToken(credentials.getAccessToken()); - profile.setExpiresAt(System.currentTimeMillis() + (credentials.getExpiresIn() * 1000)); - log.info("Refreshed Spotify token for user \"{}\"", user.getName()); - } catch (SpotifyWebApiException ex) { - log.error("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)); - 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); - } -}