add scoresaber feed command and websocket impl
All checks were successful
Deploy to Dokku / docker (ubuntu-latest) (push) Successful in 53s

This commit is contained in:
Lee
2024-06-24 17:42:57 +01:00
parent 39cdab27ce
commit c0ae0fc596
19 changed files with 570 additions and 81 deletions

View File

@ -1,11 +1,10 @@
package cc.fascinated.bat.command;
import cc.fascinated.bat.model.BatGuild;
import cc.fascinated.bat.model.guild.BatGuild;
import cc.fascinated.bat.model.user.BatUser;
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.OptionMapping;
import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction;
/**
@ -21,14 +20,12 @@ public interface BatCommandExecutor {
* @param channel the channel the command was executed in
* @param member the member that executed the command
* @param interaction the slash command interaction
* @param option the option that was used in the command, or null if no option was used
*/
default void execute(
@NonNull BatGuild guild,
@NonNull BatUser user,
@NonNull TextChannel channel,
@NonNull Member member,
@NonNull SlashCommandInteraction interaction,
OptionMapping option
@NonNull SlashCommandInteraction interaction
) {}
}

View File

@ -2,7 +2,7 @@ package cc.fascinated.bat.command.impl.global.beatsaber.scoresaber;
import cc.fascinated.bat.command.BatSubCommand;
import cc.fascinated.bat.common.EmbedUtils;
import cc.fascinated.bat.model.BatGuild;
import cc.fascinated.bat.model.guild.BatGuild;
import cc.fascinated.bat.model.beatsaber.scoresaber.ScoreSaberAccountToken;
import cc.fascinated.bat.model.user.BatUser;
import cc.fascinated.bat.model.user.profiles.ScoreSaberProfile;
@ -31,7 +31,8 @@ public class LinkSubCommand extends BatSubCommand {
}
@Override
public void execute(@NonNull BatGuild guild, @NonNull BatUser user, @NonNull TextChannel channel, @NonNull Member member, @NonNull SlashCommandInteraction interaction, OptionMapping option) {
public void execute(@NonNull BatGuild guild, @NonNull BatUser user, @NonNull TextChannel channel, @NonNull Member member, @NonNull SlashCommandInteraction interaction) {
OptionMapping option = interaction.getOption("link");
if (option == null) {
interaction.replyEmbeds(EmbedUtils.buildErrorEmbed("Please provide a ScoreSaber profile link").build()).queue();
return;

View File

@ -5,19 +5,21 @@ 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.BatGuild;
import cc.fascinated.bat.model.guild.BatGuild;
import cc.fascinated.bat.model.beatsaber.scoresaber.ScoreSaberAccountToken;
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.OptionMapping;
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;
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
import net.dv8tion.jda.internal.interactions.CommandDataImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@ -37,15 +39,31 @@ public class ScoreSaberCommand extends BatCommand {
super.setCategory(Category.BEAT_SABER);
this.scoreSaberService = scoreSaberService;
super.setDescription("View a user's ScoreSaber profile");
super.setDescription("General ScoreSaber commands");
super.setCommandData(new CommandDataImpl(this.getName(), this.getDescription())
.addOptions(new OptionData(OptionType.STRING, "link", "Link your ScoreSaber profile", false))
.addOptions(new OptionData(OptionType.USER, "user", "The user to view the ScoreSaber profile of", false))
.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))
);
}
@Override
public void execute(@NonNull BatGuild guild, @NonNull BatUser user, @NonNull TextChannel channel, @NonNull Member member, @NonNull SlashCommandInteraction interaction, OptionMapping option) {
public void execute(@NonNull BatGuild guild, @NonNull BatUser user, @NonNull TextChannel channel, @NonNull Member member, @NonNull SlashCommandInteraction interaction) {
sendProfileEmbed(true, user, scoreSaberService, interaction);
}

View File

@ -1,7 +1,8 @@
package cc.fascinated.bat.command.impl.global.beatsaber.scoresaber;
import cc.fascinated.bat.command.BatSubCommand;
import cc.fascinated.bat.model.BatGuild;
import cc.fascinated.bat.common.EmbedUtils;
import cc.fascinated.bat.model.guild.BatGuild;
import cc.fascinated.bat.model.user.BatUser;
import cc.fascinated.bat.service.ScoreSaberService;
import cc.fascinated.bat.service.UserService;
@ -28,13 +29,23 @@ public class UserSubCommand extends BatSubCommand {
}
@Override
public void execute(@NonNull BatGuild guild, @NonNull BatUser user, @NonNull TextChannel channel, @NonNull Member member, @NonNull SlashCommandInteraction interaction, OptionMapping option) {
BatUser target = userService.getUser(option.getAsUser().getId());
if (target == null) {
interaction.reply("User not found").queue();
public void execute(@NonNull BatGuild guild, @NonNull BatUser user, @NonNull TextChannel channel, @NonNull Member member, @NonNull SlashCommandInteraction interaction) {
OptionMapping option = interaction.getOption("user");
if (option == null) {
interaction.replyEmbeds(EmbedUtils.buildErrorEmbed("Please provide a user to view the ScoreSaber profile of").build()).queue();
return;
}
if (option.getAsUser().isBot()) {
interaction.replyEmbeds(EmbedUtils.buildErrorEmbed("You cannot view the ScoreSaber profile for a Bot").build()).queue();
return;
}
BatUser target = userService.getUser(option.getAsUser().getId());
if (target == null) {
interaction.replyEmbeds(EmbedUtils.buildErrorEmbed("Unknown user").build()).queue();
return;
}
ScoreSaberCommand.sendProfileEmbed(false, target, scoreSaberService, interaction);
}
}

View File

@ -0,0 +1,58 @@
package cc.fascinated.bat.command.impl.guild.beatsaber.scoresaber;
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.user.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
public class ScoreFeedChannelCommand extends BatSubCommand {
private final GuildService guildService;
@Autowired
public ScoreFeedChannelCommand(GuildService guildService) {
this.guildService = guildService;
}
@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);
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 feed channel to").build()).queue();
return;
}
interaction.replyEmbeds(EmbedUtils.buildSuccessEmbed("The current ScoreSaber 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 feed channel to %s"
.formatted(targetChannel.asTextChannel().getAsMention())).build()).queue();
}
}

