diff --git a/src/main/java/cc/fascinated/bat/Consts.java b/src/main/java/cc/fascinated/bat/Consts.java index 06dca16..0823cc3 100644 --- a/src/main/java/cc/fascinated/bat/Consts.java +++ b/src/main/java/cc/fascinated/bat/Consts.java @@ -6,4 +6,6 @@ package cc.fascinated.bat; public class Consts { public static final String INVITE_URL = "https://discord.com/oauth2/authorize?client_id=1254161119975833652&permissions=8&integration_type=0&scope=bot+applications.commands"; public static final String SUPPORT_INVITE_URL = "https://discord.gg/invite/yjj2U3ctEG"; + public static String BOT_OWNER = "474221560031608833"; + public static String ADMIN_GUILD = "1203163422498361404"; } diff --git a/src/main/java/cc/fascinated/bat/command/Category.java b/src/main/java/cc/fascinated/bat/command/Category.java index 79e66f1..4889c4f 100644 --- a/src/main/java/cc/fascinated/bat/command/Category.java +++ b/src/main/java/cc/fascinated/bat/command/Category.java @@ -9,11 +9,12 @@ import net.dv8tion.jda.api.entities.emoji.Emoji; */ @AllArgsConstructor @Getter public enum Category { - GENERAL(Emoji.fromUnicode("U+2699"), "General"), - FUN(Emoji.fromFormatted("U+1F973"), "Fun"), - SERVER(Emoji.fromFormatted("U+1F5A5"), "Server"), - UTILITY(Emoji.fromFormatted("U+1F6E0"), "Utility"), - BEAT_SABER(Emoji.fromFormatted("U+1FA84"), "Beat Saber"); + GENERAL(Emoji.fromUnicode("U+2699"), "General", false), + FUN(Emoji.fromFormatted("U+1F973"), "Fun", false), + SERVER(Emoji.fromFormatted("U+1F5A5"), "Server", false), + UTILITY(Emoji.fromFormatted("U+1F6E0"), "Utility", false), + BEAT_SABER(Emoji.fromFormatted("U+1FA84"), "Beat Saber", false), + BOT_ADMIN(null, null, true); /** * The emoji for the category @@ -25,6 +26,11 @@ public enum Category { */ private final String name; + /** + * If the category is hidden + */ + private final boolean hidden; + public static Category getByName(String name) { for (Category category : Category.values()) { if (category.getName().equalsIgnoreCase(name)) { diff --git a/src/main/java/cc/fascinated/bat/command/CommandInfo.java b/src/main/java/cc/fascinated/bat/command/CommandInfo.java index 5588589..312e8f9 100644 --- a/src/main/java/cc/fascinated/bat/command/CommandInfo.java +++ b/src/main/java/cc/fascinated/bat/command/CommandInfo.java @@ -47,4 +47,11 @@ public @interface CommandInfo { * @return the category of the command */ Category category() default Category.GENERAL; + + /** + * If the command is bot owner only + * + * @return if the command is bot owner only + */ + boolean botOwnerOnly() default false; } \ No newline at end of file diff --git a/src/main/java/cc/fascinated/bat/command/impl/AvatarCommand.java b/src/main/java/cc/fascinated/bat/command/impl/AvatarCommand.java index 9bcfa4d..49f568b 100644 --- a/src/main/java/cc/fascinated/bat/command/impl/AvatarCommand.java +++ b/src/main/java/cc/fascinated/bat/command/impl/AvatarCommand.java @@ -18,7 +18,7 @@ import org.springframework.stereotype.Component; * @author Fascinated (fascinated7) */ @Component -@CommandInfo(name = "avatar", description = "Get the avatar of a user") +@CommandInfo(name = "avatar", description = "Get the avatar of a user", guildOnly = false) public class AvatarCommand extends BatCommand { public AvatarCommand() { super.addOption(OptionType.USER, "user", "The user to get the avatar of", true); diff --git a/src/main/java/cc/fascinated/bat/command/impl/BotStatsCommand.java b/src/main/java/cc/fascinated/bat/command/impl/BotStatsCommand.java index 5725a6e..e96f0c9 100644 --- a/src/main/java/cc/fascinated/bat/command/impl/BotStatsCommand.java +++ b/src/main/java/cc/fascinated/bat/command/impl/BotStatsCommand.java @@ -24,7 +24,7 @@ import java.lang.management.RuntimeMXBean; * @author Fascinated (fascinated7) */ @Component -@CommandInfo(name = "botstats", description = "Shows the bot statistics") +@CommandInfo(name = "botstats", description = "Shows the bot statistics", guildOnly = false) public class BotStatsCommand extends BatCommand { RuntimeMXBean bean = ManagementFactory.getRuntimeMXBean(); private final GuildService guildService; diff --git a/src/main/java/cc/fascinated/bat/command/impl/HelpCommand.java b/src/main/java/cc/fascinated/bat/command/impl/HelpCommand.java index 8dfc504..db8e880 100644 --- a/src/main/java/cc/fascinated/bat/command/impl/HelpCommand.java +++ b/src/main/java/cc/fascinated/bat/command/impl/HelpCommand.java @@ -36,7 +36,7 @@ import java.util.Map; * @author Fascinated (fascinated7) */ @Component -@CommandInfo(name = "help", description = "View the bots command categories.") +@CommandInfo(name = "help", description = "View the bots command categories.", guildOnly = false) public class HelpCommand extends BatCommand implements EventListener { private final CommandService commandService; @@ -65,7 +65,7 @@ public class HelpCommand extends BatCommand implements EventListener { } String commands = ""; - List categoryCommands = commandService.getCommandsByCategory(category); + List categoryCommands = commandService.getCommandsByCategory(category, true); if (categoryCommands.isEmpty()) { commands = "No commands available in this category."; } else { @@ -111,7 +111,7 @@ public class HelpCommand extends BatCommand implements EventListener { private MessageEmbed createHomeEmbed() { String categories = ""; for (Category category : Category.values()) { - long commandCount = commandService.getCommandsByCategory(category).size(); + long commandCount = commandService.getCommandsByCategory(category, true).size(); categories += "➜ %s - **%s Command%s**\n".formatted( category.getName(), commandCount, diff --git a/src/main/java/cc/fascinated/bat/command/impl/InviteCommand.java b/src/main/java/cc/fascinated/bat/command/impl/InviteCommand.java index addc71a..d9c42aa 100644 --- a/src/main/java/cc/fascinated/bat/command/impl/InviteCommand.java +++ b/src/main/java/cc/fascinated/bat/command/impl/InviteCommand.java @@ -16,7 +16,7 @@ import org.springframework.stereotype.Component; * @author Fascinated (fascinated7) */ @Component -@CommandInfo(name = "invite", description = "Invite the bot to your server!") +@CommandInfo(name = "invite", description = "Invite the bot to your server!", guildOnly = false) public class InviteCommand extends BatCommand { @Override public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction interaction) { diff --git a/src/main/java/cc/fascinated/bat/command/impl/PingCommand.java b/src/main/java/cc/fascinated/bat/command/impl/PingCommand.java index 889f24d..55be0a4 100644 --- a/src/main/java/cc/fascinated/bat/command/impl/PingCommand.java +++ b/src/main/java/cc/fascinated/bat/command/impl/PingCommand.java @@ -15,7 +15,7 @@ import org.springframework.stereotype.Component; * @author Fascinated (fascinated7) */ @Component -@CommandInfo(name = "ping", description = "Gets the ping of the bot") +@CommandInfo(name = "ping", description = "Gets the ping of the bot", guildOnly = false) public class PingCommand extends BatCommand { @Override public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction interaction) { diff --git a/src/main/java/cc/fascinated/bat/command/impl/botadmin/premium/PremiumAdminCommand.java b/src/main/java/cc/fascinated/bat/command/impl/botadmin/premium/PremiumAdminCommand.java new file mode 100644 index 0000000..dc9b2cb --- /dev/null +++ b/src/main/java/cc/fascinated/bat/command/impl/botadmin/premium/PremiumAdminCommand.java @@ -0,0 +1,21 @@ +package cc.fascinated.bat.command.impl.botadmin.premium; + +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 = "premiumadmin", description = "Set a guild as premium", botOwnerOnly = true) +public class PremiumAdminCommand extends BatCommand { + @Autowired + public PremiumAdminCommand(@NonNull ApplicationContext context) { + super.addSubCommand(context.getBean(SetSubCommand.class)); + super.addSubCommand(context.getBean(RemoveSubCommand.class)); + } +} diff --git a/src/main/java/cc/fascinated/bat/command/impl/botadmin/premium/RemoveSubCommand.java b/src/main/java/cc/fascinated/bat/command/impl/botadmin/premium/RemoveSubCommand.java new file mode 100644 index 0000000..e78cdee --- /dev/null +++ b/src/main/java/cc/fascinated/bat/command/impl/botadmin/premium/RemoveSubCommand.java @@ -0,0 +1,54 @@ +package cc.fascinated.bat.command.impl.botadmin.premium; + +import cc.fascinated.bat.command.BatSubCommand; +import cc.fascinated.bat.command.CommandInfo; +import cc.fascinated.bat.model.BatGuild; +import cc.fascinated.bat.model.BatUser; +import cc.fascinated.bat.service.GuildService; +import lombok.NonNull; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; +import net.dv8tion.jda.api.interactions.commands.OptionMapping; +import net.dv8tion.jda.api.interactions.commands.OptionType; +import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * @author Fascinated (fascinated7) + */ +@Component +@CommandInfo(name = "remove", description = "Remove premium from a guild") +public class RemoveSubCommand extends BatSubCommand { + private final GuildService guildService; + + @Autowired + public RemoveSubCommand(GuildService guildService) { + this.guildService = guildService; + super.addOption(OptionType.STRING, "guild", "The guild id to set as premium", true); + } + + @Override + public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction interaction) { + OptionMapping guildOption = interaction.getOption("guild"); + if (guildOption == null) { + interaction.reply("Please provide a guild id").queue(); + return; + } + String guildId = guildOption.getAsString(); + BatGuild batGuild = guildService.getGuild(guildId); + if (batGuild == null) { + interaction.reply("The guild with the id %s does not exist".formatted(guildId)).queue(); + return; + } + BatGuild.Premium premium = batGuild.getPremium(); + if (!premium.hasPremium()) { + interaction.reply("The guild does not have premium").queue(); + return; + } + + premium.removePremium(); + guildService.saveGuild(batGuild); + interaction.reply("The guild **%s** has had its premium removed".formatted(guild.getName())).queue(); + } +} diff --git a/src/main/java/cc/fascinated/bat/command/impl/botadmin/premium/SetSubCommand.java b/src/main/java/cc/fascinated/bat/command/impl/botadmin/premium/SetSubCommand.java new file mode 100644 index 0000000..4563eac --- /dev/null +++ b/src/main/java/cc/fascinated/bat/command/impl/botadmin/premium/SetSubCommand.java @@ -0,0 +1,65 @@ +package cc.fascinated.bat.command.impl.botadmin.premium; + +import cc.fascinated.bat.command.BatSubCommand; +import cc.fascinated.bat.command.CommandInfo; +import cc.fascinated.bat.model.BatGuild; +import cc.fascinated.bat.model.BatUser; +import cc.fascinated.bat.service.GuildService; +import lombok.NonNull; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; +import net.dv8tion.jda.api.interactions.commands.OptionMapping; +import net.dv8tion.jda.api.interactions.commands.OptionType; +import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * @author Fascinated (fascinated7) + */ +@Component +@CommandInfo(name = "set", description = "Adds premium to a guild") +public class SetSubCommand extends BatSubCommand { + private final GuildService guildService; + + @Autowired + public SetSubCommand(GuildService guildService) { + this.guildService = guildService; + super.addOption(OptionType.STRING, "guild", "The guild id to set as premium", true); + super.addOption(OptionType.BOOLEAN, "infinite", "Whether the premium length should be infinite", true); + } + + @Override + public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction interaction) { + OptionMapping guildOption = interaction.getOption("guild"); + if (guildOption == null) { + interaction.reply("Please provide a guild id").queue(); + return; + } + String guildId = guildOption.getAsString(); + OptionMapping infiniteOption = interaction.getOption("infinite"); + if (infiniteOption == null) { + interaction.reply("Please provide whether the premium length should be infinite").queue(); + return; + } + + boolean infinite = infiniteOption.getAsBoolean(); + BatGuild batGuild = guildService.getGuild(guildId); + if (batGuild == null) { + interaction.reply("The guild with the id %s does not exist".formatted(guildId)).queue(); + return; + } + BatGuild.Premium premium = batGuild.getPremium(); + if (!infinite) { + premium.addTime(); + } else { + premium.addInfiniteTime(); + } + guildService.saveGuild(batGuild); + if (!infinite) { + interaction.reply("The guild **%s** has been set as premium until ".formatted(guild.getName(), premium.getExpiresAt().toInstant().toEpochMilli()/1000)).queue(); + } else { + interaction.reply("The guild **%s** has been set as premium indefinitely".formatted(guild.getName())).queue(); + } + } +} diff --git a/src/main/java/cc/fascinated/bat/command/impl/fun/CatCommand.java b/src/main/java/cc/fascinated/bat/command/impl/fun/CatCommand.java index a64a85b..cdd4192 100644 --- a/src/main/java/cc/fascinated/bat/command/impl/fun/CatCommand.java +++ b/src/main/java/cc/fascinated/bat/command/impl/fun/CatCommand.java @@ -18,7 +18,7 @@ import org.springframework.stereotype.Component; * @author Fascinated (fascinated7) */ @Component -@CommandInfo(name = "cat", description = "Get a random cat image", category = Category.FUN) +@CommandInfo(name = "cat", description = "Get a random cat image", category = Category.FUN, guildOnly = false) public class CatCommand extends BatCommand { @Override public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction interaction) { diff --git a/src/main/java/cc/fascinated/bat/command/impl/fun/DogCommand.java b/src/main/java/cc/fascinated/bat/command/impl/fun/DogCommand.java index 8cfa9f7..0e2fe74 100644 --- a/src/main/java/cc/fascinated/bat/command/impl/fun/DogCommand.java +++ b/src/main/java/cc/fascinated/bat/command/impl/fun/DogCommand.java @@ -18,7 +18,7 @@ import org.springframework.stereotype.Component; * @author Fascinated (fascinated7) */ @Component -@CommandInfo(name = "dog", description = "Get a random dog image", category = Category.FUN) +@CommandInfo(name = "dog", description = "Get a random dog image", category = Category.FUN, guildOnly = false) public class DogCommand extends BatCommand { @Override public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction interaction) { diff --git a/src/main/java/cc/fascinated/bat/command/impl/server/PremiumCommand.java b/src/main/java/cc/fascinated/bat/command/impl/server/PremiumCommand.java new file mode 100644 index 0000000..3b6f13d --- /dev/null +++ b/src/main/java/cc/fascinated/bat/command/impl/server/PremiumCommand.java @@ -0,0 +1,38 @@ +package cc.fascinated.bat.command.impl.server; + +import cc.fascinated.bat.command.BatCommand; +import cc.fascinated.bat.command.CommandInfo; +import cc.fascinated.bat.common.EmbedUtils; +import cc.fascinated.bat.common.TimeUtils; +import cc.fascinated.bat.model.BatGuild; +import cc.fascinated.bat.model.BatUser; +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.MessageEmbed; +import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; +import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction; +import org.springframework.stereotype.Component; + +/** + * @author Fascinated (fascinated7) + */ +@Component +@CommandInfo(name = "premium", description = "View the premium information for the guild", requiredPermissions = Permission.ADMINISTRATOR) +public class PremiumCommand extends BatCommand { + @Override + public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction interaction) { + BatGuild.Premium premium = guild.getPremium(); + EmbedBuilder embed = EmbedUtils.genericEmbed().setAuthor("Premium Information"); + if (premium.hasPremium()) { + embed.addField("Premium", premium.hasPremium() ? "Yes" : "No", true); + embed.addField("Started On", "".formatted(premium.getActivatedAt().toInstant().toEpochMilli()/1000), true); + embed.addField("Expires At", premium.isInfinite() ? "Never" : "" + .formatted(premium.getExpiresAt().toInstant().toEpochMilli()/1000), true); + } else { + embed.setDescription("The guild does not have premium"); + } + interaction.replyEmbeds(embed.build()).queue(); + } +} diff --git a/src/main/java/cc/fascinated/bat/common/ProfileHolder.java b/src/main/java/cc/fascinated/bat/common/ProfileHolder.java index 6a38aef..33aaa5b 100644 --- a/src/main/java/cc/fascinated/bat/common/ProfileHolder.java +++ b/src/main/java/cc/fascinated/bat/common/ProfileHolder.java @@ -22,7 +22,7 @@ public class ProfileHolder { * @param The type of the profile * @return The profile */ - public T getProfile(Class clazz) { + public T getProfile(Class clazz) { if (profiles == null) { profiles = new HashMap<>(); } @@ -36,6 +36,6 @@ public class ProfileHolder { e.printStackTrace(); } } - return (T) profile; + return clazz.cast(profile); } } diff --git a/src/main/java/cc/fascinated/bat/features/autorole/command/AddSubCommand.java b/src/main/java/cc/fascinated/bat/features/autorole/command/AddSubCommand.java index c7f4d90..07600cf 100644 --- a/src/main/java/cc/fascinated/bat/features/autorole/command/AddSubCommand.java +++ b/src/main/java/cc/fascinated/bat/features/autorole/command/AddSubCommand.java @@ -36,10 +36,11 @@ public class AddSubCommand extends BatSubCommand { public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction interaction) { AutoRoleProfile profile = guild.getProfile(AutoRoleProfile.class); // Check if the guild has reached the maximum auto roles count - if (profile.getRoleSlotsInUse() >= profile.getMaxRoles()) { + int maxRoleSlots = AutoRoleProfile.getMaxRoleSlots(guild); + if (profile.getRoleSlotsInUse() >= maxRoleSlots) { interaction.replyEmbeds(EmbedUtils.errorEmbed() - .setDescription("You can only have a maximum of %d roles set for the auto role feature" - .formatted(profile.getMaxRoles())) + .setDescription("The guild can only have a maximum of %d auto roles" + .formatted(maxRoleSlots)) .build()).queue(); return; } @@ -81,7 +82,7 @@ public class AddSubCommand extends BatSubCommand { profile.addRole(role.getId()); guildService.saveGuild(guild); interaction.replyEmbeds(EmbedUtils.successEmbed() - .setDescription("Successfully added the %s role to the auto roles list".formatted(role.getAsMention())) + .setDescription("You have added %s to the auto roles list".formatted(role.getAsMention())) .build()).queue(); } } diff --git a/src/main/java/cc/fascinated/bat/features/autorole/command/ListSubCommand.java b/src/main/java/cc/fascinated/bat/features/autorole/command/ListSubCommand.java index 29ae7f3..f6050a6 100644 --- a/src/main/java/cc/fascinated/bat/features/autorole/command/ListSubCommand.java +++ b/src/main/java/cc/fascinated/bat/features/autorole/command/ListSubCommand.java @@ -32,7 +32,7 @@ public class ListSubCommand extends BatSubCommand { StringBuilder roles = new StringBuilder(); roles.append("There are %d/%d auto roles\n".formatted( profile.getRoleSlotsInUse(), - profile.getMaxRoles() + AutoRoleProfile.getMaxRoleSlots(guild) )); for (int i = 0; i < profile.getRoles().size(); i++) { roles.append("%d. %s\n".formatted(i + 1, profile.getRoles().get(i).getAsMention())); diff --git a/src/main/java/cc/fascinated/bat/features/autorole/profile/AutoRoleProfile.java b/src/main/java/cc/fascinated/bat/features/autorole/profile/AutoRoleProfile.java index 508d767..d7e4544 100644 --- a/src/main/java/cc/fascinated/bat/features/autorole/profile/AutoRoleProfile.java +++ b/src/main/java/cc/fascinated/bat/features/autorole/profile/AutoRoleProfile.java @@ -1,6 +1,7 @@ package cc.fascinated.bat.features.autorole.profile; import cc.fascinated.bat.common.Profile; +import cc.fascinated.bat.model.BatGuild; import cc.fascinated.bat.service.DiscordService; import lombok.Getter; import lombok.Setter; @@ -15,30 +16,17 @@ import java.util.List; @Setter @Getter public class AutoRoleProfile extends Profile { private static final int DEFAULT_MAX_ROLES = 10; + private static final int PREMIUM_MAX_ROLES = 25; /** * The roles to assign when a user joins */ private List roleIds; - /** - * The maximum amount of roles that can be set - */ - private int maxRoles = 10; - public AutoRoleProfile() { super("auto-role"); } - /** - * Gets the amount of role slots left - * - * @return the amount - */ - public int getRoleSlotsLeft() { - return maxRoles - getRoles().size(); - } - /** * Gets the amount of role slots in use * @@ -104,9 +92,21 @@ public class AutoRoleProfile extends Profile { 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 public void reset() { roleIds.clear(); - maxRoles = DEFAULT_MAX_ROLES; } } diff --git a/src/main/java/cc/fascinated/bat/features/scoresaber/command/scoresaber/ScoreSaberCommand.java b/src/main/java/cc/fascinated/bat/features/scoresaber/command/scoresaber/ScoreSaberCommand.java index 7a8393a..3a084e8 100644 --- a/src/main/java/cc/fascinated/bat/features/scoresaber/command/scoresaber/ScoreSaberCommand.java +++ b/src/main/java/cc/fascinated/bat/features/scoresaber/command/scoresaber/ScoreSaberCommand.java @@ -27,7 +27,7 @@ import java.time.LocalDateTime; * @author Fascinated (fascinated7) */ @Component -@CommandInfo(name = "scoresaber", description = "General ScoreSaber commands") +@CommandInfo(name = "scoresaber", description = "General ScoreSaber commands", guildOnly = false) public class ScoreSaberCommand extends BatCommand { private final ScoreSaberService scoreSaberService; diff --git a/src/main/java/cc/fascinated/bat/model/BatGuild.java b/src/main/java/cc/fascinated/bat/model/BatGuild.java index c8035e4..fe8b769 100644 --- a/src/main/java/cc/fascinated/bat/model/BatGuild.java +++ b/src/main/java/cc/fascinated/bat/model/BatGuild.java @@ -2,14 +2,14 @@ package cc.fascinated.bat.model; import cc.fascinated.bat.common.ProfileHolder; import cc.fascinated.bat.service.DiscordService; -import lombok.Getter; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; -import lombok.Setter; +import jakarta.annotation.PostConstruct; +import lombok.*; import net.dv8tion.jda.api.entities.Guild; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; +import org.springframework.stereotype.Component; +import java.util.Calendar; import java.util.Date; /** @@ -29,6 +29,32 @@ public class BatGuild extends ProfileHolder { */ private Date createdAt = new Date(); + /** + * The premium information for the guild + */ + private Premium premium; + + /** + * The premium information for the guild + * + * @return the premium information + */ + public Premium getPremium() { + if (this.premium == null) { + this.premium = new Premium(null, null, null); + } + return this.premium; + } + + /** + * Gets the name of the guild + * + * @return the name + */ + public String getName() { + return getDiscordGuild().getName(); + } + /** * Gets the guild as the JDA Guild * @@ -37,4 +63,90 @@ public class BatGuild extends ProfileHolder { public Guild getDiscordGuild() { return DiscordService.JDA.getGuildById(id); } + + @AllArgsConstructor @Getter @Setter + public static class Premium { + /** + * The time the premium was activated + */ + private Date activatedAt; + + /** + * The time the premium expires + */ + private Date expiresAt; + + /** + * The type of premium + */ + private Type type; + + /** + * Checks if the guild has premium + * + * @return whether the guild has premium + */ + public boolean hasPremium() { + return this.type == Type.INFINITE || (this.expiresAt != null && this.expiresAt.after(new Date())); + } + + /** + * Adds a month to the premium time + */ + public void addTime(int months) { + if (this.type == null) { // If the type is null, set it to monthly + this.type = Type.MONTHLY; + } + if (this.expiresAt == null) { + this.expiresAt = new Date(); + } + Calendar calendar = Calendar.getInstance(); + calendar.setTime(new Date()); + calendar.add(Calendar.MONTH, months); + this.expiresAt = calendar.getTime(); + this.type = Type.MONTHLY; + } + + /** + * Adds a month to the premium time + */ + public void addTime() { + addTime(1); + } + + /** + * Adds infinite time to the premium + */ + public void addInfiniteTime() { + this.type = Type.INFINITE; + this.expiresAt = null; + this.activatedAt = new Date(); + } + + /** + * Removes the premium from the guild + */ + public void removePremium() { + this.activatedAt = null; + this.expiresAt = null; + this.type = null; + } + + /** + * Checks if the premium is infinite + * + * @return whether the premium is infinite + */ + public boolean isInfinite() { + return this.type == Type.INFINITE; + } + + /** + * The premium type for the guild + */ + public enum Type { + INFINITE, + MONTHLY + } + } } diff --git a/src/main/java/cc/fascinated/bat/service/CommandService.java b/src/main/java/cc/fascinated/bat/service/CommandService.java index 72a7b2e0..636d577 100644 --- a/src/main/java/cc/fascinated/bat/service/CommandService.java +++ b/src/main/java/cc/fascinated/bat/service/CommandService.java @@ -1,5 +1,6 @@ package cc.fascinated.bat.service; +import cc.fascinated.bat.Consts; import cc.fascinated.bat.command.BatCommand; import cc.fascinated.bat.command.BatCommandExecutor; import cc.fascinated.bat.command.BatSubCommand; @@ -77,6 +78,12 @@ public class CommandService extends ListenerAdapter { // Unregister all commands that Discord has but we don't jda.retrieveCommands().complete().forEach(command -> { + if (commands.containsKey(command.getName()) && commands.get(command.getName()).getCommandInfo().category().isHidden()) { + jda.deleteCommandById(command.getId()).complete(); // Unregister the command on Discord + log.info("Unregistered hidden command \"{}\" from Discord", command.getName()); + return; + } + if (commands.containsKey(command.getName())) { return; } @@ -86,7 +93,9 @@ public class CommandService extends ListenerAdapter { }); // Register all commands - List discordCommands = jda.updateCommands().addCommands(commands.values().stream().map(BatCommand::getCommandData).toList()).complete(); + List discordCommands = jda.updateCommands().addCommands(commands.values().stream() + .filter(command -> !command.getCategory().isHidden()) + .map(BatCommand::getCommandData).toList()).complete(); for (Command discordCommand : discordCommands) { commands.get(discordCommand.getName()).setCommandSnowflake(discordCommand.getIdLong()); if (!discordCommand.getSubcommands().isEmpty()) { @@ -95,6 +104,12 @@ public class CommandService extends ListenerAdapter { } } } + + Objects.requireNonNull(jda.getGuildById(Consts.ADMIN_GUILD), "Admin guild is null!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") + .updateCommands().addCommands(commands.values().stream() + .filter(command -> command.getCategory().isHidden()) + .map(BatCommand::getCommandData).toList()).complete(); + log.info("Registered all slash commands in {}ms", System.currentTimeMillis() - before); } @@ -104,8 +119,8 @@ public class CommandService extends ListenerAdapter { * @param category The category * @return The commands */ - public List getCommandsByCategory(Category category) { - return commands.values().stream().filter(command -> command.getCategory() == category).toList(); + public List getCommandsByCategory(Category category, boolean hideHiddenCategories) { + return commands.values().stream().filter(command -> command.getCategory() == category && (hideHiddenCategories && category.isHidden())).toList(); } @Override @@ -124,6 +139,13 @@ public class CommandService extends ListenerAdapter { BatGuild guild = ranInsideGuild ? guildService.getGuild(discordGuild.getId()) : null; BatUser user = userService.getUser(event.getUser().getId()); + if (command.getCommandInfo().botOwnerOnly() && !user.getId().equalsIgnoreCase(Consts.BOT_OWNER)) { + event.replyEmbeds(EmbedUtils.errorEmbed() + .setDescription("You do not have permission to execute this command") + .build()).setEphemeral(true).queue(); + return; + } + try { BatCommandExecutor executor = null; List requiredPermissions = new ArrayList<>(); diff --git a/src/main/java/cc/fascinated/bat/service/GuildService.java b/src/main/java/cc/fascinated/bat/service/GuildService.java index ee1cb00..1b322a6 100644 --- a/src/main/java/cc/fascinated/bat/service/GuildService.java +++ b/src/main/java/cc/fascinated/bat/service/GuildService.java @@ -10,6 +10,7 @@ import net.dv8tion.jda.api.events.guild.GuildJoinEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.DependsOn; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import java.util.*; @@ -36,6 +37,18 @@ public class GuildService extends ListenerAdapter { DiscordService.JDA.addEventListener(this); } + @Scheduled(cron = "0 0 0 * * *") + private void validatePremiumStatus() { + for (BatGuild guild : guilds.values()) { + BatGuild.Premium premium = guild.getPremium(); + if (premium.getExpiresAt() != null && premium.getExpiresAt().before(new Date())) { + premium.removePremium(); + guildRepository.save(guild); + log.info("Removed premium status from guild \"{}\"", guild.getId()); + } + } + } + /** * Gets a guild by its ID *