diff --git a/src/main/java/cc/fascinated/bat/common/NumberUtils.java b/src/main/java/cc/fascinated/bat/common/NumberUtils.java index d3c6f12..a23b691 100644 --- a/src/main/java/cc/fascinated/bat/common/NumberUtils.java +++ b/src/main/java/cc/fascinated/bat/common/NumberUtils.java @@ -1,6 +1,5 @@ package cc.fascinated.bat.common; -import java.text.DecimalFormat; import java.text.NumberFormat; /** diff --git a/src/main/java/cc/fascinated/bat/event/EventListener.java b/src/main/java/cc/fascinated/bat/event/EventListener.java new file mode 100644 index 0000000..9373905 --- /dev/null +++ b/src/main/java/cc/fascinated/bat/event/EventListener.java @@ -0,0 +1,20 @@ +package cc.fascinated.bat.event; + +import cc.fascinated.bat.model.beatsaber.scoresaber.ScoreSaberLeaderboardToken; +import cc.fascinated.bat.model.beatsaber.scoresaber.ScoreSaberPlayerScoreToken; +import cc.fascinated.bat.model.beatsaber.scoresaber.ScoreSaberScoreToken; + +/** + * @author Fascinated (fascinated7) + */ +public interface EventListener { + /** + * Called when a ScoreSaber score is received + * + * @param score the score that was set + * @param leaderboard the leaderboard that the score was set on + * @param player the player that set the score + */ + default void onScoresaberScoreReceived(ScoreSaberPlayerScoreToken score, ScoreSaberLeaderboardToken leaderboard, + ScoreSaberScoreToken.LeaderboardPlayerInfo player) {} +} diff --git a/src/main/java/cc/fascinated/bat/features/scoresaber/ScoreSaberScoreFeedListener.java b/src/main/java/cc/fascinated/bat/features/scoresaber/ScoreSaberScoreFeedListener.java new file mode 100644 index 0000000..888982e --- /dev/null +++ b/src/main/java/cc/fascinated/bat/features/scoresaber/ScoreSaberScoreFeedListener.java @@ -0,0 +1,92 @@ +package cc.fascinated.bat.features.scoresaber; + +import cc.fascinated.bat.common.Colors; +import cc.fascinated.bat.common.DateUtils; +import cc.fascinated.bat.common.NumberUtils; +import cc.fascinated.bat.common.ScoreSaberUtils; +import cc.fascinated.bat.event.EventListener; +import cc.fascinated.bat.model.beatsaber.scoresaber.ScoreSaberLeaderboardToken; +import cc.fascinated.bat.model.beatsaber.scoresaber.ScoreSaberPlayerScoreToken; +import cc.fascinated.bat.model.beatsaber.scoresaber.ScoreSaberScoreToken; +import cc.fascinated.bat.model.guild.BatGuild; +import cc.fascinated.bat.model.guild.profiles.ScoreSaberUserScoreFeedProfile; +import cc.fascinated.bat.service.DiscordService; +import cc.fascinated.bat.service.GuildService; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.MessageEmbed; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * @author Fascinated (fascinated7) + */ +@Component +public class ScoreSaberScoreFeedListener implements EventListener { + private final GuildService guildService; + + @Autowired + public ScoreSaberScoreFeedListener(GuildService guildService) { + this.guildService = guildService; + } + + @Override + public void onScoresaberScoreReceived(ScoreSaberPlayerScoreToken score, ScoreSaberLeaderboardToken leaderboard, + ScoreSaberScoreToken.LeaderboardPlayerInfo player) { + for (Guild guild : DiscordService.JDA.getGuilds()) { + BatGuild batGuild = guildService.getGuild(guild.getId()); + if (batGuild == null) { + continue; + } + + ScoreSaberUserScoreFeedProfile profile = batGuild.getProfile(ScoreSaberUserScoreFeedProfile.class); + if (profile == null) { + continue; + } + if (profile.getChannelId() == null) { + continue; + } + if (!profile.getTrackedUsers().contains(player.getId())) { + continue; + } + + profile.getAsTextChannel().sendMessageEmbeds(this.buildScoreEmbed(score)).queue(); + } + } + + /** + * Builds an embed for a score. + * + * @param score The score. + * @return The embed. + */ + public MessageEmbed buildScoreEmbed(ScoreSaberPlayerScoreToken score) { + ScoreSaberScoreToken scoreToken = score.getScore(); + ScoreSaberLeaderboardToken leaderboardToken = score.getLeaderboard(); + ScoreSaberScoreToken.LeaderboardPlayerInfo playerInfo = scoreToken.getLeaderboardPlayerInfo(); + return new EmbedBuilder() + .setAuthor(playerInfo.getName() + " just set a new score!", "https://scoresaber.com/u/%s".formatted(playerInfo.getId()), + "https://cdn.scoresaber.com/avatars/%s.jpg".formatted(playerInfo.getId())) + .setDescription("**%s** (%s%s)\n[[Map Link]](%s) [[SS Profile]](%s)".formatted( + leaderboardToken.getSongName(), + ScoreSaberUtils.getFormattedDifficulty(leaderboardToken.getDifficulty().getDifficulty()), + leaderboardToken.isRanked() ? " " + leaderboardToken.getStars() + "⭐" : "", + "https://scoresaber.com/leaderboard/%s".formatted(leaderboardToken.getId()), + "https://scoresaber.com/u/%s".formatted(playerInfo.getId()) + )) + .addField("Accuracy", "%s%%".formatted( + leaderboardToken.getMaxScore() == 0 ? "N/A" : NumberUtils.formatNumberCommas(((double) scoreToken.getBaseScore() / leaderboardToken.getMaxScore()) * 100) + ), true) + .addField("Raw PP", scoreToken.getPp() == 0 ? "Unranked" : NumberUtils.formatNumberCommas(scoreToken.getPp()), true) + .addField("Global Rank", "#%s".formatted(NumberUtils.formatNumberCommas(scoreToken.getRank())), true) + .addField("Misses", "%s".formatted(scoreToken.getMissedNotes()), true) + .addField("Bad Cuts", "%s".formatted(scoreToken.getBadCuts()), true) + .addField("Max Combo", "%s %s".formatted( + scoreToken.getMaxCombo(), + scoreToken.getMaxCombo() == leaderboardToken.getMaxScore() ? "(FC)" : "" + ), true) + .setColor(Colors.DEFAULT) + .setTimestamp(DateUtils.getDateFromString(scoreToken.getTimeSet()).toInstant()) + .build(); + } +} diff --git a/src/main/java/cc/fascinated/bat/command/impl/global/beatsaber/scoresaber/LinkSubCommand.java b/src/main/java/cc/fascinated/bat/features/scoresaber/command/scoresaber/LinkSubCommand.java similarity index 97% rename from src/main/java/cc/fascinated/bat/command/impl/global/beatsaber/scoresaber/LinkSubCommand.java rename to src/main/java/cc/fascinated/bat/features/scoresaber/command/scoresaber/LinkSubCommand.java index e18d4f5..2c46f70 100644 --- a/src/main/java/cc/fascinated/bat/command/impl/global/beatsaber/scoresaber/LinkSubCommand.java +++ b/src/main/java/cc/fascinated/bat/features/scoresaber/command/scoresaber/LinkSubCommand.java @@ -1,9 +1,9 @@ -package cc.fascinated.bat.command.impl.global.beatsaber.scoresaber; +package cc.fascinated.bat.features.scoresaber.command.scoresaber; import cc.fascinated.bat.command.BatSubCommand; import cc.fascinated.bat.common.EmbedUtils; -import cc.fascinated.bat.model.guild.BatGuild; import cc.fascinated.bat.model.beatsaber.scoresaber.ScoreSaberAccountToken; +import cc.fascinated.bat.model.guild.BatGuild; import cc.fascinated.bat.model.user.BatUser; import cc.fascinated.bat.model.user.profiles.ScoreSaberProfile; import cc.fascinated.bat.service.ScoreSaberService; diff --git a/src/main/java/cc/fascinated/bat/command/impl/global/beatsaber/scoresaber/ScoreSaberCommand.java b/src/main/java/cc/fascinated/bat/features/scoresaber/command/scoresaber/ScoreSaberCommand.java similarity index 81% rename from src/main/java/cc/fascinated/bat/command/impl/global/beatsaber/scoresaber/ScoreSaberCommand.java rename to src/main/java/cc/fascinated/bat/features/scoresaber/command/scoresaber/ScoreSaberCommand.java index 6a4fb9f..ca0b397 100644 --- a/src/main/java/cc/fascinated/bat/command/impl/global/beatsaber/scoresaber/ScoreSaberCommand.java +++ b/src/main/java/cc/fascinated/bat/features/scoresaber/command/scoresaber/ScoreSaberCommand.java @@ -1,21 +1,19 @@ -package cc.fascinated.bat.command.impl.global.beatsaber.scoresaber; +package cc.fascinated.bat.features.scoresaber.command.scoresaber; import cc.fascinated.bat.command.BatCommand; import cc.fascinated.bat.common.Colors; import cc.fascinated.bat.common.DateUtils; import cc.fascinated.bat.common.EmbedUtils; import cc.fascinated.bat.common.NumberUtils; -import cc.fascinated.bat.model.guild.BatGuild; import cc.fascinated.bat.model.beatsaber.scoresaber.ScoreSaberAccountToken; +import cc.fascinated.bat.model.guild.BatGuild; import cc.fascinated.bat.model.user.BatUser; import cc.fascinated.bat.model.user.profiles.ScoreSaberProfile; import cc.fascinated.bat.service.ScoreSaberService; import lombok.NonNull; import net.dv8tion.jda.api.EmbedBuilder; -import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; -import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions; 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; @@ -44,21 +42,9 @@ public class ScoreSaberCommand extends BatCommand { .addSubcommands(new SubcommandData("link", "Link your ScoreSaber profile") .addOptions(new OptionData(OptionType.STRING, "link", "Link your ScoreSaber profile", true)) ) - .addSubcommands(new SubcommandData("user", "View a user's ScoreSaber profile") .addOptions(new OptionData(OptionType.USER, "user", "The user to view the ScoreSaber profile of", true)) ) - - .addSubcommands(new SubcommandData("score-feed-user", "Edit your ScoreSaber score feed settings") - .addOptions(new OptionData(OptionType.USER, "user", "Add or remove a user from the score feed", false)) - ).setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.MANAGE_SERVER)) - - .addSubcommands(new SubcommandData("score-feed-clear-users", "Remove all users from the score feed")) - .setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.MANAGE_SERVER)) - - .addSubcommands(new SubcommandData("score-feed-channel", "Edit your ScoreSaber score feed settings") - .addOptions(new OptionData(OptionType.CHANNEL, "channel", "Set the channel to send the score feed in", false)) - ).setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.MANAGE_SERVER)) ); } diff --git a/src/main/java/cc/fascinated/bat/command/impl/global/beatsaber/scoresaber/UserSubCommand.java b/src/main/java/cc/fascinated/bat/features/scoresaber/command/scoresaber/UserSubCommand.java similarity index 96% rename from src/main/java/cc/fascinated/bat/command/impl/global/beatsaber/scoresaber/UserSubCommand.java rename to src/main/java/cc/fascinated/bat/features/scoresaber/command/scoresaber/UserSubCommand.java index 7e2f58e..9bf1b45 100644 --- a/src/main/java/cc/fascinated/bat/command/impl/global/beatsaber/scoresaber/UserSubCommand.java +++ b/src/main/java/cc/fascinated/bat/features/scoresaber/command/scoresaber/UserSubCommand.java @@ -1,4 +1,4 @@ -package cc.fascinated.bat.command.impl.global.beatsaber.scoresaber; +package cc.fascinated.bat.features.scoresaber.command.scoresaber; import cc.fascinated.bat.command.BatSubCommand; import cc.fascinated.bat.common.EmbedUtils; diff --git a/src/main/java/cc/fascinated/bat/command/impl/guild/beatsaber/scoresaber/ScoreFeedChannelCommand.java b/src/main/java/cc/fascinated/bat/features/scoresaber/command/userfeed/ScoreFeedChannelCommand.java similarity index 90% rename from src/main/java/cc/fascinated/bat/command/impl/guild/beatsaber/scoresaber/ScoreFeedChannelCommand.java rename to src/main/java/cc/fascinated/bat/features/scoresaber/command/userfeed/ScoreFeedChannelCommand.java index 99371c8..ffde758 100644 --- a/src/main/java/cc/fascinated/bat/command/impl/guild/beatsaber/scoresaber/ScoreFeedChannelCommand.java +++ b/src/main/java/cc/fascinated/bat/features/scoresaber/command/userfeed/ScoreFeedChannelCommand.java @@ -1,10 +1,10 @@ -package cc.fascinated.bat.command.impl.guild.beatsaber.scoresaber; +package cc.fascinated.bat.features.scoresaber.command.userfeed; import cc.fascinated.bat.command.BatSubCommand; import cc.fascinated.bat.common.EmbedUtils; import cc.fascinated.bat.common.TextChannelUtils; import cc.fascinated.bat.model.guild.BatGuild; -import cc.fascinated.bat.model.guild.profiles.ScoreSaberScoreFeedProfile; +import cc.fascinated.bat.model.guild.profiles.ScoreSaberUserScoreFeedProfile; import cc.fascinated.bat.model.user.BatUser; import cc.fascinated.bat.service.GuildService; import lombok.NonNull; @@ -31,7 +31,7 @@ public class ScoreFeedChannelCommand extends BatSubCommand { @Override public void execute(@NonNull BatGuild guild, @NonNull BatUser user, @NonNull TextChannel channel, @NonNull Member member, @NonNull SlashCommandInteraction interaction) { - ScoreSaberScoreFeedProfile profile = guild.getProfile(ScoreSaberScoreFeedProfile.class); + ScoreSaberUserScoreFeedProfile profile = guild.getProfile(ScoreSaberUserScoreFeedProfile.class); OptionMapping option = interaction.getOption("channel"); if (option == null) { if (!TextChannelUtils.isValidChannel(profile.getChannelId())) { diff --git a/src/main/java/cc/fascinated/bat/command/impl/guild/beatsaber/scoresaber/ScoreFeedClearUsersCommand.java b/src/main/java/cc/fascinated/bat/features/scoresaber/command/userfeed/ScoreFeedClearUsersCommand.java similarity index 76% rename from src/main/java/cc/fascinated/bat/command/impl/guild/beatsaber/scoresaber/ScoreFeedClearUsersCommand.java rename to src/main/java/cc/fascinated/bat/features/scoresaber/command/userfeed/ScoreFeedClearUsersCommand.java index b672bad..73a82ab 100644 --- a/src/main/java/cc/fascinated/bat/command/impl/guild/beatsaber/scoresaber/ScoreFeedClearUsersCommand.java +++ b/src/main/java/cc/fascinated/bat/features/scoresaber/command/userfeed/ScoreFeedClearUsersCommand.java @@ -1,18 +1,15 @@ -package cc.fascinated.bat.command.impl.guild.beatsaber.scoresaber; +package cc.fascinated.bat.features.scoresaber.command.userfeed; import cc.fascinated.bat.command.BatSubCommand; import cc.fascinated.bat.common.EmbedUtils; import cc.fascinated.bat.model.guild.BatGuild; -import cc.fascinated.bat.model.guild.profiles.ScoreSaberScoreFeedProfile; +import cc.fascinated.bat.model.guild.profiles.ScoreSaberUserScoreFeedProfile; import cc.fascinated.bat.model.user.BatUser; -import cc.fascinated.bat.model.user.profiles.ScoreSaberProfile; import cc.fascinated.bat.service.GuildService; import cc.fascinated.bat.service.UserService; import lombok.NonNull; import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; -import net.dv8tion.jda.api.interactions.commands.OptionMapping; import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -31,7 +28,7 @@ public class ScoreFeedClearUsersCommand extends BatSubCommand { @Override public void execute(@NonNull BatGuild guild, @NonNull BatUser user, @NonNull TextChannel channel, @NonNull Member member, @NonNull SlashCommandInteraction interaction) { - ScoreSaberScoreFeedProfile profile = guild.getProfile(ScoreSaberScoreFeedProfile.class); + ScoreSaberUserScoreFeedProfile profile = guild.getProfile(ScoreSaberUserScoreFeedProfile.class); profile.getTrackedUsers().clear(); guildService.saveGuild(guild); diff --git a/src/main/java/cc/fascinated/bat/command/impl/guild/beatsaber/scoresaber/ScoreFeedUserCommand.java b/src/main/java/cc/fascinated/bat/features/scoresaber/command/userfeed/ScoreFeedUserCommand.java similarity index 92% rename from src/main/java/cc/fascinated/bat/command/impl/guild/beatsaber/scoresaber/ScoreFeedUserCommand.java rename to src/main/java/cc/fascinated/bat/features/scoresaber/command/userfeed/ScoreFeedUserCommand.java index 0c52800..f235c5e 100644 --- a/src/main/java/cc/fascinated/bat/command/impl/guild/beatsaber/scoresaber/ScoreFeedUserCommand.java +++ b/src/main/java/cc/fascinated/bat/features/scoresaber/command/userfeed/ScoreFeedUserCommand.java @@ -1,10 +1,9 @@ -package cc.fascinated.bat.command.impl.guild.beatsaber.scoresaber; +package cc.fascinated.bat.features.scoresaber.command.userfeed; import cc.fascinated.bat.command.BatSubCommand; import cc.fascinated.bat.common.EmbedUtils; -import cc.fascinated.bat.common.Profile; import cc.fascinated.bat.model.guild.BatGuild; -import cc.fascinated.bat.model.guild.profiles.ScoreSaberScoreFeedProfile; +import cc.fascinated.bat.model.guild.profiles.ScoreSaberUserScoreFeedProfile; import cc.fascinated.bat.model.user.BatUser; import cc.fascinated.bat.model.user.profiles.ScoreSaberProfile; import cc.fascinated.bat.service.GuildService; @@ -34,7 +33,7 @@ public class ScoreFeedUserCommand extends BatSubCommand { @Override public void execute(@NonNull BatGuild guild, @NonNull BatUser user, @NonNull TextChannel channel, @NonNull Member member, @NonNull SlashCommandInteraction interaction) { - ScoreSaberScoreFeedProfile profile = guild.getProfile(ScoreSaberScoreFeedProfile.class); + ScoreSaberUserScoreFeedProfile profile = guild.getProfile(ScoreSaberUserScoreFeedProfile.class); OptionMapping option = interaction.getOption("user"); if (option == null){ if (profile.getTrackedUsers().isEmpty()) { diff --git a/src/main/java/cc/fascinated/bat/features/scoresaber/command/userfeed/ScoreSaberUserFeedCommand.java b/src/main/java/cc/fascinated/bat/features/scoresaber/command/userfeed/ScoreSaberUserFeedCommand.java new file mode 100644 index 0000000..b41fb8c --- /dev/null +++ b/src/main/java/cc/fascinated/bat/features/scoresaber/command/userfeed/ScoreSaberUserFeedCommand.java @@ -0,0 +1,35 @@ +package cc.fascinated.bat.features.scoresaber.command.userfeed; + +import cc.fascinated.bat.command.BatCommand; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions; +import net.dv8tion.jda.api.interactions.commands.OptionType; +import net.dv8tion.jda.api.interactions.commands.build.OptionData; +import net.dv8tion.jda.api.interactions.commands.build.SubcommandData; +import net.dv8tion.jda.internal.interactions.CommandDataImpl; +import org.springframework.stereotype.Component; + +/** + * @author Fascinated (fascinated7) + */ +@Component +public class ScoreSaberUserFeedCommand extends BatCommand { + public ScoreSaberUserFeedCommand() { + super("scoresaber-userfeed"); + super.setCategory(Category.BEAT_SABER); + + super.setDescription("ScoreSaber user score feed commands"); + super.setCommandData(new CommandDataImpl(this.getName(), this.getDescription()) + .addSubcommands(new SubcommandData("user", "Edit your ScoreSaber score feed settings") + .addOptions(new OptionData(OptionType.USER, "user", "Add or remove a user from the score feed", false)) + ).setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.MANAGE_SERVER)) + + .addSubcommands(new SubcommandData("clear-users", "Remove all users from the score feed")) + .setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.MANAGE_SERVER)) + + .addSubcommands(new SubcommandData("channel", "Edit your ScoreSaber score feed settings") + .addOptions(new OptionData(OptionType.CHANNEL, "channel", "Set the channel to send the score feed in", false)) + ).setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.MANAGE_SERVER)) + ); + } +} diff --git a/src/main/java/cc/fascinated/bat/model/guild/profiles/ScoreSaberScoreFeedProfile.java b/src/main/java/cc/fascinated/bat/model/guild/profiles/ScoreSaberUserScoreFeedProfile.java similarity index 91% rename from src/main/java/cc/fascinated/bat/model/guild/profiles/ScoreSaberScoreFeedProfile.java rename to src/main/java/cc/fascinated/bat/model/guild/profiles/ScoreSaberUserScoreFeedProfile.java index ab7c5d4..1b38861 100644 --- a/src/main/java/cc/fascinated/bat/model/guild/profiles/ScoreSaberScoreFeedProfile.java +++ b/src/main/java/cc/fascinated/bat/model/guild/profiles/ScoreSaberUserScoreFeedProfile.java @@ -13,9 +13,9 @@ import java.util.List; * @author Fascinated (fascinated7) */ @Getter @Setter -public class ScoreSaberScoreFeedProfile extends Profile { - public ScoreSaberScoreFeedProfile() { - super("scoresaber-score-feed"); +public class ScoreSaberUserScoreFeedProfile extends Profile { + public ScoreSaberUserScoreFeedProfile() { + super("scoresaber-user-score-feed"); } /** diff --git a/src/main/java/cc/fascinated/bat/service/CommandService.java b/src/main/java/cc/fascinated/bat/service/CommandService.java index 35de761..0750284 100644 --- a/src/main/java/cc/fascinated/bat/service/CommandService.java +++ b/src/main/java/cc/fascinated/bat/service/CommandService.java @@ -2,13 +2,14 @@ package cc.fascinated.bat.service; import cc.fascinated.bat.command.BatCommand; import cc.fascinated.bat.command.BatSubCommand; -import cc.fascinated.bat.command.impl.global.beatsaber.scoresaber.LinkSubCommand; -import cc.fascinated.bat.command.impl.global.beatsaber.scoresaber.ScoreSaberCommand; -import cc.fascinated.bat.command.impl.global.beatsaber.scoresaber.UserSubCommand; -import cc.fascinated.bat.command.impl.guild.beatsaber.scoresaber.ScoreFeedChannelCommand; -import cc.fascinated.bat.command.impl.guild.beatsaber.scoresaber.ScoreFeedClearUsersCommand; -import cc.fascinated.bat.command.impl.guild.beatsaber.scoresaber.ScoreFeedUserCommand; import cc.fascinated.bat.common.EmbedUtils; +import cc.fascinated.bat.features.scoresaber.command.scoresaber.LinkSubCommand; +import cc.fascinated.bat.features.scoresaber.command.scoresaber.ScoreSaberCommand; +import cc.fascinated.bat.features.scoresaber.command.scoresaber.UserSubCommand; +import cc.fascinated.bat.features.scoresaber.command.userfeed.ScoreFeedChannelCommand; +import cc.fascinated.bat.features.scoresaber.command.userfeed.ScoreFeedClearUsersCommand; +import cc.fascinated.bat.features.scoresaber.command.userfeed.ScoreFeedUserCommand; +import cc.fascinated.bat.features.scoresaber.command.userfeed.ScoreSaberUserFeedCommand; import cc.fascinated.bat.model.guild.BatGuild; import cc.fascinated.bat.model.user.BatUser; import lombok.NonNull; @@ -47,16 +48,10 @@ public class CommandService extends ListenerAdapter { */ private final UserService userService; - /** - * The application context to use - */ - private final ApplicationContext context; - @Autowired public CommandService(@NonNull GuildService guildService, @NonNull UserService userService, @NonNull ApplicationContext context) { this.guildService = guildService; this.userService = userService; - this.context = context; DiscordService.JDA.addEventListener(this); // Guild commands @@ -66,9 +61,11 @@ public class CommandService extends ListenerAdapter { registerCommand(context.getBean(ScoreSaberCommand.class) .addSubCommand("link", context.getBean(LinkSubCommand.class)) .addSubCommand("user", context.getBean(UserSubCommand.class)) - .addSubCommand("score-feed-user", context.getBean(ScoreFeedUserCommand.class)) - .addSubCommand("score-feed-channel", context.getBean(ScoreFeedChannelCommand.class)) - .addSubCommand("score-feed-clear-users", context.getBean(ScoreFeedClearUsersCommand.class)) + ); + registerCommand(context.getBean(ScoreSaberUserFeedCommand.class) + .addSubCommand("user", context.getBean(ScoreFeedUserCommand.class)) + .addSubCommand("channel", context.getBean(ScoreFeedChannelCommand.class)) + .addSubCommand("clear-users", context.getBean(ScoreFeedClearUsersCommand.class)) ); registerSlashCommands(); // Register all slash commands diff --git a/src/main/java/cc/fascinated/bat/service/EventService.java b/src/main/java/cc/fascinated/bat/service/EventService.java new file mode 100644 index 0000000..376fffa --- /dev/null +++ b/src/main/java/cc/fascinated/bat/service/EventService.java @@ -0,0 +1,40 @@ +package cc.fascinated.bat.service; + +import cc.fascinated.bat.event.EventListener; +import cc.fascinated.bat.features.scoresaber.ScoreSaberScoreFeedListener; +import lombok.NonNull; +import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Service; + +import java.util.HashSet; +import java.util.Set; + +/** + * @author Fascinated (fascinated7) + */ +@Service @Log4j2 +public class EventService { + /** + * The list of listeners registered + */ + public static final Set LISTENERS = new HashSet<>(); + + @Autowired + public EventService(@NonNull ApplicationContext context) { + registerListeners( + context.getBean(ScoreSaberScoreFeedListener.class) + ); + log.info("Registered {} listeners.", LISTENERS.size()); + } + + /** + * Registers an event listener + * + * @param listener the listener to register + */ + public void registerListeners(EventListener... listener) { + LISTENERS.addAll(Set.of(listener)); + } +} diff --git a/src/main/java/cc/fascinated/bat/service/ScoreSaberService.java b/src/main/java/cc/fascinated/bat/service/ScoreSaberService.java index 0adb9bc..6aa2fdd 100644 --- a/src/main/java/cc/fascinated/bat/service/ScoreSaberService.java +++ b/src/main/java/cc/fascinated/bat/service/ScoreSaberService.java @@ -1,20 +1,17 @@ package cc.fascinated.bat.service; import cc.fascinated.bat.BatApplication; -import cc.fascinated.bat.common.*; +import cc.fascinated.bat.common.DateUtils; +import cc.fascinated.bat.common.WebRequest; +import cc.fascinated.bat.event.EventListener; import cc.fascinated.bat.exception.BadRequestException; import cc.fascinated.bat.exception.ResourceNotFoundException; import cc.fascinated.bat.model.beatsaber.scoresaber.*; -import cc.fascinated.bat.model.guild.BatGuild; -import cc.fascinated.bat.model.guild.profiles.ScoreSaberScoreFeedProfile; import cc.fascinated.bat.model.user.profiles.ScoreSaberProfile; import com.google.gson.JsonObject; import lombok.NonNull; import lombok.SneakyThrows; import lombok.extern.log4j.Log4j2; -import net.dv8tion.jda.api.EmbedBuilder; -import net.dv8tion.jda.api.entities.Guild; -import net.dv8tion.jda.api.entities.MessageEmbed; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.socket.CloseStatus; @@ -26,19 +23,15 @@ import org.springframework.web.socket.handler.TextWebSocketHandler; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Optional; @Service @Log4j2(topic = "ScoreSaber Service") public class ScoreSaberService extends TextWebSocketHandler { - private final GuildService guildService; - 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"; @Autowired - public ScoreSaberService(@NonNull GuildService guildService) { - this.guildService = guildService; + public ScoreSaberService() { connectWebSocket(); } @@ -103,42 +96,6 @@ public class ScoreSaberService extends TextWebSocketHandler { return scores; } - /** - * Builds an embed for a score. - * - * @param score The score. - * @return The embed. - */ - public MessageEmbed buildScoreEmbed(ScoreSaberPlayerScoreToken score) { - ScoreSaberScoreToken scoreToken = score.getScore(); - ScoreSaberLeaderboardToken leaderboardToken = score.getLeaderboard(); - ScoreSaberScoreToken.LeaderboardPlayerInfo playerInfo = scoreToken.getLeaderboardPlayerInfo(); - return new EmbedBuilder() - .setAuthor(playerInfo.getName() + " just set a new score!", "https://scoresaber.com/u/%s".formatted(playerInfo.getId()), - "https://cdn.scoresaber.com/avatars/%s.jpg".formatted(playerInfo.getId())) - .setDescription("**%s** (%s%s)\n[[Map Link]](%s) [[SS Profile]](%s)".formatted( - leaderboardToken.getSongName(), - ScoreSaberUtils.getFormattedDifficulty(leaderboardToken.getDifficulty().getDifficulty()), - leaderboardToken.isRanked() ? " " + leaderboardToken.getStars() + "⭐" : "", - "https://scoresaber.com/leaderboard/%s".formatted(leaderboardToken.getId()), - "https://scoresaber.com/u/%s".formatted(playerInfo.getId()) - )) - .addField("Accuracy", "%s%%".formatted( - leaderboardToken.getMaxScore() == 0 ? "N/A" : NumberUtils.formatNumberCommas(((double) scoreToken.getBaseScore() / leaderboardToken.getMaxScore()) * 100) - ), true) - .addField("Raw PP", scoreToken.getPp() == 0 ? "Unranked" : NumberUtils.formatNumberCommas(scoreToken.getPp()), true) - .addField("Global Rank", "#%s".formatted(NumberUtils.formatNumberCommas(scoreToken.getRank())), true) - .addField("Misses", "%s".formatted(scoreToken.getMissedNotes()), true) - .addField("Bad Cuts", "%s".formatted(scoreToken.getBadCuts()), true) - .addField("Max Combo", "%s %s".formatted( - scoreToken.getMaxCombo(), - scoreToken.getMaxCombo() == leaderboardToken.getMaxScore() ? "(FC)" : "" - ), true) - .setColor(Colors.DEFAULT) - .setTimestamp(DateUtils.getDateFromString(scoreToken.getTimeSet()).toInstant()) - .build(); - } - /** * Connects to the ScoreSaber WebSocket. */ @@ -175,24 +132,8 @@ public class ScoreSaberService extends TextWebSocketHandler { ScoreSaberPlayerScoreToken score = BatApplication.GSON.fromJson(data, ScoreSaberPlayerScoreToken.class); ScoreSaberScoreToken.LeaderboardPlayerInfo playerInfo = score.getScore().getLeaderboardPlayerInfo(); - for (Guild guild : DiscordService.JDA.getGuilds()) { - BatGuild batGuild = guildService.getGuild(guild.getId()); - if (batGuild == null) { - continue; - } - - ScoreSaberScoreFeedProfile profile = batGuild.getProfile(ScoreSaberScoreFeedProfile.class); - if (profile == null) { - continue; - } - if (profile.getChannelId() == null) { - continue; - } - if (!profile.getTrackedUsers().contains(playerInfo.getId())) { - continue; - } - - profile.getAsTextChannel().sendMessageEmbeds(this.buildScoreEmbed(score)).queue(); + for (EventListener listener : EventService.LISTENERS) { + listener.onScoresaberScoreReceived(score, score.getLeaderboard(), playerInfo); } } } catch (Exception ex) {