forked from Fascinated/Bat
Compare commits
No commits in common. "7f58a84acf7df87aff8ae7ad1fba33acec8ea53b" and "3af151d89a8dc28c617f9f6c0f7cb2e9c80102c1" have entirely different histories.
7f58a84acf
...
3af151d89a
@ -19,9 +19,5 @@ WORKDIR /home/container
|
|||||||
# Copy the built jar file from the builder stage
|
# Copy the built jar file from the builder stage
|
||||||
COPY --from=builder /home/container/target/Bat.jar .
|
COPY --from=builder /home/container/target/Bat.jar .
|
||||||
|
|
||||||
# Export the port
|
|
||||||
ENV PORT=8080
|
|
||||||
EXPOSE $PORT
|
|
||||||
|
|
||||||
# Run the jar file
|
# Run the jar file
|
||||||
CMD java -jar Bat.jar -Djava.awt.headless=true
|
CMD java -jar Bat.jar -Djava.awt.headless=true
|
5
pom.xml
5
pom.xml
@ -115,11 +115,6 @@
|
|||||||
<artifactId>expiringmap</artifactId>
|
<artifactId>expiringmap</artifactId>
|
||||||
<version>0.5.11</version>
|
<version>0.5.11</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>se.michaelthelin.spotify</groupId>
|
|
||||||
<artifactId>spotify-web-api-java</artifactId>
|
|
||||||
<version>8.4.0</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- Test Dependencies -->
|
<!-- Test Dependencies -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -18,22 +18,22 @@ import java.util.Objects;
|
|||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@Log4j2(topic = "Bat")
|
@Log4j2(topic = "Bat")
|
||||||
public class BatApplication {
|
public class BatApplication {
|
||||||
public static Gson GSON = new GsonBuilder().create();
|
public static Gson GSON = new GsonBuilder().create();
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public static void main(@NonNull String[] args) {
|
public static void main(@NonNull String[] args) {
|
||||||
// Handle loading of our configuration file
|
// Handle loading of our configuration file
|
||||||
File config = new File("application.yml");
|
File config = new File("application.yml");
|
||||||
if (!config.exists()) { // Saving the default config if it doesn't exist locally
|
if (!config.exists()) { // Saving the default config if it doesn't exist locally
|
||||||
Files.copy(Objects.requireNonNull(BatApplication.class.getResourceAsStream("/application.yml")), config.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
Files.copy(Objects.requireNonNull(BatApplication.class.getResourceAsStream("/application.yml")), config.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||||
log.info("Saved the default configuration to '{}', please re-launch the application", // Log the default config being saved
|
log.info("Saved the default configuration to '{}', please re-launch the application", // Log the default config being saved
|
||||||
config.getAbsolutePath()
|
config.getAbsolutePath()
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
log.info("Found configuration at '{}'", config.getAbsolutePath()); // Log the found config
|
log.info("Found configuration at '{}'", config.getAbsolutePath()); // Log the found config
|
||||||
|
|
||||||
// Start the app
|
// Start the app
|
||||||
SpringApplication.run(BatApplication.class, args);
|
SpringApplication.run(BatApplication.class, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -15,8 +15,7 @@ import java.util.Map;
|
|||||||
/**
|
/**
|
||||||
* @author Fascinated (fascinated7)
|
* @author Fascinated (fascinated7)
|
||||||
*/
|
*/
|
||||||
@Getter
|
@Getter @Setter
|
||||||
@Setter
|
|
||||||
public abstract class BatCommand implements BatCommandExecutor {
|
public abstract class BatCommand implements BatCommandExecutor {
|
||||||
/**
|
/**
|
||||||
* The information about the command
|
* The information about the command
|
||||||
@ -70,10 +69,10 @@ public abstract class BatCommand implements BatCommandExecutor {
|
|||||||
/**
|
/**
|
||||||
* Adds an option to the sub command
|
* Adds an option to the sub command
|
||||||
*
|
*
|
||||||
* @param optionType the type of the option
|
* @param optionType the type of the option
|
||||||
* @param name the name of the option
|
* @param name the name of the option
|
||||||
* @param description the description of the option
|
* @param description the description of the option
|
||||||
* @param required whether the option is required
|
* @param required whether the option is required
|
||||||
*/
|
*/
|
||||||
protected void addOption(OptionType optionType, String name, String description, boolean required) {
|
protected void addOption(OptionType optionType, String name, String description, boolean required) {
|
||||||
this.commandData.addOption(optionType, name, description, required);
|
this.commandData.addOption(optionType, name, description, required);
|
||||||
|
@ -14,10 +14,10 @@ public interface BatCommandExecutor {
|
|||||||
/**
|
/**
|
||||||
* Executes the command using a slash command interaction.
|
* Executes the command using a slash command interaction.
|
||||||
*
|
*
|
||||||
* @param guild the bat guild the command was executed in (null if the command was executed in a DM)
|
* @param guild the bat guild the command was executed in (null if the command was executed in a DM)
|
||||||
* @param user the bat user that executed the command
|
* @param user the bat user that executed the command
|
||||||
* @param channel the channel the command was executed in
|
* @param channel the channel the command was executed in
|
||||||
* @param member the member that executed the command
|
* @param member the member that executed the command
|
||||||
* @param interaction the slash command interaction
|
* @param interaction the slash command interaction
|
||||||
*/
|
*/
|
||||||
default void execute(
|
default void execute(
|
||||||
@ -26,6 +26,5 @@ public interface BatCommandExecutor {
|
|||||||
@NonNull MessageChannel channel,
|
@NonNull MessageChannel channel,
|
||||||
Member member,
|
Member member,
|
||||||
@NonNull SlashCommandInteraction interaction
|
@NonNull SlashCommandInteraction interaction
|
||||||
) {
|
) {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -8,8 +8,7 @@ import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
|
|||||||
/**
|
/**
|
||||||
* @author Fascinated (fascinated7)
|
* @author Fascinated (fascinated7)
|
||||||
*/
|
*/
|
||||||
@Getter
|
@Getter @Setter
|
||||||
@Setter
|
|
||||||
public class BatSubCommand implements BatCommandExecutor {
|
public class BatSubCommand implements BatCommandExecutor {
|
||||||
/**
|
/**
|
||||||
* The information about the sub command
|
* The information about the sub command
|
||||||
@ -34,10 +33,10 @@ public class BatSubCommand implements BatCommandExecutor {
|
|||||||
/**
|
/**
|
||||||
* Adds an option to the sub command
|
* Adds an option to the sub command
|
||||||
*
|
*
|
||||||
* @param optionType the type of the option
|
* @param optionType the type of the option
|
||||||
* @param name the name of the option
|
* @param name the name of the option
|
||||||
* @param description the description of the option
|
* @param description the description of the option
|
||||||
* @param required whether the option is required
|
* @param required whether the option is required
|
||||||
*/
|
*/
|
||||||
public void addOption(OptionType optionType, String name, String description, boolean required) {
|
public void addOption(OptionType optionType, String name, String description, boolean required) {
|
||||||
this.commandData.addOption(optionType, name, description, required);
|
this.commandData.addOption(optionType, name, description, required);
|
||||||
|
@ -10,14 +10,12 @@ import java.util.List;
|
|||||||
/**
|
/**
|
||||||
* @author Fascinated (fascinated7)
|
* @author Fascinated (fascinated7)
|
||||||
*/
|
*/
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor @Getter
|
||||||
@Getter
|
|
||||||
public enum Category {
|
public enum Category {
|
||||||
GENERAL(Emoji.fromUnicode("U+2699"), "General", false),
|
GENERAL(Emoji.fromUnicode("U+2699"), "General", false),
|
||||||
FUN(Emoji.fromFormatted("U+1F973"), "Fun", false),
|
FUN(Emoji.fromFormatted("U+1F973"), "Fun", false),
|
||||||
SERVER(Emoji.fromFormatted("U+1F5A5"), "Server", false),
|
SERVER(Emoji.fromFormatted("U+1F5A5"), "Server", false),
|
||||||
UTILITY(Emoji.fromFormatted("U+1F6E0"), "Utility", false),
|
UTILITY(Emoji.fromFormatted("U+1F6E0"), "Utility", false),
|
||||||
MUSIC(Emoji.fromFormatted("U+1F3B5"), "Music", false),
|
|
||||||
BEAT_SABER(Emoji.fromFormatted("U+1FA84"), "Beat Saber", false),
|
BEAT_SABER(Emoji.fromFormatted("U+1FA84"), "Beat Saber", false),
|
||||||
BOT_ADMIN(null, null, true);
|
BOT_ADMIN(null, null, true);
|
||||||
|
|
||||||
|
@ -26,9 +26,9 @@ import java.lang.management.RuntimeMXBean;
|
|||||||
@Component
|
@Component
|
||||||
@CommandInfo(name = "botstats", description = "Shows the bot statistics", guildOnly = false)
|
@CommandInfo(name = "botstats", description = "Shows the bot statistics", guildOnly = false)
|
||||||
public class BotStatsCommand extends BatCommand {
|
public class BotStatsCommand extends BatCommand {
|
||||||
|
RuntimeMXBean bean = ManagementFactory.getRuntimeMXBean();
|
||||||
private final GuildService guildService;
|
private final GuildService guildService;
|
||||||
private final UserService userService;
|
private final UserService userService;
|
||||||
private final RuntimeMXBean bean = ManagementFactory.getRuntimeMXBean();
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public BotStatsCommand(@NonNull GuildService guildService, @NonNull UserService userService) {
|
public BotStatsCommand(@NonNull GuildService guildService, @NonNull UserService userService) {
|
||||||
@ -42,14 +42,14 @@ public class BotStatsCommand extends BatCommand {
|
|||||||
|
|
||||||
interaction.replyEmbeds(EmbedUtils.genericEmbed().setDescription(
|
interaction.replyEmbeds(EmbedUtils.genericEmbed().setDescription(
|
||||||
"**Bot Statistics**\n" +
|
"**Bot Statistics**\n" +
|
||||||
"➜ Guilds: **%s\n".formatted(jda.getGuilds().size()) +
|
"➜ Guilds: %s\n".formatted(jda.getGuilds().size()) +
|
||||||
"➜ Users: **%s\n".formatted(jda.getUsers().size()) +
|
"➜ Users: %s\n".formatted(jda.getUsers().size()) +
|
||||||
"➜ Gateway Ping: **%sms**\n".formatted(jda.getGatewayPing()) +
|
"➜ Gateway Ping: %sms\n".formatted(jda.getGatewayPing()) +
|
||||||
"\n" +
|
"\n" +
|
||||||
"**Bat Statistics**\n" +
|
"**Bat Statistics**\n" +
|
||||||
"➜ Uptime: **%s**\n".formatted(TimeUtils.format(bean.getUptime())) +
|
"➜ Uptime: %s\n".formatted(TimeUtils.format(bean.getUptime())) +
|
||||||
"➜ Cached Guilds: **%s**\n".formatted(guildService.getGuilds().size()) +
|
"➜ Cached Guilds: %s\n".formatted(guildService.getGuilds().size()) +
|
||||||
"➜ Cached Users: **%s**".formatted(userService.getUsers().size())
|
"➜ Cached Users: %s".formatted(userService.getUsers().size())
|
||||||
).build()).queue();
|
).build()).queue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -135,7 +135,7 @@ public class HelpCommand extends BatCommand implements EventListener {
|
|||||||
SelectOption.of(category.getName(), category.getName()).withEmoji(category.getEmoji()))
|
SelectOption.of(category.getName(), category.getName()).withEmoji(category.getEmoji()))
|
||||||
.toList());
|
.toList());
|
||||||
|
|
||||||
return new LayoutComponent[]{
|
return new LayoutComponent[] {
|
||||||
ActionRow.of(
|
ActionRow.of(
|
||||||
Button.of(ButtonStyle.LINK, Consts.INVITE_URL, "Invite"),
|
Button.of(ButtonStyle.LINK, Consts.INVITE_URL, "Invite"),
|
||||||
Button.of(ButtonStyle.LINK, Consts.SUPPORT_INVITE_URL, "Support")
|
Button.of(ButtonStyle.LINK, Consts.SUPPORT_INVITE_URL, "Support")
|
||||||
@ -147,4 +147,4 @@ public class HelpCommand extends BatCommand implements EventListener {
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ public class PingCommand extends BatCommand {
|
|||||||
interaction.reply("Pinging...").queue(response -> {
|
interaction.reply("Pinging...").queue(response -> {
|
||||||
response.editOriginal("Gateway response time: `%sms`\nAPI response time `%sms`".formatted(
|
response.editOriginal("Gateway response time: `%sms`\nAPI response time `%sms`".formatted(
|
||||||
DiscordService.JDA.getGatewayPing(),
|
DiscordService.JDA.getGatewayPing(),
|
||||||
System.currentTimeMillis() - time
|
System.currentTimeMillis() - time
|
||||||
)).queue();
|
)).queue();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ public class SetSubCommand extends BatSubCommand {
|
|||||||
}
|
}
|
||||||
guildService.saveGuild(batGuild);
|
guildService.saveGuild(batGuild);
|
||||||
if (!infinite) {
|
if (!infinite) {
|
||||||
interaction.reply("The guild **%s** has been set as premium until <t:%s>".formatted(guild.getName(), premium.getExpiresAt().toInstant().toEpochMilli() / 1000)).queue();
|
interaction.reply("The guild **%s** has been set as premium until <t:%s>".formatted(guild.getName(), premium.getExpiresAt().toInstant().toEpochMilli()/1000)).queue();
|
||||||
} else {
|
} else {
|
||||||
interaction.reply("The guild **%s** has been set as premium indefinitely".formatted(guild.getName())).queue();
|
interaction.reply("The guild **%s** has been set as premium indefinitely".formatted(guild.getName())).queue();
|
||||||
}
|
}
|
||||||
|
@ -25,9 +25,9 @@ public class PremiumCommand extends BatCommand {
|
|||||||
EmbedBuilder embed = EmbedUtils.genericEmbed().setAuthor("Premium Information");
|
EmbedBuilder embed = EmbedUtils.genericEmbed().setAuthor("Premium Information");
|
||||||
if (premium.hasPremium()) {
|
if (premium.hasPremium()) {
|
||||||
embed.addField("Premium", premium.hasPremium() ? "Yes" : "No", true);
|
embed.addField("Premium", premium.hasPremium() ? "Yes" : "No", true);
|
||||||
embed.addField("Started", "<t:%d>".formatted(premium.getActivatedAt().toInstant().toEpochMilli() / 1000), true);
|
embed.addField("Started On", "<t:%d>".formatted(premium.getActivatedAt().toInstant().toEpochMilli()/1000), true);
|
||||||
embed.addField("Expires", premium.isInfinite() ? "Never" : "<t:%d>"
|
embed.addField("Expires At", premium.isInfinite() ? "Never" : "<t:%d>"
|
||||||
.formatted(premium.getExpiresAt().toInstant().toEpochMilli() / 1000), true);
|
.formatted(premium.getExpiresAt().toInstant().toEpochMilli()/1000), true);
|
||||||
} else {
|
} else {
|
||||||
embed.setDescription("The guild does not have premium");
|
embed.setDescription("The guild does not have premium");
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ public final class MathUtils {
|
|||||||
/**
|
/**
|
||||||
* Format a number to a specific amount of decimal places.
|
* Format a number to a specific amount of decimal places.
|
||||||
*
|
*
|
||||||
* @param number the number to format
|
* @param number the number to format
|
||||||
* @param additional the additional decimal places to format
|
* @param additional the additional decimal places to format
|
||||||
* @return the formatted number
|
* @return the formatted number
|
||||||
*/
|
*/
|
||||||
|
@ -15,7 +15,7 @@ public class MemberUtils {
|
|||||||
* Checks if a user has permission to edit another user
|
* Checks if a user has permission to edit another user
|
||||||
*
|
*
|
||||||
* @param guild the guild to check
|
* @param guild the guild to check
|
||||||
* @param user the user to check
|
* @param user the user to check
|
||||||
* @return if the user has permission to edit another user
|
* @return if the user has permission to edit another user
|
||||||
*/
|
*/
|
||||||
public static boolean hasPermissionToEdit(BatGuild guild, BatUser user) {
|
public static boolean hasPermissionToEdit(BatGuild guild, BatUser user) {
|
||||||
|
@ -12,7 +12,7 @@ public class NumberUtils {
|
|||||||
/**
|
/**
|
||||||
* Formats a number with commas.
|
* Formats a number with commas.
|
||||||
* <p>
|
* <p>
|
||||||
* Example: 1000 -> 1,000 | Example: 1000.5 -> 1,000.5
|
* Example: 1000 -> 1,000 | Example: 1000.5 -> 1,000.5
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param number the number to format
|
* @param number the number to format
|
||||||
|
@ -8,16 +8,14 @@ import lombok.Setter;
|
|||||||
* @author Fascinated (fascinated7)
|
* @author Fascinated (fascinated7)
|
||||||
*/
|
*/
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@Getter
|
@Getter @Setter
|
||||||
@Setter
|
|
||||||
public abstract class Profile {
|
public abstract class Profile {
|
||||||
/**
|
/**
|
||||||
* The key of the profile.
|
* The key of the profile.
|
||||||
*/
|
*/
|
||||||
private String profileKey;
|
private String profileKey;
|
||||||
|
|
||||||
public Profile() {
|
public Profile() {}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets the profile
|
* Resets the profile
|
||||||
|
@ -19,7 +19,7 @@ public class ProfileHolder {
|
|||||||
* Gets a profile for the holder
|
* Gets a profile for the holder
|
||||||
*
|
*
|
||||||
* @param clazz The class of the profile
|
* @param clazz The class of the profile
|
||||||
* @param <T> The type of the profile
|
* @param <T> The type of the profile
|
||||||
* @return The profile
|
* @return The profile
|
||||||
*/
|
*/
|
||||||
public <T extends Profile> T getProfile(Class<T> clazz) {
|
public <T extends Profile> T getProfile(Class<T> clazz) {
|
||||||
@ -30,7 +30,7 @@ public class ProfileHolder {
|
|||||||
Profile profile = profiles.values().stream().filter(p -> p.getClass().equals(clazz)).findFirst().orElse(null);
|
Profile profile = profiles.values().stream().filter(p -> p.getClass().equals(clazz)).findFirst().orElse(null);
|
||||||
if (profile == null) {
|
if (profile == null) {
|
||||||
try {
|
try {
|
||||||
profile = clazz.newInstance();
|
profile = (Profile) clazz.newInstance();
|
||||||
profiles.put(profile.getProfileKey(), profile);
|
profiles.put(profile.getProfileKey(), profile);
|
||||||
} catch (InstantiationException | IllegalAccessException e) {
|
} catch (InstantiationException | IllegalAccessException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
@ -11,9 +11,9 @@ public class RoleUtils {
|
|||||||
/**
|
/**
|
||||||
* Checks if a member has permission to give the role to another member
|
* Checks if a member has permission to give the role to another member
|
||||||
*
|
*
|
||||||
* @param guild the guild to check
|
* @param guild the guild to check
|
||||||
* @param member the member to check
|
* @param member the member to check
|
||||||
* @param role the role to check
|
* @param role the role to check
|
||||||
* @return if the member has permission to give the role
|
* @return if the member has permission to give the role
|
||||||
*/
|
*/
|
||||||
public static boolean hasPermissionToGiveRole(BatGuild guild, Member member, Role role) {
|
public static boolean hasPermissionToGiveRole(BatGuild guild, Member member, Role role) {
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
package cc.fascinated.bat.common;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Fascinated (fascinated7)
|
|
||||||
*/
|
|
||||||
public class StringUtils {
|
|
||||||
/**
|
|
||||||
* Generates a random string
|
|
||||||
*
|
|
||||||
* @param length the length of the string
|
|
||||||
* @return the random string
|
|
||||||
*/
|
|
||||||
public static String randomString(int length) {
|
|
||||||
StringBuilder stringBuilder = new StringBuilder();
|
|
||||||
for (int i = 0; i < length; i++) {
|
|
||||||
stringBuilder.append((char) (Math.random() * 26 + 'a'));
|
|
||||||
}
|
|
||||||
return stringBuilder.toString();
|
|
||||||
}
|
|
||||||
}
|
|
@ -20,49 +20,49 @@ public final class TimeUtils {
|
|||||||
* @return the formatted time
|
* @return the formatted time
|
||||||
*/
|
*/
|
||||||
public static String format(long millis) {
|
public static String format(long millis) {
|
||||||
return format(millis, BatTimeFormat.FIT);
|
return format(millis, WildTimeUnit.FIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format a time in millis to a readable time format.
|
* Format a time in millis to a readable time format.
|
||||||
*
|
*
|
||||||
* @param millis the millis to format
|
* @param millis the millis to format
|
||||||
* @param timeUnit the time unit to format the millis to
|
* @param timeUnit the time unit to format the millis to
|
||||||
* @return the formatted time
|
* @return the formatted time
|
||||||
*/
|
*/
|
||||||
public static String format(long millis, BatTimeFormat timeUnit) {
|
public static String format(long millis, WildTimeUnit timeUnit) {
|
||||||
return format(millis, timeUnit, false);
|
return format(millis, timeUnit, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format a time in millis to a readable time format.
|
* Format a time in millis to a readable time format.
|
||||||
*
|
*
|
||||||
* @param millis the millis to format
|
* @param millis the millis to format
|
||||||
* @param timeUnit the time unit to format the millis to
|
* @param timeUnit the time unit to format the millis to
|
||||||
* @param compact whether to use a compact display
|
* @param compact whether to use a compact display
|
||||||
* @return the formatted time
|
* @return the formatted time
|
||||||
*/
|
*/
|
||||||
public static String format(long millis, BatTimeFormat timeUnit, boolean compact) {
|
public static String format(long millis, WildTimeUnit timeUnit, boolean compact) {
|
||||||
return format(millis, timeUnit, true, compact);
|
return format(millis, timeUnit, true, compact);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format a time in millis to a readable time format.
|
* Format a time in millis to a readable time format.
|
||||||
*
|
*
|
||||||
* @param millis the millis to format
|
* @param millis the millis to format
|
||||||
* @param timeUnit the time unit to format the millis to
|
* @param timeUnit the time unit to format the millis to
|
||||||
* @param decimals whether to include decimals
|
* @param decimals whether to include decimals
|
||||||
* @param compact whether to use a compact display
|
* @param compact whether to use a compact display
|
||||||
* @return the formatted time
|
* @return the formatted time
|
||||||
*/
|
*/
|
||||||
public static String format(long millis, BatTimeFormat timeUnit, boolean decimals, boolean compact) {
|
public static String format(long millis, WildTimeUnit timeUnit, boolean decimals, boolean compact) {
|
||||||
if (millis == -1L) { // Format permanent
|
if (millis == -1L) { // Format permanent
|
||||||
return "Perm" + (compact ? "" : "anent");
|
return "Perm" + (compact ? "" : "anent");
|
||||||
}
|
}
|
||||||
// Format the time to the best fitting time unit
|
// Format the time to the best fitting time unit
|
||||||
if (timeUnit == BatTimeFormat.FIT) {
|
if (timeUnit == WildTimeUnit.FIT) {
|
||||||
for (BatTimeFormat otherTimeUnit : BatTimeFormat.VALUES) {
|
for (WildTimeUnit otherTimeUnit : WildTimeUnit.VALUES) {
|
||||||
if (otherTimeUnit != BatTimeFormat.FIT && millis >= otherTimeUnit.getMillis()) {
|
if (otherTimeUnit != WildTimeUnit.FIT && millis >= otherTimeUnit.getMillis()) {
|
||||||
timeUnit = otherTimeUnit;
|
timeUnit = otherTimeUnit;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -74,7 +74,7 @@ public final class TimeUtils {
|
|||||||
}
|
}
|
||||||
String formatted = time + (compact ? timeUnit.getSuffix() : " " + timeUnit.getDisplay()); // Append the time unit
|
String formatted = time + (compact ? timeUnit.getSuffix() : " " + timeUnit.getDisplay()); // Append the time unit
|
||||||
if (time != 1.0 && !compact) { // Pluralize the time unit
|
if (time != 1.0 && !compact) { // Pluralize the time unit
|
||||||
formatted += "s";
|
formatted+= "s";
|
||||||
}
|
}
|
||||||
return formatted;
|
return formatted;
|
||||||
}
|
}
|
||||||
@ -89,16 +89,16 @@ public final class TimeUtils {
|
|||||||
* @return the time in millis
|
* @return the time in millis
|
||||||
*/
|
*/
|
||||||
public static long fromString(String input) {
|
public static long fromString(String input) {
|
||||||
Matcher matcher = BatTimeFormat.SUFFIX_PATTERN.matcher(input); // Match the given input
|
Matcher matcher = WildTimeUnit.SUFFIX_PATTERN.matcher(input); // Match the given input
|
||||||
long millis = 0; // The total millis
|
long millis = 0; // The total millis
|
||||||
|
|
||||||
// Match corresponding suffixes and add up the total millis
|
// Match corresponding suffixes and add up the total millis
|
||||||
while (matcher.find()) {
|
while (matcher.find()) {
|
||||||
int amount = Integer.parseInt(matcher.group(1)); // The amount of time to add
|
int amount = Integer.parseInt(matcher.group(1)); // The amount of time to add
|
||||||
String suffix = matcher.group(2); // The unit suffix
|
String suffix = matcher.group(2); // The unit suffix
|
||||||
BatTimeFormat timeUnit = BatTimeFormat.fromSuffix(suffix); // The time unit to add
|
WildTimeUnit timeUnit = WildTimeUnit.fromSuffix(suffix); // The time unit to add
|
||||||
if (timeUnit != null) { // Increment the total millis
|
if (timeUnit != null) { // Increment the total millis
|
||||||
millis += amount * timeUnit.getMillis();
|
millis+= amount * timeUnit.getMillis();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return millis;
|
return millis;
|
||||||
@ -107,11 +107,9 @@ public final class TimeUtils {
|
|||||||
/**
|
/**
|
||||||
* Represents a unit of time.
|
* Represents a unit of time.
|
||||||
*/
|
*/
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor @AllArgsConstructor
|
||||||
@AllArgsConstructor
|
@Getter(AccessLevel.PRIVATE) @ToString
|
||||||
@Getter(AccessLevel.PRIVATE)
|
public enum WildTimeUnit {
|
||||||
@ToString
|
|
||||||
public enum BatTimeFormat {
|
|
||||||
FIT,
|
FIT,
|
||||||
YEARS("Year", "y", TimeUnit.DAYS.toMillis(365L)),
|
YEARS("Year", "y", TimeUnit.DAYS.toMillis(365L)),
|
||||||
MONTHS("Month", "mo", TimeUnit.DAYS.toMillis(30L)),
|
MONTHS("Month", "mo", TimeUnit.DAYS.toMillis(30L)),
|
||||||
@ -125,7 +123,7 @@ public final class TimeUtils {
|
|||||||
/**
|
/**
|
||||||
* Our cached unit values.
|
* Our cached unit values.
|
||||||
*/
|
*/
|
||||||
public static final BatTimeFormat[] VALUES = values();
|
public static final WildTimeUnit[] VALUES = values();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Our cached suffix pattern.
|
* Our cached suffix pattern.
|
||||||
@ -154,8 +152,8 @@ public final class TimeUtils {
|
|||||||
* @return the time unit, null if not found
|
* @return the time unit, null if not found
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public static BatTimeFormat fromSuffix(String suffix) {
|
public static WildTimeUnit fromSuffix(String suffix) {
|
||||||
for (BatTimeFormat unit : VALUES) {
|
for (WildTimeUnit unit : VALUES) {
|
||||||
if (unit != FIT && unit.getSuffix().equals(suffix)) {
|
if (unit != FIT && unit.getSuffix().equals(suffix)) {
|
||||||
return unit;
|
return unit;
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ public class TimerUtils {
|
|||||||
* Runs a repeating task on a schedule
|
* Runs a repeating task on a schedule
|
||||||
*
|
*
|
||||||
* @param runnable the task to run
|
* @param runnable the task to run
|
||||||
* @param delay the delay before the task runs
|
* @param delay the delay before the task runs
|
||||||
*/
|
*/
|
||||||
public static void scheduleRepeating(Runnable runnable, long delay, long period) {
|
public static void scheduleRepeating(Runnable runnable, long delay, long period) {
|
||||||
new Timer().scheduleAtFixedRate(new TimerTask() {
|
new Timer().scheduleAtFixedRate(new TimerTask() {
|
||||||
|
@ -27,15 +27,14 @@ public class WebRequest {
|
|||||||
* Gets a response from the given URL.
|
* Gets a response from the given URL.
|
||||||
*
|
*
|
||||||
* @param url the url
|
* @param url the url
|
||||||
* @param <T> the type of the response
|
|
||||||
* @return the response
|
* @return the response
|
||||||
|
* @param <T> the type of the response
|
||||||
*/
|
*/
|
||||||
public static <T> T getAsEntity(String url, Class<T> clazz) throws RateLimitException {
|
public static <T> T getAsEntity(String url, Class<T> clazz) throws RateLimitException {
|
||||||
ResponseEntity<T> responseEntity = CLIENT.get()
|
ResponseEntity<T> responseEntity = CLIENT.get()
|
||||||
.uri(url)
|
.uri(url)
|
||||||
.retrieve()
|
.retrieve()
|
||||||
.onStatus(HttpStatusCode::isError, (request, response) -> {
|
.onStatus(HttpStatusCode::isError, (request, response) -> {}) // Don't throw exceptions on error
|
||||||
}) // Don't throw exceptions on error
|
|
||||||
.toEntity(clazz);
|
.toEntity(clazz);
|
||||||
|
|
||||||
if (responseEntity.getStatusCode().isError()) {
|
if (responseEntity.getStatusCode().isError()) {
|
||||||
@ -57,8 +56,7 @@ public class WebRequest {
|
|||||||
return CLIENT.get()
|
return CLIENT.get()
|
||||||
.uri(url)
|
.uri(url)
|
||||||
.retrieve()
|
.retrieve()
|
||||||
.onStatus(HttpStatusCode::isError, (request, response) -> {
|
.onStatus(HttpStatusCode::isError, (request, response) -> {}) // Don't throw exceptions on error
|
||||||
}) // Don't throw exceptions on error
|
|
||||||
.toEntity(clazz);
|
.toEntity(clazz);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,8 +70,7 @@ public class WebRequest {
|
|||||||
return CLIENT.head()
|
return CLIENT.head()
|
||||||
.uri(url)
|
.uri(url)
|
||||||
.retrieve()
|
.retrieve()
|
||||||
.onStatus(HttpStatusCode::isError, (request, response) -> {
|
.onStatus(HttpStatusCode::isError, (request, response) -> {}) // Don't throw exceptions on error
|
||||||
}) // Don't throw exceptions on error
|
|
||||||
.toEntity(clazz);
|
.toEntity(clazz);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,5 +8,4 @@ import org.springframework.context.annotation.Configuration;
|
|||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
@ComponentScan(basePackages = "cc.fascinated.bat")
|
@ComponentScan(basePackages = "cc.fascinated.bat")
|
||||||
public class AppConfig {
|
public class AppConfig { }
|
||||||
}
|
|
@ -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<String> authorizationCallback(@RequestParam String code) {
|
|
||||||
return ResponseEntity.ok(spotifyService.authorize(code));
|
|
||||||
}
|
|
||||||
}
|
|
@ -8,8 +8,6 @@ import cc.fascinated.bat.model.token.beatsaber.scoresaber.ScoreSaberScoreToken;
|
|||||||
import lombok.NonNull;
|
import lombok.NonNull;
|
||||||
import net.dv8tion.jda.api.events.guild.member.GuildMemberJoinEvent;
|
import net.dv8tion.jda.api.events.guild.member.GuildMemberJoinEvent;
|
||||||
import net.dv8tion.jda.api.events.guild.member.GuildMemberRemoveEvent;
|
import net.dv8tion.jda.api.events.guild.member.GuildMemberRemoveEvent;
|
||||||
import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent;
|
|
||||||
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
|
|
||||||
import net.dv8tion.jda.api.events.interaction.component.StringSelectInteractionEvent;
|
import net.dv8tion.jda.api.events.interaction.component.StringSelectInteractionEvent;
|
||||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||||
|
|
||||||
@ -20,65 +18,42 @@ public interface EventListener {
|
|||||||
/**
|
/**
|
||||||
* Called when a ScoreSaber score is received
|
* Called when a ScoreSaber score is received
|
||||||
*
|
*
|
||||||
* @param score the score that was set
|
* @param score the score that was set
|
||||||
* @param leaderboard the leaderboard that the score was set on
|
* @param leaderboard the leaderboard that the score was set on
|
||||||
* @param player the player that set the score
|
* @param player the player that set the score
|
||||||
*/
|
*/
|
||||||
default void onScoresaberScoreReceived(@NonNull ScoreSaberPlayerScoreToken score, @NonNull ScoreSaberLeaderboardToken leaderboard,
|
default void onScoresaberScoreReceived(@NonNull ScoreSaberPlayerScoreToken score, @NonNull ScoreSaberLeaderboardToken leaderboard,
|
||||||
@NonNull ScoreSaberScoreToken.LeaderboardPlayerInfo player) {
|
@NonNull ScoreSaberScoreToken.LeaderboardPlayerInfo player) {}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a user joins a guild
|
* Called when a user joins a guild
|
||||||
*
|
*
|
||||||
* @param guild the guild the user joined
|
* @param guild the guild the user joined
|
||||||
* @param user the user that joined the guild
|
* @param user the user that joined the guild
|
||||||
*/
|
*/
|
||||||
default void onGuildMemberJoin(@NonNull BatGuild guild, @NonNull BatUser user, @NonNull GuildMemberJoinEvent event) {
|
default void onGuildMemberJoin(@NonNull BatGuild guild, @NonNull BatUser user, @NonNull GuildMemberJoinEvent event) {}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a user leaves a guild
|
* Called when a user leaves a guild
|
||||||
*
|
*
|
||||||
* @param guild the guild the user left
|
* @param guild the guild the user left
|
||||||
* @param user the user that left the guild
|
* @param user the user that left the guild
|
||||||
*/
|
*/
|
||||||
default void onGuildMemberLeave(@NonNull BatGuild guild, @NonNull BatUser user, @NonNull GuildMemberRemoveEvent event) {
|
default void onGuildMemberLeave(@NonNull BatGuild guild, @NonNull BatUser user, @NonNull GuildMemberRemoveEvent event) {}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a user types a message
|
* Called when a user types a message
|
||||||
*
|
*
|
||||||
* @param guild the guild that the message was sent in
|
* @param guild the guild that the message was sent in
|
||||||
* @param user the user that sent the message
|
* @param user the user that sent the message
|
||||||
*/
|
*/
|
||||||
default void onGuildMessageReceive(@NonNull BatGuild guild, @NonNull BatUser user, @NonNull MessageReceivedEvent event) {
|
default void onGuildMessageReceive(@NonNull BatGuild guild, @NonNull BatUser user, @NonNull MessageReceivedEvent event) {}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a user selects a string
|
* Called when a user selects a string
|
||||||
*
|
*
|
||||||
* @param guild the guild that the string was selected in
|
* @param guild the guild that the string was selected in
|
||||||
* @param user the user that selected the string
|
* @param user the user that selected the string
|
||||||
*/
|
*/
|
||||||
default void onStringSelectInteraction(BatGuild guild, @NonNull BatUser user, @NonNull StringSelectInteractionEvent event) {
|
default void onStringSelectInteraction(BatGuild guild, @NonNull BatUser user, @NonNull StringSelectInteractionEvent event) {}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when a user interacts with a button
|
|
||||||
*
|
|
||||||
* @param guild the guild that the button was interacted with in
|
|
||||||
* @param user the user that interacted with the button
|
|
||||||
*/
|
|
||||||
default void onButtonInteraction(BatGuild guild, @NonNull BatUser user, @NonNull ButtonInteractionEvent event) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when a user interacts with a modal
|
|
||||||
*
|
|
||||||
* @param guild the guild that the modal was interacted with in
|
|
||||||
* @param user the user that interacted with the modal
|
|
||||||
*/
|
|
||||||
default void onModalInteraction(BatGuild guild, @NonNull BatUser user, @NonNull ModalInteractionEvent event) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -6,5 +6,4 @@ import org.springframework.web.bind.annotation.ResponseStatus;
|
|||||||
|
|
||||||
@StandardException
|
@StandardException
|
||||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||||
public class BadRequestException extends RuntimeException {
|
public class BadRequestException extends RuntimeException { }
|
||||||
}
|
|
||||||
|
@ -6,5 +6,4 @@ import org.springframework.web.bind.annotation.ResponseStatus;
|
|||||||
|
|
||||||
@StandardException
|
@StandardException
|
||||||
@ResponseStatus(HttpStatus.NOT_FOUND)
|
@ResponseStatus(HttpStatus.NOT_FOUND)
|
||||||
public class ResourceNotFoundException extends RuntimeException {
|
public class ResourceNotFoundException extends RuntimeException { }
|
||||||
}
|
|
||||||
|
@ -29,7 +29,7 @@ public abstract class Feature {
|
|||||||
* Registers the command for the feature
|
* Registers the command for the feature
|
||||||
*
|
*
|
||||||
* @param commandService The command service
|
* @param commandService The command service
|
||||||
* @param command The command to register
|
* @param command The command to register
|
||||||
*/
|
*/
|
||||||
public void registerCommand(@NonNull CommandService commandService, @NonNull BatCommand command) {
|
public void registerCommand(@NonNull CommandService commandService, @NonNull BatCommand command) {
|
||||||
command.setCategory(category);
|
command.setCategory(category);
|
||||||
|
@ -24,7 +24,7 @@ public class AfkProfile extends Profile {
|
|||||||
/**
|
/**
|
||||||
* Adds a user to the AFK list
|
* Adds a user to the AFK list
|
||||||
*
|
*
|
||||||
* @param guild the guild enable afk mode for
|
* @param guild the guild enable afk mode for
|
||||||
* @param userId the user ID to add
|
* @param userId the user ID to add
|
||||||
* @param reason the reason for being AFK
|
* @param reason the reason for being AFK
|
||||||
*/
|
*/
|
||||||
@ -41,14 +41,13 @@ public class AfkProfile extends Profile {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
member.modifyNickname("[AFK] " + member.getEffectiveName()).queue();
|
member.modifyNickname("[AFK] " + member.getEffectiveName()).queue();
|
||||||
} catch (Exception ignored) {
|
} catch (Exception ignored) {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes a user from the AFK list
|
* Removes a user from the AFK list
|
||||||
*
|
*
|
||||||
* @param guild the guild to remove the user from
|
* @param guild the guild to remove the user from
|
||||||
* @param userId the user ID to remove
|
* @param userId the user ID to remove
|
||||||
*/
|
*/
|
||||||
public void removeAfkUser(BatGuild guild, String userId) {
|
public void removeAfkUser(BatGuild guild, String userId) {
|
||||||
@ -64,8 +63,7 @@ public class AfkProfile extends Profile {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
member.modifyNickname(member.getEffectiveName().replace("[AFK] ", "")).queue();
|
member.modifyNickname(member.getEffectiveName().replace("[AFK] ", "")).queue();
|
||||||
} catch (Exception ignored) {
|
} catch (Exception ignored) {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -40,7 +40,7 @@ public class AddSubCommand extends BatSubCommand {
|
|||||||
if (profile.getRoleSlotsInUse() >= maxRoleSlots) {
|
if (profile.getRoleSlotsInUse() >= maxRoleSlots) {
|
||||||
interaction.replyEmbeds(EmbedUtils.errorEmbed()
|
interaction.replyEmbeds(EmbedUtils.errorEmbed()
|
||||||
.setDescription("The guild can only have a maximum of %d auto roles"
|
.setDescription("The guild can only have a maximum of %d auto roles"
|
||||||
.formatted(maxRoleSlots))
|
.formatted(maxRoleSlots))
|
||||||
.build()).queue();
|
.build()).queue();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -13,8 +13,7 @@ import java.util.List;
|
|||||||
/**
|
/**
|
||||||
* @author Fascinated (fascinated7)
|
* @author Fascinated (fascinated7)
|
||||||
*/
|
*/
|
||||||
@Setter
|
@Setter @Getter
|
||||||
@Getter
|
|
||||||
public class AutoRoleProfile extends Profile {
|
public class AutoRoleProfile extends Profile {
|
||||||
private static final int DEFAULT_MAX_ROLES = 10;
|
private static final int DEFAULT_MAX_ROLES = 10;
|
||||||
private static final int PREMIUM_MAX_ROLES = 25;
|
private static final int PREMIUM_MAX_ROLES = 25;
|
||||||
@ -28,19 +27,6 @@ public class AutoRoleProfile extends Profile {
|
|||||||
super("auto-role");
|
super("auto-role");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the maximum amount of roles that can be set in the guild
|
|
||||||
*
|
|
||||||
* @param guild the guild to check
|
|
||||||
* @return the amount of role slots
|
|
||||||
*/
|
|
||||||
public static int getMaxRoleSlots(BatGuild guild) {
|
|
||||||
if (guild.getPremium().hasPremium()) {
|
|
||||||
return PREMIUM_MAX_ROLES;
|
|
||||||
}
|
|
||||||
return DEFAULT_MAX_ROLES;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the amount of role slots in use
|
* Gets the amount of role slots in use
|
||||||
*
|
*
|
||||||
@ -106,6 +92,19 @@ public class AutoRoleProfile extends Profile {
|
|||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the maximum amount of roles that can be set in the guild
|
||||||
|
*
|
||||||
|
* @param guild the guild to check
|
||||||
|
* @return the amount of role slots
|
||||||
|
*/
|
||||||
|
public static int getMaxRoleSlots(BatGuild guild) {
|
||||||
|
if (guild.getPremium().hasPremium()) {
|
||||||
|
return PREMIUM_MAX_ROLES;
|
||||||
|
}
|
||||||
|
return DEFAULT_MAX_ROLES;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reset() {
|
public void reset() {
|
||||||
roleIds.clear();
|
roleIds.clear();
|
||||||
|
@ -80,8 +80,7 @@ public class MessageSubCommand extends BatSubCommand {
|
|||||||
private Date parseBirthday(String birthday) {
|
private Date parseBirthday(String birthday) {
|
||||||
try {
|
try {
|
||||||
return FORMATTER.parse(birthday);
|
return FORMATTER.parse(birthday);
|
||||||
} catch (ParseException ignored) {
|
} catch (ParseException ignored) {}
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,8 +80,7 @@ public class SetSubCommand extends BatSubCommand {
|
|||||||
private Date parseBirthday(String birthday) {
|
private Date parseBirthday(String birthday) {
|
||||||
try {
|
try {
|
||||||
return FORMATTER.parse(birthday);
|
return FORMATTER.parse(birthday);
|
||||||
} catch (ParseException ignored) {
|
} catch (ParseException ignored) {}
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,8 +19,7 @@ import org.springframework.stereotype.Component;
|
|||||||
/**
|
/**
|
||||||
* @author Fascinated (fascinated7)
|
* @author Fascinated (fascinated7)
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component @Log4j2
|
||||||
@Log4j2
|
|
||||||
public class NumberOneScoreFeedListener implements EventListener {
|
public class NumberOneScoreFeedListener implements EventListener {
|
||||||
private final GuildService guildService;
|
private final GuildService guildService;
|
||||||
|
|
||||||
|
@ -18,8 +18,7 @@ import org.springframework.stereotype.Component;
|
|||||||
/**
|
/**
|
||||||
* @author Fascinated (fascinated7)
|
* @author Fascinated (fascinated7)
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component @Log4j2
|
||||||
@Log4j2
|
|
||||||
public class UserScoreFeedListener implements EventListener {
|
public class UserScoreFeedListener implements EventListener {
|
||||||
private final GuildService guildService;
|
private final GuildService guildService;
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ public class ChannelSubCommand extends BatSubCommand {
|
|||||||
if (option == null) {
|
if (option == null) {
|
||||||
if (!TextChannelUtils.isValidChannel(profile.getChannelId())) {
|
if (!TextChannelUtils.isValidChannel(profile.getChannelId())) {
|
||||||
interaction.replyEmbeds(EmbedUtils.errorEmbed()
|
interaction.replyEmbeds(EmbedUtils.errorEmbed()
|
||||||
.setDescription("There is no channel set for the feed notifications.")
|
.setDescription("There is no channel set for the feed notifications. Please provide a channel to set the feed channel to")
|
||||||
.build()).queue();
|
.build()).queue();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,7 @@ public class LinkSubCommand extends BatSubCommand {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
user.getProfile(UserScoreSaberProfile.class).setSteamId(id);
|
((UserScoreSaberProfile) user.getProfile(UserScoreSaberProfile.class)).setSteamId(id);
|
||||||
userService.saveUser(user);
|
userService.saveUser(user);
|
||||||
interaction.replyEmbeds(EmbedUtils.successEmbed()
|
interaction.replyEmbeds(EmbedUtils.successEmbed()
|
||||||
.setDescription("Successfully linked your [ScoreSaber](%s) profile".formatted("https://scoresaber.com/u/%s".formatted(id)))
|
.setDescription("Successfully linked your [ScoreSaber](%s) profile".formatted("https://scoresaber.com/u/%s".formatted(id)))
|
||||||
|
@ -41,6 +41,11 @@ public class ScoreSaberCommand extends BatCommand {
|
|||||||
super.addSubCommand(context.getBean(ResetSubCommand.class));
|
super.addSubCommand(context.getBean(ResetSubCommand.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction interaction) {
|
||||||
|
sendProfileEmbed(true, user, scoreSaberService, interaction);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds the profile embed for the ScoreSaber profile
|
* Builds the profile embed for the ScoreSaber profile
|
||||||
*
|
*
|
||||||
@ -98,9 +103,4 @@ public class ScoreSaberCommand extends BatCommand {
|
|||||||
.build()).queue();
|
.build()).queue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction interaction) {
|
|
||||||
sendProfileEmbed(true, user, scoreSaberService, interaction);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ public class ChannelSubCommand extends BatSubCommand {
|
|||||||
if (option == null) {
|
if (option == null) {
|
||||||
if (!TextChannelUtils.isValidChannel(profile.getChannelId())) {
|
if (!TextChannelUtils.isValidChannel(profile.getChannelId())) {
|
||||||
interaction.replyEmbeds(EmbedUtils.errorEmbed()
|
interaction.replyEmbeds(EmbedUtils.errorEmbed()
|
||||||
.setDescription("There is no channel set for the feed notifications.")
|
.setDescription("There is no channel set for the feed notifications. Please provide a channel to set the feed channel to")
|
||||||
.build()).queue();
|
.build()).queue();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ public class UserSubCommand extends BatSubCommand {
|
|||||||
public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction interaction) {
|
public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction interaction) {
|
||||||
GuildUserScoreFeedProfile profile = guild.getProfile(GuildUserScoreFeedProfile.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()) {
|
||||||
interaction.replyEmbeds(EmbedUtils.errorEmbed()
|
interaction.replyEmbeds(EmbedUtils.errorEmbed()
|
||||||
.setDescription("There are no users being tracked in the feed")
|
.setDescription("There are no users being tracked in the feed")
|
||||||
|
@ -9,8 +9,7 @@ import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
|
|||||||
/**
|
/**
|
||||||
* @author Fascinated (fascinated7)
|
* @author Fascinated (fascinated7)
|
||||||
*/
|
*/
|
||||||
@Getter
|
@Getter @Setter
|
||||||
@Setter
|
|
||||||
public class GuildNumberOneScoreFeedProfile extends Profile {
|
public class GuildNumberOneScoreFeedProfile extends Profile {
|
||||||
/**
|
/**
|
||||||
* The channel ID of the score feed
|
* The channel ID of the score feed
|
||||||
|
@ -12,8 +12,7 @@ import java.util.List;
|
|||||||
/**
|
/**
|
||||||
* @author Fascinated (fascinated7)
|
* @author Fascinated (fascinated7)
|
||||||
*/
|
*/
|
||||||
@Getter
|
@Getter @Setter
|
||||||
@Setter
|
|
||||||
public class GuildUserScoreFeedProfile extends Profile {
|
public class GuildUserScoreFeedProfile extends Profile {
|
||||||
/**
|
/**
|
||||||
* The channel ID of the score feed
|
* The channel ID of the score feed
|
||||||
|
@ -7,8 +7,7 @@ import lombok.Setter;
|
|||||||
/**
|
/**
|
||||||
* @author Fascinated (fascinated7)
|
* @author Fascinated (fascinated7)
|
||||||
*/
|
*/
|
||||||
@Setter
|
@Setter @Getter
|
||||||
@Getter
|
|
||||||
public class UserScoreSaberProfile extends Profile {
|
public class UserScoreSaberProfile extends Profile {
|
||||||
/**
|
/**
|
||||||
* The Account ID of the ScoreSaber profile
|
* The Account ID of the ScoreSaber profile
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
package cc.fascinated.bat.features.spotify;
|
|
||||||
|
|
||||||
import cc.fascinated.bat.command.Category;
|
|
||||||
import cc.fascinated.bat.common.EmbedUtils;
|
|
||||||
import cc.fascinated.bat.features.Feature;
|
|
||||||
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 SpotifyFeature extends Feature {
|
|
||||||
@Autowired
|
|
||||||
public SpotifyFeature() {
|
|
||||||
super("Spotify", Category.MUSIC);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The embed for when a user needs to link their Spotify account.
|
|
||||||
*
|
|
||||||
* @return The embed.
|
|
||||||
*/
|
|
||||||
public static MessageEmbed linkAccountEmbed() {
|
|
||||||
return EmbedUtils.genericEmbed()
|
|
||||||
.setDescription("You need to link your Spotify account before you can use this command.")
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,87 +0,0 @@
|
|||||||
package cc.fascinated.bat.features.spotify.command;
|
|
||||||
|
|
||||||
import cc.fascinated.bat.command.BatSubCommand;
|
|
||||||
import cc.fascinated.bat.command.CommandInfo;
|
|
||||||
import cc.fascinated.bat.common.EmbedUtils;
|
|
||||||
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.extern.log4j.Log4j2;
|
|
||||||
import net.dv8tion.jda.api.entities.Member;
|
|
||||||
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;
|
|
||||||
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
|
|
||||||
@Log4j2
|
|
||||||
@CommandInfo(name = "current", description = "Gets the currently playing Spotify track")
|
|
||||||
public class CurrentSubCommand extends BatSubCommand {
|
|
||||||
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, @NonNull SlashCommandInteraction interaction) {
|
|
||||||
SpotifyProfile profile = user.getProfile(SpotifyProfile.class);
|
|
||||||
if (!profile.hasLinkedAccount()) {
|
|
||||||
interaction.replyEmbeds(SpotifyFeature.linkAccountEmbed()).queue();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!spotifyService.hasTrackPlaying(user)) {
|
|
||||||
interaction.replyEmbeds(EmbedUtils.genericEmbed()
|
|
||||||
.setDescription("You are not currently playing a track.")
|
|
||||||
.build())
|
|
||||||
.queue();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
CurrentlyPlaying currentlyPlaying = spotifyService.getCurrentlyPlayingTrack(user);
|
|
||||||
Track track = (Track) currentlyPlaying.getItem();
|
|
||||||
AlbumSimplified album = track.getAlbum();
|
|
||||||
String trackUrl = "https://open.spotify.com/track/" + track.getId();
|
|
||||||
String albumUrl = "https://open.spotify.com/album/" + album.getId();
|
|
||||||
|
|
||||||
String description =
|
|
||||||
"➜ Song: **[%s](%s)**\n".formatted(track.getName(), trackUrl) +
|
|
||||||
"➜ Album: **[%s](%s)**\n".formatted(album.getName(), albumUrl) +
|
|
||||||
"➜ Position: %s\n".formatted(getFormattedTime(currentlyPlaying));
|
|
||||||
|
|
||||||
Image albumCover = album.getImages()[0];
|
|
||||||
interaction.replyEmbeds(EmbedUtils.genericEmbed()
|
|
||||||
.setAuthor("Listening to %s".formatted(track.getName()), trackUrl)
|
|
||||||
.setThumbnail(albumCover.getUrl())
|
|
||||||
.setDescription(description)
|
|
||||||
.build()).queue();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the formatted time of the currently playing track
|
|
||||||
*
|
|
||||||
* @param currentlyPlaying the currently playing track
|
|
||||||
* @return the formatted time
|
|
||||||
*/
|
|
||||||
private 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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,83 +0,0 @@
|
|||||||
package cc.fascinated.bat.features.spotify.command;
|
|
||||||
|
|
||||||
import cc.fascinated.bat.command.BatSubCommand;
|
|
||||||
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 net.dv8tion.jda.api.entities.Member;
|
|
||||||
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 BatSubCommand implements EventListener {
|
|
||||||
private final SpotifyService spotifyService;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
public LinkSubCommand(@NonNull SpotifyService spotifyService) {
|
|
||||||
this.spotifyService = spotifyService;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction interaction) {
|
|
||||||
interaction.replyEmbeds(EmbedUtils.genericEmbed()
|
|
||||||
.setDescription("You can link your Spotify account by clicking [here](%s)".formatted(spotifyService.getAuthorizationUrl()))
|
|
||||||
.build())
|
|
||||||
.addComponents(ActionRow.of(Button.primary("spotify_link", "Link Spotify")))
|
|
||||||
.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
|
|
||||||
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();
|
|
||||||
spotifyService.linkAccount(user, code);
|
|
||||||
event.replyEmbeds(EmbedUtils.successEmbed()
|
|
||||||
.setDescription("Successfully linked your Spotify account!")
|
|
||||||
.build())
|
|
||||||
.queue();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,53 +0,0 @@
|
|||||||
package cc.fascinated.bat.features.spotify.command;
|
|
||||||
|
|
||||||
import cc.fascinated.bat.command.BatSubCommand;
|
|
||||||
import cc.fascinated.bat.command.CommandInfo;
|
|
||||||
import cc.fascinated.bat.common.EmbedUtils;
|
|
||||||
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 net.dv8tion.jda.api.entities.Member;
|
|
||||||
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 BatSubCommand {
|
|
||||||
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, @NonNull SlashCommandInteraction interaction) {
|
|
||||||
SpotifyProfile profile = user.getProfile(SpotifyProfile.class);
|
|
||||||
if (!profile.hasLinkedAccount()) {
|
|
||||||
interaction.replyEmbeds(SpotifyFeature.linkAccountEmbed()).queue();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!spotifyService.hasTrackPlaying(user)) {
|
|
||||||
interaction.replyEmbeds(EmbedUtils.genericEmbed()
|
|
||||||
.setDescription("You need to be playing a track to pause it.")
|
|
||||||
.build())
|
|
||||||
.queue();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean didPause = spotifyService.pausePlayback(user);
|
|
||||||
interaction.replyEmbeds(EmbedUtils.genericEmbed()
|
|
||||||
.setDescription(didPause ? "Paused the current track." : "The current track is already paused.")
|
|
||||||
.build())
|
|
||||||
.queue();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,53 +0,0 @@
|
|||||||
package cc.fascinated.bat.features.spotify.command;
|
|
||||||
|
|
||||||
import cc.fascinated.bat.command.BatSubCommand;
|
|
||||||
import cc.fascinated.bat.command.CommandInfo;
|
|
||||||
import cc.fascinated.bat.common.EmbedUtils;
|
|
||||||
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 net.dv8tion.jda.api.entities.Member;
|
|
||||||
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 BatSubCommand {
|
|
||||||
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, @NonNull SlashCommandInteraction interaction) {
|
|
||||||
SpotifyProfile profile = user.getProfile(SpotifyProfile.class);
|
|
||||||
if (!profile.hasLinkedAccount()) {
|
|
||||||
interaction.replyEmbeds(SpotifyFeature.linkAccountEmbed()).queue();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!spotifyService.hasTrackPlaying(user)) {
|
|
||||||
interaction.replyEmbeds(EmbedUtils.genericEmbed()
|
|
||||||
.setDescription("You need to be playing a track to pause it.")
|
|
||||||
.build())
|
|
||||||
.queue();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean didPause = spotifyService.resumePlayback(user);
|
|
||||||
interaction.replyEmbeds(EmbedUtils.genericEmbed()
|
|
||||||
.setDescription(didPause ? "Resumed the current track." : "The current track is already playing.")
|
|
||||||
.build())
|
|
||||||
.queue();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
package cc.fascinated.bat.features.spotify.command;
|
|
||||||
|
|
||||||
import cc.fascinated.bat.command.BatCommand;
|
|
||||||
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)
|
|
||||||
public class SpotifyCommand extends BatCommand {
|
|
||||||
@Autowired
|
|
||||||
public SpotifyCommand(@NonNull ApplicationContext context) {
|
|
||||||
super.addSubCommand(context.getBean(LinkSubCommand.class));
|
|
||||||
super.addSubCommand(context.getBean(UnlinkSubCommand.class));
|
|
||||||
super.addSubCommand(context.getBean(PauseSubCommand.class));
|
|
||||||
super.addSubCommand(context.getBean(ResumeSubCommand.class));
|
|
||||||
super.addSubCommand(context.getBean(CurrentSubCommand.class));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,49 +0,0 @@
|
|||||||
package cc.fascinated.bat.features.spotify.command;
|
|
||||||
|
|
||||||
import cc.fascinated.bat.command.BatSubCommand;
|
|
||||||
import cc.fascinated.bat.command.CommandInfo;
|
|
||||||
import cc.fascinated.bat.common.EmbedUtils;
|
|
||||||
import cc.fascinated.bat.event.EventListener;
|
|
||||||
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.UserService;
|
|
||||||
import lombok.NonNull;
|
|
||||||
import net.dv8tion.jda.api.entities.Member;
|
|
||||||
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 BatSubCommand implements EventListener {
|
|
||||||
private final UserService userService;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
public UnlinkSubCommand(@NonNull UserService userService) {
|
|
||||||
this.userService = userService;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction interaction) {
|
|
||||||
SpotifyProfile profile = user.getProfile(SpotifyProfile.class);
|
|
||||||
if (!profile.hasLinkedAccount()) {
|
|
||||||
interaction.replyEmbeds(EmbedUtils.genericEmbed()
|
|
||||||
.setDescription("You do not have a linked Spotify account.")
|
|
||||||
.build())
|
|
||||||
.queue();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
profile.reset();
|
|
||||||
userService.saveUser(user);
|
|
||||||
interaction.replyEmbeds(EmbedUtils.genericEmbed()
|
|
||||||
.setDescription("Successfully unlinked your Spotify account.")
|
|
||||||
.build())
|
|
||||||
.queue();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
package cc.fascinated.bat.features.spotify.profile;
|
|
||||||
|
|
||||||
import cc.fascinated.bat.common.Profile;
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.Setter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Fascinated (fascinated7)
|
|
||||||
*/
|
|
||||||
@Getter @Setter
|
|
||||||
public class SpotifyProfile extends Profile {
|
|
||||||
/**
|
|
||||||
* The access token
|
|
||||||
*/
|
|
||||||
private String accessToken;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The refresh token
|
|
||||||
*/
|
|
||||||
private String refreshToken;
|
|
||||||
|
|
||||||
public SpotifyProfile() {
|
|
||||||
super("spotify");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -14,16 +14,13 @@ import java.util.Date;
|
|||||||
* @author Fascinated (fascinated7)
|
* @author Fascinated (fascinated7)
|
||||||
*/
|
*/
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@Getter
|
@Getter @Setter
|
||||||
@Setter
|
|
||||||
@Document(collection = "guilds")
|
@Document(collection = "guilds")
|
||||||
public class BatGuild extends ProfileHolder {
|
public class BatGuild extends ProfileHolder {
|
||||||
/**
|
/**
|
||||||
* The ID of the guild
|
* The ID of the guild
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull @Id private final String id;
|
||||||
@Id
|
|
||||||
private final String id;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The time this guild was joined
|
* The time this guild was joined
|
||||||
@ -65,9 +62,7 @@ public class BatGuild extends ProfileHolder {
|
|||||||
return DiscordService.JDA.getGuildById(id);
|
return DiscordService.JDA.getGuildById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor @Getter @Setter
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
public static class Premium {
|
public static class Premium {
|
||||||
/**
|
/**
|
||||||
* The time the premium was activated
|
* The time the premium was activated
|
||||||
|
@ -16,29 +16,19 @@ import java.util.Date;
|
|||||||
* @author Fascinated (fascinated7)
|
* @author Fascinated (fascinated7)
|
||||||
*/
|
*/
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@Getter
|
@Getter @Setter
|
||||||
@Setter
|
|
||||||
@Document(collection = "users")
|
@Document(collection = "users")
|
||||||
public class BatUser extends ProfileHolder {
|
public class BatUser extends ProfileHolder {
|
||||||
/**
|
/**
|
||||||
* The ID of the user
|
* The ID of the user
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull @Id private final String id;
|
||||||
@Id
|
|
||||||
private final String id;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The time this user was created
|
* The time this user was created
|
||||||
*/
|
*/
|
||||||
private Date createdAt = new Date();
|
private Date createdAt = new Date();
|
||||||
|
|
||||||
/**
|
|
||||||
* The name of the user
|
|
||||||
*/
|
|
||||||
public String getName() {
|
|
||||||
return getDiscordUser().getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the guild as the JDA Guild
|
* Gets the guild as the JDA Guild
|
||||||
*
|
*
|
||||||
|
@ -88,8 +88,7 @@ public class ScoreSaberAccountToken {
|
|||||||
/**
|
/**
|
||||||
* The badge for this account.
|
* The badge for this account.
|
||||||
*/
|
*/
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor @Getter
|
||||||
@Getter
|
|
||||||
public static class Badge {
|
public static class Badge {
|
||||||
/**
|
/**
|
||||||
* The image for this badge.
|
* The image for this badge.
|
||||||
@ -105,8 +104,7 @@ public class ScoreSaberAccountToken {
|
|||||||
/**
|
/**
|
||||||
* The score stats for this account.
|
* The score stats for this account.
|
||||||
*/
|
*/
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor @Getter
|
||||||
@Getter
|
|
||||||
public static class ScoreStats {
|
public static class ScoreStats {
|
||||||
/**
|
/**
|
||||||
* The total score for this account.
|
* The total score for this account.
|
||||||
|
@ -5,8 +5,7 @@ import lombok.ToString;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Getter
|
@Getter @ToString
|
||||||
@ToString
|
|
||||||
public class ScoreSaberLeaderboardToken {
|
public class ScoreSaberLeaderboardToken {
|
||||||
/**
|
/**
|
||||||
* The ID of the leaderboard.
|
* The ID of the leaderboard.
|
||||||
|
@ -3,8 +3,7 @@ package cc.fascinated.bat.model.token.beatsaber.scoresaber;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
|
|
||||||
@Getter
|
@Getter @ToString
|
||||||
@ToString
|
|
||||||
public class ScoreSaberPageMetadataToken {
|
public class ScoreSaberPageMetadataToken {
|
||||||
/**
|
/**
|
||||||
* The total amount of scores.
|
* The total amount of scores.
|
||||||
|
@ -3,8 +3,7 @@ package cc.fascinated.bat.model.token.beatsaber.scoresaber;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
|
|
||||||
@Getter
|
@Getter @ToString
|
||||||
@ToString
|
|
||||||
public class ScoreSaberPlayerScoreToken {
|
public class ScoreSaberPlayerScoreToken {
|
||||||
/**
|
/**
|
||||||
* The score that was set.
|
* The score that was set.
|
||||||
|
@ -3,8 +3,7 @@ package cc.fascinated.bat.model.token.beatsaber.scoresaber;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
|
|
||||||
@Getter
|
@Getter @ToString
|
||||||
@ToString
|
|
||||||
public class ScoreSaberScoreToken {
|
public class ScoreSaberScoreToken {
|
||||||
/**
|
/**
|
||||||
* The id for this score.
|
* The id for this score.
|
||||||
|
@ -4,8 +4,7 @@ import lombok.Getter;
|
|||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
|
|
||||||
@Getter
|
@Getter @Setter
|
||||||
@Setter
|
|
||||||
@ToString
|
@ToString
|
||||||
public class ScoreSaberScoresPageToken {
|
public class ScoreSaberScoresPageToken {
|
||||||
/**
|
/**
|
||||||
|
@ -6,8 +6,7 @@ import lombok.Getter;
|
|||||||
/**
|
/**
|
||||||
* @author Fascinated (fascinated7)
|
* @author Fascinated (fascinated7)
|
||||||
*/
|
*/
|
||||||
@Getter
|
@Getter @AllArgsConstructor
|
||||||
@AllArgsConstructor
|
|
||||||
public class RandomImage {
|
public class RandomImage {
|
||||||
/**
|
/**
|
||||||
* The URL of the image.
|
* The URL of the image.
|
||||||
|
@ -6,8 +6,7 @@ import lombok.Getter;
|
|||||||
/**
|
/**
|
||||||
* @author Fascinated (fascinated7)
|
* @author Fascinated (fascinated7)
|
||||||
*/
|
*/
|
||||||
@Getter
|
@Getter @AllArgsConstructor
|
||||||
@AllArgsConstructor
|
|
||||||
public class CatImageToken {
|
public class CatImageToken {
|
||||||
/**
|
/**
|
||||||
* The ID of the image.
|
* The ID of the image.
|
||||||
|
@ -6,5 +6,4 @@ import org.springframework.data.mongodb.repository.MongoRepository;
|
|||||||
/**
|
/**
|
||||||
* @author Fascinated (fascinated7)
|
* @author Fascinated (fascinated7)
|
||||||
*/
|
*/
|
||||||
public interface GuildRepository extends MongoRepository<BatGuild, String> {
|
public interface GuildRepository extends MongoRepository<BatGuild, String> { }
|
||||||
}
|
|
@ -6,5 +6,4 @@ import org.springframework.data.mongodb.repository.MongoRepository;
|
|||||||
/**
|
/**
|
||||||
* @author Fascinated (fascinated7)
|
* @author Fascinated (fascinated7)
|
||||||
*/
|
*/
|
||||||
public interface UserRepository extends MongoRepository<BatUser, String> {
|
public interface UserRepository extends MongoRepository<BatUser, String> { }
|
||||||
}
|
|
@ -17,8 +17,7 @@ import java.util.List;
|
|||||||
/**
|
/**
|
||||||
* @author Fascinated (fascinated7)
|
* @author Fascinated (fascinated7)
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service @Getter
|
||||||
@Getter
|
|
||||||
public class DiscordService {
|
public class DiscordService {
|
||||||
/**
|
/**
|
||||||
* The JDA instance
|
* The JDA instance
|
||||||
|
@ -7,8 +7,6 @@ import lombok.NonNull;
|
|||||||
import lombok.extern.log4j.Log4j2;
|
import lombok.extern.log4j.Log4j2;
|
||||||
import net.dv8tion.jda.api.events.guild.member.GuildMemberJoinEvent;
|
import net.dv8tion.jda.api.events.guild.member.GuildMemberJoinEvent;
|
||||||
import net.dv8tion.jda.api.events.guild.member.GuildMemberRemoveEvent;
|
import net.dv8tion.jda.api.events.guild.member.GuildMemberRemoveEvent;
|
||||||
import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent;
|
|
||||||
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
|
|
||||||
import net.dv8tion.jda.api.events.interaction.component.StringSelectInteractionEvent;
|
import net.dv8tion.jda.api.events.interaction.component.StringSelectInteractionEvent;
|
||||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||||
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
||||||
@ -24,16 +22,16 @@ import java.util.Set;
|
|||||||
/**
|
/**
|
||||||
* @author Fascinated (fascinated7)
|
* @author Fascinated (fascinated7)
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service @Log4j2
|
||||||
@Log4j2
|
|
||||||
@DependsOn("discordService")
|
@DependsOn("discordService")
|
||||||
public class EventService extends ListenerAdapter {
|
public class EventService extends ListenerAdapter {
|
||||||
|
private final GuildService guildService;
|
||||||
|
private final UserService userService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The list of listeners registered
|
* The list of listeners registered
|
||||||
*/
|
*/
|
||||||
public static final Set<EventListener> LISTENERS = new HashSet<>();
|
public static final Set<EventListener> LISTENERS = new HashSet<>();
|
||||||
private final GuildService guildService;
|
|
||||||
private final UserService userService;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public EventService(@NonNull GuildService guildService, @NonNull UserService userService, @NonNull ApplicationContext context) {
|
public EventService(@NonNull GuildService guildService, @NonNull UserService userService, @NonNull ApplicationContext context) {
|
||||||
@ -105,30 +103,4 @@ public class EventService extends ListenerAdapter {
|
|||||||
listener.onStringSelectInteraction(guild, user, event);
|
listener.onStringSelectInteraction(guild, user, event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onButtonInteraction(ButtonInteractionEvent event) {
|
|
||||||
if (event.getUser().isBot()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
BatGuild guild = event.getGuild() != null ? guildService.getGuild(event.getGuild().getId()) : null;
|
|
||||||
BatUser user = userService.getUser(event.getUser().getId());
|
|
||||||
|
|
||||||
for (EventListener listener : LISTENERS) {
|
|
||||||
listener.onButtonInteraction(guild, user, event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onModalInteraction(ModalInteractionEvent event) {
|
|
||||||
if (event.getUser().isBot()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
BatGuild guild = event.getGuild() != null ? guildService.getGuild(event.getGuild().getId()) : null;
|
|
||||||
BatUser user = userService.getUser(event.getUser().getId());
|
|
||||||
|
|
||||||
for (EventListener listener : LISTENERS) {
|
|
||||||
listener.onModalInteraction(guild, user, event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -16,9 +16,7 @@ import java.util.List;
|
|||||||
/**
|
/**
|
||||||
* @author Fascinated (fascinated7)
|
* @author Fascinated (fascinated7)
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service @Getter @Log4j2
|
||||||
@Getter
|
|
||||||
@Log4j2
|
|
||||||
@DependsOn("commandService")
|
@DependsOn("commandService")
|
||||||
public class FeatureService {
|
public class FeatureService {
|
||||||
/**
|
/**
|
||||||
|
@ -20,9 +20,7 @@ import java.util.concurrent.TimeUnit;
|
|||||||
/**
|
/**
|
||||||
* @author Fascinated (fascinated7)
|
* @author Fascinated (fascinated7)
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service @Log4j2 @Getter
|
||||||
@Log4j2
|
|
||||||
@Getter
|
|
||||||
@DependsOn("discordService")
|
@DependsOn("discordService")
|
||||||
public class GuildService extends ListenerAdapter {
|
public class GuildService extends ListenerAdapter {
|
||||||
/**
|
/**
|
||||||
|
@ -27,8 +27,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@Service
|
@Service @Log4j2(topic = "ScoreSaber Service")
|
||||||
@Log4j2(topic = "ScoreSaber Service")
|
|
||||||
public class ScoreSaberService extends TextWebSocketHandler {
|
public class ScoreSaberService extends TextWebSocketHandler {
|
||||||
private static final String SCORESABER_API = "https://scoresaber.com/api/";
|
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_ENDPOINT = SCORESABER_API + "player/%s/full";
|
||||||
@ -51,7 +50,7 @@ public class ScoreSaberService extends TextWebSocketHandler {
|
|||||||
*
|
*
|
||||||
* @param id The id of the account.
|
* @param id The id of the account.
|
||||||
* @return The account.
|
* @return The account.
|
||||||
* @throws ResourceNotFoundException If the account is not found.
|
* @throws ResourceNotFoundException If the account is not found.
|
||||||
* @throws cc.fascinated.bat.exception.RateLimitException If the ScoreSaber rate limit is reached.
|
* @throws cc.fascinated.bat.exception.RateLimitException If the ScoreSaber rate limit is reached.
|
||||||
*/
|
*/
|
||||||
public ScoreSaberAccountToken getAccount(String id) {
|
public ScoreSaberAccountToken getAccount(String id) {
|
||||||
@ -78,7 +77,7 @@ public class ScoreSaberService extends TextWebSocketHandler {
|
|||||||
* Gets the scores for the account.
|
* Gets the scores for the account.
|
||||||
*
|
*
|
||||||
* @param profile The profile.
|
* @param profile The profile.
|
||||||
* @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(UserScoreSaberProfile profile, int page) {
|
public ScoreSaberScoresPageToken getPageScores(UserScoreSaberProfile profile, int page) {
|
||||||
@ -132,8 +131,7 @@ public class ScoreSaberService extends TextWebSocketHandler {
|
|||||||
connectWebSocket(); // Reconnect to the WebSocket.
|
connectWebSocket(); // Reconnect to the WebSocket.
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override @SneakyThrows
|
||||||
@SneakyThrows
|
|
||||||
protected void handleTextMessage(@NonNull WebSocketSession session, @NonNull TextMessage message) {
|
protected void handleTextMessage(@NonNull WebSocketSession session, @NonNull TextMessage message) {
|
||||||
// Ignore the connection message.
|
// Ignore the connection message.
|
||||||
if (message.getPayload().equals("Connected to the ScoreSaber WSS")) {
|
if (message.getPayload().equals("Connected to the ScoreSaber WSS")) {
|
||||||
|
@ -1,188 +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 net.jodah.expiringmap.ExpiringMap;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import se.michaelthelin.spotify.SpotifyApi;
|
|
||||||
import se.michaelthelin.spotify.enums.AuthorizationScope;
|
|
||||||
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
|
|
||||||
public class SpotifyService {
|
|
||||||
/**
|
|
||||||
* The access token map.
|
|
||||||
*/
|
|
||||||
private final Map<String, AuthorizationCodeCredentials> accessToken = ExpiringMap.builder()
|
|
||||||
.expiration(30, TimeUnit.MINUTES)
|
|
||||||
.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.APP_REMOTE_CONTROL,
|
|
||||||
AuthorizationScope.USER_READ_PLAYBACK_POSITION,
|
|
||||||
AuthorizationScope.USER_READ_PLAYBACK_STATE,
|
|
||||||
AuthorizationScope.USER_MODIFY_PLAYBACK_STATE,
|
|
||||||
AuthorizationScope.USER_READ_CURRENTLY_PLAYING,
|
|
||||||
AuthorizationScope.APP_REMOTE_CONTROL,
|
|
||||||
AuthorizationScope.STREAMING
|
|
||||||
)
|
|
||||||
.build().execute().toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the currently playing track for the user.
|
|
||||||
*
|
|
||||||
* @param user the user to check
|
|
||||||
* @return the currently playing track
|
|
||||||
*/
|
|
||||||
public CurrentlyPlaying getCurrentlyPlayingTrack(BatUser user) {
|
|
||||||
try {
|
|
||||||
return getSpotifyApi(user).getUsersCurrentlyPlayingTrack().build().execute();
|
|
||||||
} catch (Exception e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
* @return if the playback was paused
|
|
||||||
*/
|
|
||||||
@SneakyThrows
|
|
||||||
public boolean pausePlayback(BatUser user) {
|
|
||||||
try {
|
|
||||||
getSpotifyApi(user).pauseUsersPlayback().build().execute();
|
|
||||||
return true;
|
|
||||||
} catch (Exception e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pauses playback for the user.
|
|
||||||
*
|
|
||||||
* @param user the user to start playback for
|
|
||||||
* @return if the playback was paused
|
|
||||||
*/
|
|
||||||
@SneakyThrows
|
|
||||||
public boolean resumePlayback(BatUser user) {
|
|
||||||
try {
|
|
||||||
getSpotifyApi(user).startResumeUsersPlayback().build().execute();
|
|
||||||
return true;
|
|
||||||
} catch (Exception e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 "Missing code param";
|
|
||||||
}
|
|
||||||
AuthorizationCodeCredentials credentials = spotifyApi.authorizationCode(code).build().execute();
|
|
||||||
String key = StringUtils.randomString(16);
|
|
||||||
accessToken.put(key, credentials);
|
|
||||||
return "Authorization Key: " + key;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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());
|
|
||||||
userService.saveUser(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a new Spotify API instance.
|
|
||||||
*
|
|
||||||
* @return the Spotify API
|
|
||||||
*/
|
|
||||||
public SpotifyApi getSpotifyApi(BatUser user) {
|
|
||||||
SpotifyProfile profile = user.getProfile(SpotifyProfile.class);
|
|
||||||
return new SpotifyApi.Builder()
|
|
||||||
.setAccessToken(profile.getAccessToken())
|
|
||||||
.setClientSecret(clientSecret)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
}
|
|
@ -17,9 +17,7 @@ import java.util.concurrent.TimeUnit;
|
|||||||
/**
|
/**
|
||||||
* @author Fascinated (fascinated7)
|
* @author Fascinated (fascinated7)
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service @Log4j2 @Getter
|
||||||
@Log4j2
|
|
||||||
@Getter
|
|
||||||
@DependsOn("discordService")
|
@DependsOn("discordService")
|
||||||
public class UserService {
|
public class UserService {
|
||||||
/**
|
/**
|
||||||
|
@ -1,15 +1,11 @@
|
|||||||
# Discord Configuration
|
|
||||||
discord:
|
discord:
|
||||||
token: "oh my goodnesssssssssss"
|
token: "oh my goodnesssssssssss"
|
||||||
|
|
||||||
# Spotify Configuration
|
|
||||||
spotify:
|
|
||||||
redirect-uri: "http://localhost:8080/spotify/callback"
|
|
||||||
client-id: "spotify-client-id"
|
|
||||||
client-secret: "spotify-client-secret"
|
|
||||||
|
|
||||||
# Spring Configuration
|
# Spring Configuration
|
||||||
spring:
|
spring:
|
||||||
|
# Disable the Spring Web Server
|
||||||
|
main:
|
||||||
|
web-application-type: none
|
||||||
data:
|
data:
|
||||||
# MongoDB Configuration
|
# MongoDB Configuration
|
||||||
mongodb:
|
mongodb:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user