add scoresaber #1 feed

This commit is contained in:
Lee 2024-06-25 11:55:26 +01:00
parent b37a30b0a3
commit e9422793cb
29 changed files with 376 additions and 60 deletions

@ -1,7 +1,7 @@
package cc.fascinated.bat.command; package cc.fascinated.bat.command;
import cc.fascinated.bat.model.guild.BatGuild; import cc.fascinated.bat.model.BatGuild;
import cc.fascinated.bat.model.user.BatUser; import cc.fascinated.bat.model.BatUser;
import lombok.NonNull; import lombok.NonNull;
import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;

@ -2,15 +2,22 @@ package cc.fascinated.bat.common;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import lombok.Setter;
import org.springframework.data.annotation.Transient; import org.springframework.data.annotation.Transient;
/** /**
* @author Fascinated (fascinated7) * @author Fascinated (fascinated7)
*/ */
@AllArgsConstructor @Getter @Getter @Setter
public class Profile { public class Profile {
/** /**
* The key of the profile. * The key of the profile.
*/ */
@Transient private final String profileKey; private String profileKey;
public Profile(String profileKey) {
this.profileKey = profileKey;
}
public Profile() {}
} }

@ -0,0 +1,102 @@
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.features.scoresaber.profiles.GuildNumberOneScoreFeedProfile;
import cc.fascinated.bat.features.scoresaber.profiles.GuildUserScoreFeedProfile;
import cc.fascinated.bat.model.BatGuild;
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.service.DiscordService;
import cc.fascinated.bat.service.GuildService;
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 net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @author Fascinated (fascinated7)
*/
@Component @Log4j2
public class NumberOneScoreFeedListener implements EventListener {
private final GuildService guildService;
@Autowired
public NumberOneScoreFeedListener(GuildService guildService) {
this.guildService = guildService;
}
@Override
public void onScoresaberScoreReceived(ScoreSaberPlayerScoreToken score, ScoreSaberLeaderboardToken leaderboard,
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;
}
for (Guild guild : DiscordService.JDA.getGuilds()) {
BatGuild batGuild = guildService.getGuild(guild.getId());
if (batGuild == null) {
continue;
}
GuildNumberOneScoreFeedProfile profile = batGuild.getProfile(GuildNumberOneScoreFeedProfile.class);
if (profile == null || profile.getChannelId() == null) {
continue;
}
TextChannel channel = profile.getAsTextChannel();
if (channel == null) {
log.error("Scoresaber user feed channel is null for guild {}, removing the stored channel.", guild.getId());
profile.setChannelId(null);
guildService.saveGuild(batGuild);
continue;
}
channel.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 #1!", "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("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();
}
}

