diff --git a/src/main/java/cc/fascinated/bat/features/minecraft/MinecraftFeature.java b/src/main/java/cc/fascinated/bat/features/minecraft/MinecraftFeature.java index dba7acd..d96c505 100644 --- a/src/main/java/cc/fascinated/bat/features/minecraft/MinecraftFeature.java +++ b/src/main/java/cc/fascinated/bat/features/minecraft/MinecraftFeature.java @@ -2,11 +2,17 @@ package cc.fascinated.bat.features.minecraft; import cc.fascinated.bat.features.Feature; import cc.fascinated.bat.features.FeatureProfile; -import cc.fascinated.bat.features.minecraft.command.MinecraftCommand; +import cc.fascinated.bat.features.minecraft.command.minecraft.MinecraftCommand; +import cc.fascinated.bat.features.minecraft.command.serverwatcher.ServerWatcherCommand; +import cc.fascinated.bat.model.BatGuild; import cc.fascinated.bat.service.CommandService; +import cc.fascinated.bat.service.DiscordService; +import cc.fascinated.bat.service.GuildService; import lombok.NonNull; +import net.dv8tion.jda.api.entities.Guild; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; /** @@ -14,10 +20,25 @@ import org.springframework.stereotype.Component; */ @Component public class MinecraftFeature extends Feature { + private final GuildService guildService; + @Autowired - public MinecraftFeature(@NonNull ApplicationContext context, @NonNull CommandService commandService) { + public MinecraftFeature(@NonNull ApplicationContext context, @NonNull CommandService commandService, @NonNull GuildService guildService) { super("Minecraft", FeatureProfile.FeatureState.DISABLED, true); + this.guildService = guildService; super.registerCommand(commandService, context.getBean(MinecraftCommand.class)); + super.registerCommand(commandService, context.getBean(ServerWatcherCommand.class)); + } + + /** + * Check servers every minute + */ + @Scheduled(cron = "0 * * * * *") + public void checkServers() { + for (Guild guild : DiscordService.JDA.getGuilds()) { + BatGuild batGuild = guildService.getGuild(guild.getId()); + batGuild.getMinecraftProfile().checkServers(); + } } } diff --git a/src/main/java/cc/fascinated/bat/features/minecraft/MinecraftProfile.java b/src/main/java/cc/fascinated/bat/features/minecraft/MinecraftProfile.java new file mode 100644 index 0000000..47f3e87 --- /dev/null +++ b/src/main/java/cc/fascinated/bat/features/minecraft/MinecraftProfile.java @@ -0,0 +1,143 @@ +package cc.fascinated.bat.features.minecraft; + +import cc.fascinated.bat.Emojis; +import cc.fascinated.bat.common.*; +import com.google.gson.Gson; +import lombok.Getter; +import net.dv8tion.jda.api.EmbedBuilder; +import org.bson.Document; +import xyz.mcutils.McUtilsAPI; +import xyz.mcutils.models.server.MinecraftServer; +import xyz.mcutils.models.server.ServerPlatform; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Fascinated (fascinated7) + */ +@Getter +public class MinecraftProfile extends Serializable { + /** + * The servers that are getting their status watched + */ + private final List serverWatchers = new ArrayList<>(); + + /** + * Adds a server watcher + * + * @param serverWatcher - The server watcher to add + */ + public void addServerWatcher(ServerWatcher serverWatcher) { + serverWatchers.add(serverWatcher); + } + + /** + * Removes a server watcher + * + * @param serverWatcher - The server watcher to remove + */ + public void removeServerWatcher(ServerWatcher serverWatcher) { + serverWatchers.remove(serverWatcher); + } + + /** + * Gets a server watcher by hostname + * + * @param hostname the hostname of the server + * @param platform the platform of the server + * @return the server watcher + */ + public ServerWatcher getServerWatcher(String hostname, ServerPlatform platform) { + for (ServerWatcher serverWatcher : serverWatchers) { + if (serverWatcher.getHostname().equalsIgnoreCase(hostname) && serverWatcher.getPlatform() == platform) { + return serverWatcher; + } + } + return null; + } + + public void checkServers() { + for (ServerWatcher server : serverWatchers) { + int platformDefaultPort = server.getPlatform() == ServerPlatform.JAVA ? 25565 : 19132; + String hostname = server.getHostname() + (server.getPort() != platformDefaultPort ? ":" + server.getPort() : ""); + boolean isOnline = true; + + MinecraftServer minecraftServer = null; + switch (server.getPlatform()) { + case JAVA -> { + try { + minecraftServer = McUtilsAPI.getJavaServer(hostname); + } catch (Exception e) { + isOnline = false; + } + } + case BEDROCK -> { + try { + minecraftServer = McUtilsAPI.getBedrockServer(hostname); + } catch (Exception e) { + isOnline = false; + } + } + } + + if (isOnline == server.isLastState()) { + return; + } + server.setLastState(isOnline); + EmbedBuilder embedBuilder = isOnline ? EmbedUtils.successEmbed() : EmbedUtils.errorEmbed(); + EmbedDescriptionBuilder description = new EmbedDescriptionBuilder("Server Watcher"); + description.appendLine("%s %s server `%s` is now **%s**".formatted( + isOnline ? Emojis.CHECK_MARK_EMOJI : Emojis.CROSS_MARK_EMOJI, + EnumUtils.getEnumName(server.getPlatform()), + hostname, + isOnline ? "online" : "offline" + ), false); + if (minecraftServer != null) { + description.appendLine("Players: `%s/%s`".formatted( + NumberFormatter.simpleFormat(minecraftServer.getPlayers().getOnline()), + NumberFormatter.simpleFormat(minecraftServer.getPlayers().getMax()) + ), true); + } + server.getChannel().sendMessageEmbeds(embedBuilder + .setDescription(description.build()) + .setThumbnail("https://api.mcutils.xyz/server/icon/%s".formatted(hostname)) + .build()).queue(); + } + } + + @Override + public void load(Document document, Gson gson) { + for (Document watcherDocument : document.getList("serverWatchers", Document.class, new ArrayList<>())) { + serverWatchers.add(new ServerWatcher( + watcherDocument.getString("hostname"), + watcherDocument.getInteger("port"), + ServerPlatform.valueOf(watcherDocument.getString("platform")), + watcherDocument.getString("channelId"), + watcherDocument.getBoolean("lastState", false) + )); + } + } + + @Override + public Document serialize(Gson gson) { + Document document = new Document(); + List watcherDocuments = new ArrayList<>(); + for (ServerWatcher serverWatcher : serverWatchers) { + Document watcherDocument = new Document(); + watcherDocument.append("hostname", serverWatcher.getHostname()); + watcherDocument.append("port", serverWatcher.getPort()); + watcherDocument.append("platform", serverWatcher.getPlatform().name()); + watcherDocument.append("channelId", serverWatcher.getChannelId()); + watcherDocument.append("lastState", serverWatcher.isLastState()); + watcherDocuments.add(watcherDocument); + } + document.append("serverWatchers", watcherDocuments); + return document; + } + + @Override + public void reset() { + serverWatchers.clear(); + } +} diff --git a/src/main/java/cc/fascinated/bat/features/minecraft/ServerWatcher.java b/src/main/java/cc/fascinated/bat/features/minecraft/ServerWatcher.java new file mode 100644 index 0000000..efe84f4 --- /dev/null +++ b/src/main/java/cc/fascinated/bat/features/minecraft/ServerWatcher.java @@ -0,0 +1,54 @@ +package cc.fascinated.bat.features.minecraft; + +import cc.fascinated.bat.common.ChannelUtils; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; +import xyz.mcutils.models.server.ServerPlatform; + +/** + * @author Fascinated (fascinated7) + */ +@AllArgsConstructor +@Getter +@Setter +public class ServerWatcher { + /** + * The hostname of the server + */ + private final String hostname; + + /** + * The port of the server + */ + private final int port; + + /** + * The platform of the server + */ + private final ServerPlatform platform; + + /** + * The channel id to send notifications in + */ + private final String channelId; + + /** + * The last state of the server + *

+ * true = online + * false = offline + *

+ */ + private boolean lastState; + + /** + * Gets the channel + * + * @return - The channel + */ + public TextChannel getChannel() { + return ChannelUtils.getTextChannel(channelId); + } +} diff --git a/src/main/java/cc/fascinated/bat/features/minecraft/command/LookupPlayerCommand.java b/src/main/java/cc/fascinated/bat/features/minecraft/command/minecraft/LookupPlayerSubCommand.java similarity index 94% rename from src/main/java/cc/fascinated/bat/features/minecraft/command/LookupPlayerCommand.java rename to src/main/java/cc/fascinated/bat/features/minecraft/command/minecraft/LookupPlayerSubCommand.java index 5ba6f36..2f4905f 100644 --- a/src/main/java/cc/fascinated/bat/features/minecraft/command/LookupPlayerCommand.java +++ b/src/main/java/cc/fascinated/bat/features/minecraft/command/minecraft/LookupPlayerSubCommand.java @@ -1,4 +1,4 @@ -package cc.fascinated.bat.features.minecraft.command; +package cc.fascinated.bat.features.minecraft.command.minecraft; import cc.fascinated.bat.command.BatCommand; import cc.fascinated.bat.command.CommandInfo; @@ -26,8 +26,8 @@ import xyz.mcutils.models.player.Skin; name = "lookup-player", description = "Lookup a Minecraft player" ) -public class LookupPlayerCommand extends BatCommand { - public LookupPlayerCommand() { +public class LookupPlayerSubCommand extends BatCommand { + public LookupPlayerSubCommand() { super.addOptions( new OptionData(OptionType.STRING, "player", "The player to lookup", true) ); diff --git a/src/main/java/cc/fascinated/bat/features/minecraft/command/LookupServerCommand.java b/src/main/java/cc/fascinated/bat/features/minecraft/command/minecraft/LookupServerSubCommand.java similarity index 96% rename from src/main/java/cc/fascinated/bat/features/minecraft/command/LookupServerCommand.java rename to src/main/java/cc/fascinated/bat/features/minecraft/command/minecraft/LookupServerSubCommand.java index 2ca253c..d9323b2 100644 --- a/src/main/java/cc/fascinated/bat/features/minecraft/command/LookupServerCommand.java +++ b/src/main/java/cc/fascinated/bat/features/minecraft/command/minecraft/LookupServerSubCommand.java @@ -1,4 +1,4 @@ -package cc.fascinated.bat.features.minecraft.command; +package cc.fascinated.bat.features.minecraft.command.minecraft; import cc.fascinated.bat.command.BatCommand; import cc.fascinated.bat.command.CommandInfo; @@ -27,8 +27,8 @@ import xyz.mcutils.models.server.MinecraftServer; name = "lookup-server", description = "Lookup a Minecraft server" ) -public class LookupServerCommand extends BatCommand { - public LookupServerCommand() { +public class LookupServerSubCommand extends BatCommand { + public LookupServerSubCommand() { super.addOptions( new OptionData(OptionType.STRING, "platform", "The platform of the server to lookup", true) .addChoice("Java", "java") diff --git a/src/main/java/cc/fascinated/bat/features/minecraft/command/MinecraftCommand.java b/src/main/java/cc/fascinated/bat/features/minecraft/command/minecraft/MinecraftCommand.java similarity index 67% rename from src/main/java/cc/fascinated/bat/features/minecraft/command/MinecraftCommand.java rename to src/main/java/cc/fascinated/bat/features/minecraft/command/minecraft/MinecraftCommand.java index f7d6a07..d77c2ac 100644 --- a/src/main/java/cc/fascinated/bat/features/minecraft/command/MinecraftCommand.java +++ b/src/main/java/cc/fascinated/bat/features/minecraft/command/minecraft/MinecraftCommand.java @@ -1,4 +1,4 @@ -package cc.fascinated.bat.features.minecraft.command; +package cc.fascinated.bat.features.minecraft.command.minecraft; import cc.fascinated.bat.command.BatCommand; import cc.fascinated.bat.command.CommandInfo; @@ -13,14 +13,15 @@ import org.springframework.stereotype.Component; @Component @CommandInfo( name = "minecraft", - description = "Minecraft related commands" + description = "Minecraft related commands", + userInstall = true ) public class MinecraftCommand extends BatCommand { @Autowired public MinecraftCommand(@NonNull ApplicationContext context) { super.addSubCommands( - context.getBean(LookupPlayerCommand.class), - context.getBean(LookupServerCommand.class) + context.getBean(LookupPlayerSubCommand.class), + context.getBean(LookupServerSubCommand.class) ); } } diff --git a/src/main/java/cc/fascinated/bat/features/minecraft/command/serverwatcher/AddSubCommand.java b/src/main/java/cc/fascinated/bat/features/minecraft/command/serverwatcher/AddSubCommand.java new file mode 100644 index 0000000..6a3c8c8 --- /dev/null +++ b/src/main/java/cc/fascinated/bat/features/minecraft/command/serverwatcher/AddSubCommand.java @@ -0,0 +1,100 @@ +package cc.fascinated.bat.features.minecraft.command.serverwatcher; + +import cc.fascinated.bat.command.BatCommand; +import cc.fascinated.bat.command.CommandInfo; +import cc.fascinated.bat.common.EmbedUtils; +import cc.fascinated.bat.features.minecraft.MinecraftProfile; +import cc.fascinated.bat.features.minecraft.ServerWatcher; +import cc.fascinated.bat.model.BatGuild; +import cc.fascinated.bat.model.BatUser; +import lombok.NonNull; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; +import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; +import net.dv8tion.jda.api.entities.channel.unions.GuildChannelUnion; +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 net.dv8tion.jda.api.interactions.commands.build.OptionData; +import org.springframework.stereotype.Component; +import xyz.mcutils.McUtilsAPI; +import xyz.mcutils.models.server.MinecraftServer; +import xyz.mcutils.models.server.ServerPlatform; + +/** + * @author Fascinated (fascinated7) + */ +@Component("minecraft-server-watcher.add:sub") +@CommandInfo( + name = "add", + description = "Add a server to the server watcher" +) +public class AddSubCommand extends BatCommand { + public AddSubCommand() { + super.addOptions( + new OptionData(OptionType.CHANNEL, "channel", "The channel to send the server watcher notifications", true), + new OptionData(OptionType.STRING, "platform", "The platform of the server to lookup", true) + .addChoice("Java", "java") + .addChoice("Bedrock", "bedrock"), + new OptionData(OptionType.STRING, "host", "The host/ip of the server to add", true) + ); + } + + @Override + public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction event) { + OptionMapping platformOption = event.getOption("platform"); + OptionMapping hostOption = event.getOption("host"); + OptionMapping channelOption = event.getOption("channel"); + if (platformOption == null || hostOption == null || channelOption == null) { + return; + } + String platform = platformOption.getAsString(); + String host = hostOption.getAsString(); + GuildChannelUnion channelUnion = channelOption.getAsChannel(); + TextChannel textChannel = channelUnion.asTextChannel(); + + MinecraftServer server; + try { + if (platform.equalsIgnoreCase("java")) { + server = McUtilsAPI.getJavaServer(host); + } else { + server = McUtilsAPI.getBedrockServer(host); + } + } catch (Exception ex) { + event.replyEmbeds(EmbedUtils.errorEmbed() + .setDescription("The server `%s` is invalid or offline".formatted(host)) + .build()).queue(); + return; + } + + MinecraftProfile profile = guild.getMinecraftProfile(); + if (profile.getServerWatchers().size() >= 10) { + event.replyEmbeds(EmbedUtils.errorEmbed() + .setDescription("You can only have a maximum of `10` server watchers") + .build()).queue(); + return; + } + + if (profile.getServerWatcher(host, ServerPlatform.valueOf(platform.toUpperCase())) != null) { + event.replyEmbeds(EmbedUtils.errorEmbed() + .setDescription("The server `%s` is already being watched".formatted(host)) + .build()).queue(); + return; + } + + profile.addServerWatcher(new ServerWatcher( + server.getHostname(), + server.getPort(), + ServerPlatform.valueOf(platform.toUpperCase()), + textChannel.getId(), + false + )); + profile.checkServers(); // Force check the servers + + event.replyEmbeds(EmbedUtils.successEmbed() + .setDescription("Setup the server watcher for `%s` in %s".formatted( + server.getHostname(), + textChannel.getAsMention() + )).build()).queue(); + } +} diff --git a/src/main/java/cc/fascinated/bat/features/minecraft/command/serverwatcher/ListSubCommand.java b/src/main/java/cc/fascinated/bat/features/minecraft/command/serverwatcher/ListSubCommand.java new file mode 100644 index 0000000..e42ec6f --- /dev/null +++ b/src/main/java/cc/fascinated/bat/features/minecraft/command/serverwatcher/ListSubCommand.java @@ -0,0 +1,50 @@ +package cc.fascinated.bat.features.minecraft.command.serverwatcher; + +import cc.fascinated.bat.command.BatCommand; +import cc.fascinated.bat.command.CommandInfo; +import cc.fascinated.bat.common.EmbedDescriptionBuilder; +import cc.fascinated.bat.common.EmbedUtils; +import cc.fascinated.bat.common.EnumUtils; +import cc.fascinated.bat.features.minecraft.MinecraftProfile; +import cc.fascinated.bat.features.minecraft.ServerWatcher; +import cc.fascinated.bat.model.BatGuild; +import cc.fascinated.bat.model.BatUser; +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.stereotype.Component; +import xyz.mcutils.models.server.ServerPlatform; + +/** + * @author Fascinated (fascinated7) + */ +@Component("minecraft-server-watcher.list:sub") +@CommandInfo( + name = "list", + description = "Shows a list of all the servers being watched" +) +public class ListSubCommand extends BatCommand { + @Override + public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction event) { + MinecraftProfile profile = guild.getMinecraftProfile(); + + EmbedDescriptionBuilder description = new EmbedDescriptionBuilder("Server Watcher"); + description.appendLine("Here is a list of all the servers being watched", false); + description.emptyLine(); + for (ServerWatcher server : profile.getServerWatchers()) { + int platformDefaultPort = server.getPlatform() == ServerPlatform.JAVA ? 25565 : 19132; + String hostname = server.getHostname() + (server.getPort() != platformDefaultPort ? ":" + server.getPort() : ""); + + description.appendLine("`%s` (%s) - %s".formatted( + hostname, + EnumUtils.getEnumName(server.getPlatform()), + server.getChannel().getAsMention() + ), true); + } + + event.replyEmbeds(EmbedUtils.successEmbed() + .setDescription(description.build()) + .build()).queue(); + } +} diff --git a/src/main/java/cc/fascinated/bat/features/minecraft/command/serverwatcher/RemoveSubCommand.java b/src/main/java/cc/fascinated/bat/features/minecraft/command/serverwatcher/RemoveSubCommand.java new file mode 100644 index 0000000..584c9a1 --- /dev/null +++ b/src/main/java/cc/fascinated/bat/features/minecraft/command/serverwatcher/RemoveSubCommand.java @@ -0,0 +1,62 @@ +package cc.fascinated.bat.features.minecraft.command.serverwatcher; + +import cc.fascinated.bat.command.BatCommand; +import cc.fascinated.bat.command.CommandInfo; +import cc.fascinated.bat.common.EmbedUtils; +import cc.fascinated.bat.features.minecraft.MinecraftProfile; +import cc.fascinated.bat.features.minecraft.ServerWatcher; +import cc.fascinated.bat.model.BatGuild; +import cc.fascinated.bat.model.BatUser; +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 net.dv8tion.jda.api.interactions.commands.build.OptionData; +import org.springframework.stereotype.Component; +import xyz.mcutils.models.server.ServerPlatform; + +/** + * @author Fascinated (fascinated7) + */ +@Component("minecraft-server-watcher.remove:sub") +@CommandInfo( + name = "remove", + description = "Remove a server from the server watcher" +) +public class RemoveSubCommand extends BatCommand { + public RemoveSubCommand() { + super.addOptions( + new OptionData(OptionType.STRING, "platform", "The platform of the server to lookup", true) + .addChoice("Java", "java") + .addChoice("Bedrock", "bedrock"), + new OptionData(OptionType.STRING, "host", "The host/ip of the server to remove", true) + ); + } + + @Override + public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction event) { + OptionMapping platformOption = event.getOption("platform"); + OptionMapping hostOption = event.getOption("host"); + if (platformOption == null || hostOption == null) { + return; + } + String platform = platformOption.getAsString(); + String host = hostOption.getAsString(); + + MinecraftProfile profile = guild.getMinecraftProfile(); + ServerWatcher serverWatcher = profile.getServerWatcher(host, ServerPlatform.valueOf(platform.toUpperCase())); + if (serverWatcher == null) { + event.replyEmbeds(EmbedUtils.errorEmbed() + .setDescription("The server `%s` is not being watched".formatted(host)) + .build()).queue(); + return; + } + + profile.removeServerWatcher(serverWatcher); + event.replyEmbeds(EmbedUtils.successEmbed() + .setDescription("The server `%s` has been removed from the server watcher".formatted(host)) + .build()).queue(); + } +} diff --git a/src/main/java/cc/fascinated/bat/features/minecraft/command/serverwatcher/ServerWatcherCommand.java b/src/main/java/cc/fascinated/bat/features/minecraft/command/serverwatcher/ServerWatcherCommand.java new file mode 100644 index 0000000..faaa166 --- /dev/null +++ b/src/main/java/cc/fascinated/bat/features/minecraft/command/serverwatcher/ServerWatcherCommand.java @@ -0,0 +1,29 @@ +package cc.fascinated.bat.features.minecraft.command.serverwatcher; + +import cc.fascinated.bat.command.BatCommand; +import cc.fascinated.bat.command.CommandInfo; +import lombok.NonNull; +import net.dv8tion.jda.api.Permission; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Component; + +/** + * @author Fascinated (fascinated7) + */ +@Component +@CommandInfo( + name = "minecraft-server-watcher", + description = "Configure the server watcher for Minecraft servers", + requiredPermissions = Permission.MANAGE_SERVER +) +public class ServerWatcherCommand extends BatCommand { + @Autowired + public ServerWatcherCommand(@NonNull ApplicationContext context) { + super.addSubCommands( + context.getBean(AddSubCommand.class), + context.getBean(RemoveSubCommand.class), + context.getBean(ListSubCommand.class) + ); + } +} diff --git a/src/main/java/cc/fascinated/bat/model/BatGuild.java b/src/main/java/cc/fascinated/bat/model/BatGuild.java index ae2a8eb..091edf5 100644 --- a/src/main/java/cc/fascinated/bat/model/BatGuild.java +++ b/src/main/java/cc/fascinated/bat/model/BatGuild.java @@ -9,6 +9,7 @@ import cc.fascinated.bat.features.counter.CounterProfile; import cc.fascinated.bat.features.leveling.LevelingFeature; import cc.fascinated.bat.features.leveling.LevelingProfile; import cc.fascinated.bat.features.logging.LogProfile; +import cc.fascinated.bat.features.minecraft.MinecraftProfile; import cc.fascinated.bat.features.namehistory.profile.guild.NameHistoryProfile; import cc.fascinated.bat.features.reminder.ReminderProfile; import cc.fascinated.bat.features.welcomer.WelcomerProfile; @@ -171,6 +172,15 @@ public class BatGuild extends ProfileHolder { return getProfile(LevelingProfile.class); } + /** + * Gets the minecraft profile + * + * @return the minecraft profile + */ + public MinecraftProfile getMinecraftProfile() { + return getProfile(MinecraftProfile.class); + } + /** * Saves the user */