View File

@ -0,0 +1,40 @@
package cc.fascinated.bat.command.impl.guild.beatsaber.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.guild.profiles.ScoreSaberScoreFeedProfile;
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;
/**
* @author Fascinated (fascinated7)
*/
@Component
public class ScoreFeedClearUsersCommand extends BatSubCommand {
private final GuildService guildService;
@Autowired
public ScoreFeedClearUsersCommand(GuildService guildService, UserService userService) {
this.guildService = guildService;
}
@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);
profile.getTrackedUsers().clear();
guildService.saveGuild(guild);
interaction.replyEmbeds(EmbedUtils.buildSuccessEmbed("Successfully cleared all users from the ScoreSaber feed").build()).queue();
}
}

View File

@ -0,0 +1,72 @@
package cc.fascinated.bat.command.impl.guild.beatsaber.scoresaber;
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.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;
/**
* @author Fascinated (fascinated7)
*/
@Component
public class ScoreFeedUserCommand extends BatSubCommand {
private final GuildService guildService;
private final UserService userService;
@Autowired
public ScoreFeedUserCommand(GuildService guildService, UserService userService) {
this.guildService = guildService;
this.userService = userService;
}
@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);
OptionMapping option = interaction.getOption("user");
if (option == null){
if (profile.getTrackedUsers().isEmpty()) {
interaction.replyEmbeds(EmbedUtils.buildGenericEmbed("There are no users being tracked in the ScoreSaber 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)
));
}
interaction.replyEmbeds(EmbedUtils.buildGenericEmbed("The current users being tracked in the ScoreSaber feed are:\n%s".formatted(stringBuilder.toString())).build()).queue();
return;
}
User target = option.getAsUser();
BatUser targetUser = userService.getUser(target.getId());
ScoreSaberProfile targetProfile = targetUser.getProfile(ScoreSaberProfile.class);
if (targetProfile.getId() == null) {
interaction.replyEmbeds(EmbedUtils.buildErrorEmbed("The user you are trying to track does not have a linked ScoreSaber profile").build()).queue();
return;
}
if (profile.getTrackedUsers().contains(targetProfile.getId())) {
profile.getTrackedUsers().remove(targetProfile.getId());
interaction.replyEmbeds(EmbedUtils.buildSuccessEmbed("Successfully removed %s from the ScoreSaber feed".formatted(target.getAsMention())).build()).queue();
} else {
profile.getTrackedUsers().add(targetProfile.getId());
interaction.replyEmbeds(EmbedUtils.buildSuccessEmbed("Successfully added %s to the ScoreSaber feed".formatted(target.getAsMention())).build()).queue();
}
guildService.saveGuild(guild);
}
}