@ -5,11 +5,11 @@ import cc.fascinated.bat.common.DateUtils;
import cc.fascinated.bat.common.NumberUtils; import cc.fascinated.bat.common.NumberUtils;
import cc.fascinated.bat.common.ScoreSaberUtils; import cc.fascinated.bat.common.ScoreSaberUtils;
import cc.fascinated.bat.event.EventListener; import cc.fascinated.bat.event.EventListener;
import cc.fascinated.bat.features.scoresaber.profiles.GuildUserScoreFeedProfile;
import cc.fascinated.bat.model.beatsaber.scoresaber.ScoreSaberLeaderboardToken; import cc.fascinated.bat.model.beatsaber.scoresaber.ScoreSaberLeaderboardToken;
import cc.fascinated.bat.model.beatsaber.scoresaber.ScoreSaberPlayerScoreToken; import cc.fascinated.bat.model.beatsaber.scoresaber.ScoreSaberPlayerScoreToken;
import cc.fascinated.bat.model.beatsaber.scoresaber.ScoreSaberScoreToken; import cc.fascinated.bat.model.beatsaber.scoresaber.ScoreSaberScoreToken;
import cc.fascinated.bat.model.guild.BatGuild; import cc.fascinated.bat.model.BatGuild;
import cc.fascinated.bat.model.guild.profiles.ScoreSaberUserScoreFeedProfile;
import cc.fascinated.bat.service.DiscordService; import cc.fascinated.bat.service.DiscordService;
import cc.fascinated.bat.service.GuildService; import cc.fascinated.bat.service.GuildService;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
@ -40,7 +40,7 @@ public class UserScoreFeedListener implements EventListener {
if (batGuild == null) { if (batGuild == null) {
continue; continue;
} }
ScoreSaberUserScoreFeedProfile profile = batGuild.getProfile(ScoreSaberUserScoreFeedProfile.class); GuildUserScoreFeedProfile profile = batGuild.getProfile(GuildUserScoreFeedProfile.class);
if (profile == null || profile.getChannelId() == null || !profile.getTrackedUsers().contains(player.getId())) { if (profile == null || profile.getChannelId() == null || !profile.getTrackedUsers().contains(player.getId())) {
continue; continue;
} }

@ -0,0 +1,59 @@
package cc.fascinated.bat.features.scoresaber.command.numberone;
import cc.fascinated.bat.command.BatSubCommand;
import cc.fascinated.bat.common.EmbedUtils;
import cc.fascinated.bat.common.TextChannelUtils;
import cc.fascinated.bat.features.scoresaber.profiles.GuildNumberOneScoreFeedProfile;
import cc.fascinated.bat.features.scoresaber.profiles.GuildUserScoreFeedProfile;
import cc.fascinated.bat.model.BatGuild;
import cc.fascinated.bat.model.BatUser;
import cc.fascinated.bat.service.GuildService;
import lombok.NonNull;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.channel.ChannelType;
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
import net.dv8tion.jda.api.entities.channel.unions.GuildChannelUnion;
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;
/**
* @author Fascinated (fascinated7)
*/
@Component("scoresaber-number-one-feed:channel.sub")
public class ChannelSubCommand extends BatSubCommand {
private final GuildService guildService;
@Autowired
public ChannelSubCommand(GuildService guildService) {
this.guildService = guildService;
}
@Override
public void execute(@NonNull BatGuild guild, @NonNull BatUser user, @NonNull TextChannel channel, @NonNull Member member, @NonNull SlashCommandInteraction interaction) {
GuildNumberOneScoreFeedProfile profile = guild.getProfile(GuildNumberOneScoreFeedProfile.class);
OptionMapping option = interaction.getOption("channel");
if (option == null) {
if (!TextChannelUtils.isValidChannel(profile.getChannelId())) {
interaction.replyEmbeds(EmbedUtils.buildErrorEmbed("Please provide a channel to set the ScoreSaber #1 feed channel to").build()).queue();
return;
}
interaction.replyEmbeds(EmbedUtils.buildSuccessEmbed("The current ScoreSaber #1 feed channel is %s"
.formatted(TextChannelUtils.getChannelMention(profile.getChannelId()))).build()).queue();
return;
}
GuildChannelUnion targetChannel = option.getAsChannel();
if (targetChannel.getType() != ChannelType.TEXT) {
interaction.replyEmbeds(EmbedUtils.buildErrorEmbed("Invalid channel type, please provide a text channel").build()).queue();
return;
}
profile.setChannelId(targetChannel.getId());
guildService.saveGuild(guild);
interaction.replyEmbeds(EmbedUtils.buildSuccessEmbed("Successfully set the ScoreSaber #1 feed channel to %s"
.formatted(targetChannel.asTextChannel().getAsMention())).build()).queue();
}
}

@ -0,0 +1,40 @@
package cc.fascinated.bat.features.scoresaber.command.numberone;
import cc.fascinated.bat.command.BatSubCommand;
import cc.fascinated.bat.common.EmbedUtils;
import cc.fascinated.bat.common.TextChannelUtils;
import cc.fascinated.bat.features.scoresaber.profiles.GuildNumberOneScoreFeedProfile;
import cc.fascinated.bat.model.BatGuild;
import cc.fascinated.bat.model.BatUser;
import cc.fascinated.bat.service.GuildService;
import lombok.NonNull;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.channel.ChannelType;
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
import net.dv8tion.jda.api.entities.channel.unions.GuildChannelUnion;
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;
/**
* @author Fascinated (fascinated7)
*/
@Component("scoresaber-number-one-feed:clear-channel.sub")
public class ClearChannelSubCommand extends BatSubCommand {
private final GuildService guildService;
@Autowired
public ClearChannelSubCommand(GuildService guildService) {
this.guildService = guildService;
}
@Override
public void execute(@NonNull BatGuild guild, @NonNull BatUser user, @NonNull TextChannel channel, @NonNull Member member, @NonNull SlashCommandInteraction interaction) {
GuildNumberOneScoreFeedProfile profile = guild.getProfile(GuildNumberOneScoreFeedProfile.class);
profile.setChannelId(null);
guildService.saveGuild(guild);
interaction.replyEmbeds(EmbedUtils.buildSuccessEmbed("Successfully cleared the ScoreSaber #1 feed channel").build()).queue();
}
}

@ -0,0 +1,35 @@
package cc.fascinated.bat.features.scoresaber.command.numberone;
import cc.fascinated.bat.command.BatCommand;
import lombok.NonNull;
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.context.ApplicationContext;
import org.springframework.stereotype.Component;
/**
* @author Fascinated (fascinated7)
*/
@Component("scoresaber-number-one-feed")
public class NunberOneFeedCommand extends BatCommand {
public NunberOneFeedCommand(@NonNull ApplicationContext context) {
super("scoresaber-number-one-feed");
super.setCategory(Category.BEAT_SABER);
super.addSubCommand("channel", context.getBean(ChannelSubCommand.class));
super.addSubCommand("clear-channel", context.getBean(ClearChannelSubCommand.class));
super.setDescription("ScoreSaber #1 score feed commands");
super.setCommandData(new CommandDataImpl(this.getName(), this.getDescription())
.addSubcommands(new SubcommandData("channel", "Edit the channel the score feed is sent in")
.addOptions(new OptionData(OptionType.CHANNEL, "channel", "Set the channel to send the score feed in", false))
).setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.MANAGE_SERVER))
.addSubcommands(new SubcommandData("clear-channel", "Disable the score feed"))
.setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.MANAGE_SERVER))
);
}
}

@ -3,9 +3,9 @@ package cc.fascinated.bat.features.scoresaber.command.scoresaber;
import cc.fascinated.bat.command.BatSubCommand; import cc.fascinated.bat.command.BatSubCommand;
import cc.fascinated.bat.common.EmbedUtils; import cc.fascinated.bat.common.EmbedUtils;
import cc.fascinated.bat.model.beatsaber.scoresaber.ScoreSaberAccountToken; import cc.fascinated.bat.model.beatsaber.scoresaber.ScoreSaberAccountToken;
import cc.fascinated.bat.model.guild.BatGuild; import cc.fascinated.bat.model.BatGuild;
import cc.fascinated.bat.model.user.BatUser; import cc.fascinated.bat.model.BatUser;
import cc.fascinated.bat.model.user.profiles.ScoreSaberProfile; import cc.fascinated.bat.features.scoresaber.profiles.UserScoreSaberProfile;
import cc.fascinated.bat.service.ScoreSaberService; import cc.fascinated.bat.service.ScoreSaberService;
import cc.fascinated.bat.service.UserService; import cc.fascinated.bat.service.UserService;
import lombok.NonNull; import lombok.NonNull;
@ -55,7 +55,7 @@ public class LinkSubCommand extends BatSubCommand {
return; return;
} }
((ScoreSaberProfile) user.getProfile(ScoreSaberProfile.class)).setId(id); ((UserScoreSaberProfile) user.getProfile(UserScoreSaberProfile.class)).setId(id);
userService.saveUser(user); userService.saveUser(user);
interaction.replyEmbeds(EmbedUtils.buildSuccessEmbed("Successfully linked your [ScoreSaber](%s) profile".formatted( interaction.replyEmbeds(EmbedUtils.buildSuccessEmbed("Successfully linked your [ScoreSaber](%s) profile".formatted(
"https://scoresaber.com/u/%s".formatted(id) "https://scoresaber.com/u/%s".formatted(id)

@ -1,8 +1,8 @@
package cc.fascinated.bat.features.scoresaber.command.scoresaber; package cc.fascinated.bat.features.scoresaber.command.scoresaber;
import cc.fascinated.bat.command.BatSubCommand; import cc.fascinated.bat.command.BatSubCommand;
import cc.fascinated.bat.model.guild.BatGuild; import cc.fascinated.bat.model.BatGuild;
import cc.fascinated.bat.model.user.BatUser; import cc.fascinated.bat.model.BatUser;
import cc.fascinated.bat.service.ScoreSaberService; import cc.fascinated.bat.service.ScoreSaberService;
import lombok.NonNull; import lombok.NonNull;
import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Member;

@ -6,9 +6,9 @@ import cc.fascinated.bat.common.DateUtils;
import cc.fascinated.bat.common.EmbedUtils; import cc.fascinated.bat.common.EmbedUtils;
import cc.fascinated.bat.common.NumberUtils; import cc.fascinated.bat.common.NumberUtils;
import cc.fascinated.bat.model.beatsaber.scoresaber.ScoreSaberAccountToken; import cc.fascinated.bat.model.beatsaber.scoresaber.ScoreSaberAccountToken;
import cc.fascinated.bat.model.guild.BatGuild; import cc.fascinated.bat.model.BatGuild;
import cc.fascinated.bat.model.user.BatUser; import cc.fascinated.bat.model.BatUser;
import cc.fascinated.bat.model.user.profiles.ScoreSaberProfile; import cc.fascinated.bat.features.scoresaber.profiles.UserScoreSaberProfile;
import cc.fascinated.bat.service.ScoreSaberService; import cc.fascinated.bat.service.ScoreSaberService;
import lombok.NonNull; import lombok.NonNull;
import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.EmbedBuilder;
@ -67,7 +67,7 @@ public class ScoreSaberCommand extends BatCommand {
* @param interaction The interaction * @param interaction The interaction
*/ */
public static void sendProfileEmbed(boolean isSelf, BatUser user, ScoreSaberService scoreSaberService, SlashCommandInteraction interaction) { public static void sendProfileEmbed(boolean isSelf, BatUser user, ScoreSaberService scoreSaberService, SlashCommandInteraction interaction) {
ScoreSaberProfile profile = user.getProfile(ScoreSaberProfile.class); UserScoreSaberProfile profile = user.getProfile(UserScoreSaberProfile.class);
if (profile.getId() == null) { if (profile.getId() == null) {
if (!isSelf) { if (!isSelf) {
interaction.replyEmbeds(EmbedUtils.buildErrorEmbed("%s does not have a linked ScoreSaber account" interaction.replyEmbeds(EmbedUtils.buildErrorEmbed("%s does not have a linked ScoreSaber account"

@ -2,8 +2,8 @@ package cc.fascinated.bat.features.scoresaber.command.scoresaber;
import cc.fascinated.bat.command.BatSubCommand; import cc.fascinated.bat.command.BatSubCommand;
import cc.fascinated.bat.common.EmbedUtils; import cc.fascinated.bat.common.EmbedUtils;
import cc.fascinated.bat.model.guild.BatGuild; import cc.fascinated.bat.model.BatGuild;
import cc.fascinated.bat.model.user.BatUser; import cc.fascinated.bat.model.BatUser;
import cc.fascinated.bat.service.ScoreSaberService; import cc.fascinated.bat.service.ScoreSaberService;
import cc.fascinated.bat.service.UserService; import cc.fascinated.bat.service.UserService;
import lombok.NonNull; import lombok.NonNull;

@ -3,9 +3,9 @@ package cc.fascinated.bat.features.scoresaber.command.userfeed;
import cc.fascinated.bat.command.BatSubCommand; import cc.fascinated.bat.command.BatSubCommand;
import cc.fascinated.bat.common.EmbedUtils; import cc.fascinated.bat.common.EmbedUtils;
import cc.fascinated.bat.common.TextChannelUtils; import cc.fascinated.bat.common.TextChannelUtils;
import cc.fascinated.bat.model.guild.BatGuild; import cc.fascinated.bat.model.BatGuild;
import cc.fascinated.bat.model.guild.profiles.ScoreSaberUserScoreFeedProfile; import cc.fascinated.bat.features.scoresaber.profiles.GuildUserScoreFeedProfile;
import cc.fascinated.bat.model.user.BatUser; import cc.fascinated.bat.model.BatUser;
import cc.fascinated.bat.service.GuildService; import cc.fascinated.bat.service.GuildService;
import lombok.NonNull; import lombok.NonNull;
import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Member;
@ -31,7 +31,7 @@ public class ChannelSubCommand extends BatSubCommand {
@Override @Override
public void execute(@NonNull BatGuild guild, @NonNull BatUser user, @NonNull TextChannel channel, @NonNull Member member, @NonNull SlashCommandInteraction interaction) { public void execute(@NonNull BatGuild guild, @NonNull BatUser user, @NonNull TextChannel channel, @NonNull Member member, @NonNull SlashCommandInteraction interaction) {
ScoreSaberUserScoreFeedProfile profile = guild.getProfile(ScoreSaberUserScoreFeedProfile.class); GuildUserScoreFeedProfile profile = guild.getProfile(GuildUserScoreFeedProfile.class);
OptionMapping option = interaction.getOption("channel"); OptionMapping option = interaction.getOption("channel");
if (option == null) { if (option == null) {
if (!TextChannelUtils.isValidChannel(profile.getChannelId())) { if (!TextChannelUtils.isValidChannel(profile.getChannelId())) {

@ -0,0 +1,37 @@
package cc.fascinated.bat.features.scoresaber.command.userfeed;
import cc.fascinated.bat.command.BatSubCommand;
import cc.fascinated.bat.common.EmbedUtils;
import cc.fascinated.bat.features.scoresaber.profiles.GuildNumberOneScoreFeedProfile;
import cc.fascinated.bat.features.scoresaber.profiles.GuildUserScoreFeedProfile;
import cc.fascinated.bat.model.BatGuild;
import cc.fascinated.bat.model.BatUser;
import cc.fascinated.bat.service.GuildService;
import lombok.NonNull;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
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-userfeed:clear-channel.sub")
public class ClearChannelSubCommand extends BatSubCommand {
private final GuildService guildService;
@Autowired
public ClearChannelSubCommand(GuildService guildService) {
this.guildService = guildService;
}
@Override
public void execute(@NonNull BatGuild guild, @NonNull BatUser user, @NonNull TextChannel channel, @NonNull Member member, @NonNull SlashCommandInteraction interaction) {
GuildUserScoreFeedProfile profile = guild.getProfile(GuildUserScoreFeedProfile.class);
profile.setChannelId(null);
guildService.saveGuild(guild);
interaction.replyEmbeds(EmbedUtils.buildSuccessEmbed("Successfully cleared the ScoreSaber feed channel").build()).queue();
}
}

@ -2,9 +2,9 @@ package cc.fascinated.bat.features.scoresaber.command.userfeed;
import cc.fascinated.bat.command.BatSubCommand; import cc.fascinated.bat.command.BatSubCommand;
import cc.fascinated.bat.common.EmbedUtils; import cc.fascinated.bat.common.EmbedUtils;
import cc.fascinated.bat.model.guild.BatGuild; import cc.fascinated.bat.model.BatGuild;
import cc.fascinated.bat.model.guild.profiles.ScoreSaberUserScoreFeedProfile; import cc.fascinated.bat.features.scoresaber.profiles.GuildUserScoreFeedProfile;
import cc.fascinated.bat.model.user.BatUser; import cc.fascinated.bat.model.BatUser;
import cc.fascinated.bat.service.GuildService; import cc.fascinated.bat.service.GuildService;
import cc.fascinated.bat.service.UserService; import cc.fascinated.bat.service.UserService;
import lombok.NonNull; import lombok.NonNull;
@ -28,7 +28,7 @@ public class ClearUsersSubCommand extends BatSubCommand {
@Override @Override
public void execute(@NonNull BatGuild guild, @NonNull BatUser user, @NonNull TextChannel channel, @NonNull Member member, @NonNull SlashCommandInteraction interaction) { public void execute(@NonNull BatGuild guild, @NonNull BatUser user, @NonNull TextChannel channel, @NonNull Member member, @NonNull SlashCommandInteraction interaction) {
ScoreSaberUserScoreFeedProfile profile = guild.getProfile(ScoreSaberUserScoreFeedProfile.class); GuildUserScoreFeedProfile profile = guild.getProfile(GuildUserScoreFeedProfile.class);
profile.getTrackedUsers().clear(); profile.getTrackedUsers().clear();
guildService.saveGuild(guild); guildService.saveGuild(guild);

@ -17,25 +17,29 @@ import org.springframework.stereotype.Component;
@Component("scoresaber-userfeed:feed.sub") @Component("scoresaber-userfeed:feed.sub")
public class UserFeedCommand extends BatCommand { public class UserFeedCommand extends BatCommand {
public UserFeedCommand(@NonNull ApplicationContext context) { public UserFeedCommand(@NonNull ApplicationContext context) {
super("scoresaber-userfeed"); super("scoresaber-user-feed");
super.setCategory(Category.BEAT_SABER); super.setCategory(Category.BEAT_SABER);
super.addSubCommand("user", context.getBean(UserSubCommand.class)); super.addSubCommand("user", context.getBean(UserSubCommand.class));
super.addSubCommand("channel", context.getBean(ChannelSubCommand.class)); super.addSubCommand("channel", context.getBean(ChannelSubCommand.class));
super.addSubCommand("clear-channel", context.getBean(ClearChannelSubCommand.class));
super.addSubCommand("clear-users", context.getBean(ClearUsersSubCommand.class)); super.addSubCommand("clear-users", context.getBean(ClearUsersSubCommand.class));
super.setDescription("ScoreSaber user score feed commands"); super.setDescription("ScoreSaber user score feed commands");
super.setCommandData(new CommandDataImpl(this.getName(), this.getDescription()) super.setCommandData(new CommandDataImpl(this.getName(), this.getDescription())
.addSubcommands(new SubcommandData("user", "Edit your ScoreSaber score feed settings") .addSubcommands(new SubcommandData("user", "Edit the users in the score feed")
.addOptions(new OptionData(OptionType.USER, "user", "Add or remove a user from the score feed", false)) .addOptions(new OptionData(OptionType.USER, "user", "Add or remove a user from the score feed", false))
).setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.MANAGE_SERVER)) ).setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.MANAGE_SERVER))
.addSubcommands(new SubcommandData("clear-users", "Remove all users from the score feed")) .addSubcommands(new SubcommandData("clear-users", "Remove all users from the score feed"))
.setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.MANAGE_SERVER)) .setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.MANAGE_SERVER))
.addSubcommands(new SubcommandData("channel", "Edit your ScoreSaber score feed settings") .addSubcommands(new SubcommandData("channel", "Edit the channel the score feed is sent in")
.addOptions(new OptionData(OptionType.CHANNEL, "channel", "Set the channel to send the score feed in", false)) .addOptions(new OptionData(OptionType.CHANNEL, "channel", "Set the channel to send the score feed in", false))
).setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.MANAGE_SERVER)) ).setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.MANAGE_SERVER))
.addSubcommands(new SubcommandData("clear-channel", "Disable the score feed"))
.setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.MANAGE_SERVER))
); );
} }
} }

@ -2,10 +2,10 @@ package cc.fascinated.bat.features.scoresaber.command.userfeed;
import cc.fascinated.bat.command.BatSubCommand; import cc.fascinated.bat.command.BatSubCommand;
import cc.fascinated.bat.common.EmbedUtils; import cc.fascinated.bat.common.EmbedUtils;
import cc.fascinated.bat.model.guild.BatGuild; import cc.fascinated.bat.model.BatGuild;
import cc.fascinated.bat.model.guild.profiles.ScoreSaberUserScoreFeedProfile; import cc.fascinated.bat.features.scoresaber.profiles.GuildUserScoreFeedProfile;
import cc.fascinated.bat.model.user.BatUser; import cc.fascinated.bat.model.BatUser;
import cc.fascinated.bat.model.user.profiles.ScoreSaberProfile; import cc.fascinated.bat.features.scoresaber.profiles.UserScoreSaberProfile;
import cc.fascinated.bat.service.GuildService; import cc.fascinated.bat.service.GuildService;
import cc.fascinated.bat.service.UserService; import cc.fascinated.bat.service.UserService;
import lombok.NonNull; import lombok.NonNull;
@ -33,7 +33,7 @@ public class UserSubCommand extends BatSubCommand {
@Override @Override
public void execute(@NonNull BatGuild guild, @NonNull BatUser user, @NonNull TextChannel channel, @NonNull Member member, @NonNull SlashCommandInteraction interaction) { public void execute(@NonNull BatGuild guild, @NonNull BatUser user, @NonNull TextChannel channel, @NonNull Member member, @NonNull SlashCommandInteraction interaction) {
ScoreSaberUserScoreFeedProfile profile = guild.getProfile(ScoreSaberUserScoreFeedProfile.class); GuildUserScoreFeedProfile profile = guild.getProfile(GuildUserScoreFeedProfile.class);
OptionMapping option = interaction.getOption("user"); OptionMapping option = interaction.getOption("user");
if (option == null){ if (option == null){
if (profile.getTrackedUsers().isEmpty()) { if (profile.getTrackedUsers().isEmpty()) {
@ -53,7 +53,7 @@ public class UserSubCommand extends BatSubCommand {
User target = option.getAsUser(); User target = option.getAsUser();
BatUser targetUser = userService.getUser(target.getId()); BatUser targetUser = userService.getUser(target.getId());
ScoreSaberProfile targetProfile = targetUser.getProfile(ScoreSaberProfile.class); UserScoreSaberProfile targetProfile = targetUser.getProfile(UserScoreSaberProfile.class);
if (targetProfile.getId() == null) { if (targetProfile.getId() == null) {
interaction.replyEmbeds(EmbedUtils.buildErrorEmbed("The user you are trying to track does not have a linked ScoreSaber profile").build()).queue(); interaction.replyEmbeds(EmbedUtils.buildErrorEmbed("The user you are trying to track does not have a linked ScoreSaber profile").build()).queue();
return; return;

@ -0,0 +1,34 @@
package cc.fascinated.bat.features.scoresaber.profiles;
import cc.fascinated.bat.common.Profile;
import cc.fascinated.bat.service.DiscordService;
import lombok.Getter;
import lombok.Setter;
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
import java.util.ArrayList;
import java.util.List;
/**
* @author Fascinated (fascinated7)
*/
@Getter @Setter
public class GuildNumberOneScoreFeedProfile extends Profile {
public GuildNumberOneScoreFeedProfile() {
super("scoresaber-number-one-score-feed");
}
/**
* The channel ID of the score feed
*/
private String channelId;
/**
* Gets the channel as a TextChannel
*
* @return the channel as a TextChannel
*/
public TextChannel getAsTextChannel() {
return DiscordService.JDA.getTextChannelById(channelId);
}
}

@ -1,4 +1,4 @@
package cc.fascinated.bat.model.guild.profiles; package cc.fascinated.bat.features.scoresaber.profiles;
import cc.fascinated.bat.common.Profile; import cc.fascinated.bat.common.Profile;
import cc.fascinated.bat.service.DiscordService; import cc.fascinated.bat.service.DiscordService;
@ -13,8 +13,8 @@ import java.util.List;
* @author Fascinated (fascinated7) * @author Fascinated (fascinated7)
*/ */
@Getter @Setter @Getter @Setter
public class ScoreSaberUserScoreFeedProfile extends Profile { public class GuildUserScoreFeedProfile extends Profile {
public ScoreSaberUserScoreFeedProfile() { public GuildUserScoreFeedProfile() {
super("scoresaber-user-score-feed"); super("scoresaber-user-score-feed");
} }

@ -1,4 +1,4 @@
package cc.fascinated.bat.model.user.profiles; package cc.fascinated.bat.features.scoresaber.profiles;
import cc.fascinated.bat.common.Profile; import cc.fascinated.bat.common.Profile;
import lombok.Getter; import lombok.Getter;
@ -8,13 +8,13 @@ import lombok.Setter;
* @author Fascinated (fascinated7) * @author Fascinated (fascinated7)
*/ */
@Setter @Getter @Setter @Getter
public class ScoreSaberProfile extends Profile { public class UserScoreSaberProfile extends Profile {
/** /**
* The Account ID of the ScoreSaber profile * The Account ID of the ScoreSaber profile
*/ */
private String id; private String id;
public ScoreSaberProfile() { public UserScoreSaberProfile() {
super("scoresaber"); super("scoresaber");
} }
} }

@ -1,4 +1,4 @@
package cc.fascinated.bat.model.guild; package cc.fascinated.bat.model;
import cc.fascinated.bat.common.Profile; import cc.fascinated.bat.common.Profile;
import cc.fascinated.bat.service.DiscordService; import cc.fascinated.bat.service.DiscordService;

@ -1,4 +1,4 @@
package cc.fascinated.bat.model.user; package cc.fascinated.bat.model;
import cc.fascinated.bat.common.Profile; import cc.fascinated.bat.common.Profile;
import cc.fascinated.bat.service.DiscordService; import cc.fascinated.bat.service.DiscordService;

@ -1,6 +1,6 @@
package cc.fascinated.bat.repository; package cc.fascinated.bat.repository;
import cc.fascinated.bat.model.guild.BatGuild; import cc.fascinated.bat.model.BatGuild;
import org.springframework.data.mongodb.repository.MongoRepository; import org.springframework.data.mongodb.repository.MongoRepository;
/** /**

@ -1,6 +1,6 @@
package cc.fascinated.bat.repository; package cc.fascinated.bat.repository;
import cc.fascinated.bat.model.user.BatUser; import cc.fascinated.bat.model.BatUser;
import org.springframework.data.mongodb.repository.MongoRepository; import org.springframework.data.mongodb.repository.MongoRepository;
/** /**

@ -3,15 +3,11 @@ package cc.fascinated.bat.service;
import cc.fascinated.bat.command.BatCommand; import cc.fascinated.bat.command.BatCommand;
import cc.fascinated.bat.command.BatSubCommand; import cc.fascinated.bat.command.BatSubCommand;
import cc.fascinated.bat.common.EmbedUtils; import cc.fascinated.bat.common.EmbedUtils;
import cc.fascinated.bat.features.scoresaber.command.scoresaber.LinkSubCommand; import cc.fascinated.bat.features.scoresaber.command.numberone.NunberOneFeedCommand;
import cc.fascinated.bat.features.scoresaber.command.scoresaber.MeSubCommand;
import cc.fascinated.bat.features.scoresaber.command.scoresaber.ScoreSaberCommand; import cc.fascinated.bat.features.scoresaber.command.scoresaber.ScoreSaberCommand;
import cc.fascinated.bat.features.scoresaber.command.userfeed.ChannelSubCommand;
import cc.fascinated.bat.features.scoresaber.command.userfeed.ClearUsersSubCommand;
import cc.fascinated.bat.features.scoresaber.command.userfeed.UserFeedCommand; import cc.fascinated.bat.features.scoresaber.command.userfeed.UserFeedCommand;
import cc.fascinated.bat.features.scoresaber.command.userfeed.UserSubCommand; import cc.fascinated.bat.model.BatGuild;
import cc.fascinated.bat.model.guild.BatGuild; import cc.fascinated.bat.model.BatUser;
import cc.fascinated.bat.model.user.BatUser;
import lombok.NonNull; import lombok.NonNull;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.JDA;
@ -56,6 +52,7 @@ public class CommandService extends ListenerAdapter {
// Guild commands // Guild commands
registerCommand(context.getBean(UserFeedCommand.class)); registerCommand(context.getBean(UserFeedCommand.class));
registerCommand(context.getBean(NunberOneFeedCommand.class));
// Global commands // Global commands
registerCommand(context.getBean(ScoreSaberCommand.class)); registerCommand(context.getBean(ScoreSaberCommand.class));

@ -36,7 +36,6 @@ public class DiscordService {
TimerUtils.scheduleRepeating(this::updateActivity, 0, 1000 * 60 * 5); TimerUtils.scheduleRepeating(this::updateActivity, 0, 1000 * 60 * 5);
} }
/** /**
* Updates the activity of the bot * Updates the activity of the bot
*/ */

@ -1,6 +1,7 @@
package cc.fascinated.bat.service; package cc.fascinated.bat.service;
import cc.fascinated.bat.event.EventListener; import cc.fascinated.bat.event.EventListener;
import cc.fascinated.bat.features.scoresaber.NumberOneScoreFeedListener;
import cc.fascinated.bat.features.scoresaber.UserScoreFeedListener; import cc.fascinated.bat.features.scoresaber.UserScoreFeedListener;
import lombok.NonNull; import lombok.NonNull;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
@ -24,7 +25,8 @@ public class EventService {
@Autowired @Autowired
public EventService(@NonNull ApplicationContext context) { public EventService(@NonNull ApplicationContext context) {
registerListeners( registerListeners(
context.getBean(UserScoreFeedListener.class) context.getBean(UserScoreFeedListener.class),
context.getBean(NumberOneScoreFeedListener.class)
); );
log.info("Registered {} listeners.", LISTENERS.size()); log.info("Registered {} listeners.", LISTENERS.size());
} }

@ -1,6 +1,6 @@
package cc.fascinated.bat.service; package cc.fascinated.bat.service;
import cc.fascinated.bat.model.guild.BatGuild; import cc.fascinated.bat.model.BatGuild;
import cc.fascinated.bat.repository.GuildRepository; import cc.fascinated.bat.repository.GuildRepository;
import lombok.NonNull; import lombok.NonNull;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;

@ -7,7 +7,7 @@ import cc.fascinated.bat.event.EventListener;
import cc.fascinated.bat.exception.BadRequestException; import cc.fascinated.bat.exception.BadRequestException;
import cc.fascinated.bat.exception.ResourceNotFoundException; import cc.fascinated.bat.exception.ResourceNotFoundException;
import cc.fascinated.bat.model.beatsaber.scoresaber.*; import cc.fascinated.bat.model.beatsaber.scoresaber.*;
import cc.fascinated.bat.model.user.profiles.ScoreSaberProfile; import cc.fascinated.bat.features.scoresaber.profiles.UserScoreSaberProfile;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import lombok.NonNull; import lombok.NonNull;
import lombok.SneakyThrows; import lombok.SneakyThrows;
@ -65,7 +65,7 @@ public class ScoreSaberService extends TextWebSocketHandler {
* @param page The page to get the scores from. * @param page The page to get the scores from.
* @return The scores. * @return The scores.
*/ */
public ScoreSaberScoresPageToken getPageScores(ScoreSaberProfile profile, int page) { public ScoreSaberScoresPageToken getPageScores(UserScoreSaberProfile profile, int page) {
log.info("Fetching scores for account '{}' from page {}.", profile.getId(), page); log.info("Fetching scores for account '{}' from page {}.", profile.getId(), page);
ScoreSaberScoresPageToken pageToken = WebRequest.getAsEntity(String.format(GET_PLAYER_SCORES_ENDPOINT, profile.getId(), "recent", page), ScoreSaberScoresPageToken.class); ScoreSaberScoresPageToken pageToken = WebRequest.getAsEntity(String.format(GET_PLAYER_SCORES_ENDPOINT, profile.getId(), "recent", page), ScoreSaberScoresPageToken.class);
if (pageToken == null) { // Check if the page doesn't exist. if (pageToken == null) { // Check if the page doesn't exist.
@ -84,7 +84,7 @@ public class ScoreSaberService extends TextWebSocketHandler {
* @param profile The profile. * @param profile The profile.
* @return The scores. * @return The scores.
*/ */
public List<ScoreSaberScoresPageToken> getScores(ScoreSaberProfile profile) { public List<ScoreSaberScoresPageToken> getScores(UserScoreSaberProfile profile) {
List<ScoreSaberScoresPageToken> scores = new ArrayList<>(List.of(getPageScores(profile, 1))); List<ScoreSaberScoresPageToken> scores = new ArrayList<>(List.of(getPageScores(profile, 1)));
ScoreSaberPageMetadataToken metadata = scores.get(0).getMetadata(); ScoreSaberPageMetadataToken metadata = scores.get(0).getMetadata();
int totalPages = (int) Math.ceil((double) metadata.getTotal() / metadata.getItemsPerPage()); int totalPages = (int) Math.ceil((double) metadata.getTotal() / metadata.getItemsPerPage());

@ -1,6 +1,6 @@
package cc.fascinated.bat.service; package cc.fascinated.bat.service;
import cc.fascinated.bat.model.user.BatUser; import cc.fascinated.bat.model.BatUser;
import cc.fascinated.bat.repository.UserRepository; import cc.fascinated.bat.repository.UserRepository;
import lombok.NonNull; import lombok.NonNull;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;