diff --git a/src/main/java/wtf/beatrice/hidekobot/Configuration.java b/src/main/java/wtf/beatrice/hidekobot/Configuration.java index f330753..763f4ed 100644 --- a/src/main/java/wtf/beatrice/hidekobot/Configuration.java +++ b/src/main/java/wtf/beatrice/hidekobot/Configuration.java @@ -1,9 +1,16 @@ package wtf.beatrice.hidekobot; +import net.dv8tion.jda.api.interactions.commands.Command; import org.jetbrains.annotations.Nullable; import wtf.beatrice.hidekobot.database.DatabaseManager; import wtf.beatrice.hidekobot.listeners.MessageLogger; +import java.awt.*; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + public class Configuration { @@ -20,6 +27,16 @@ public class Configuration // note: discord sets interactions' expiry time to 15 minutes by default, so we can't go higher than that. private final static long expiryTimeSeconds = 60L; + // used to count eg. uptime + private static LocalDateTime startupTime; + + + private static final String botVersion = "0.1.1-slash"; // we should probably find a way to make this consistent with Maven + private static final String botName = "HidekoBot"; + private static final Color botColor = Color.PINK; + + private static final List registeredCommands = new ArrayList<>(); + private final static String defaultInviteLink = "https://discord.com/api/oauth2/authorize?client_id=%userid%&scope=bot+applications.commands&permissions=8"; @@ -134,4 +151,64 @@ public class Configuration */ public static long getExpiryTimeSeconds() { return expiryTimeSeconds; } + + public static String getBotName() { return botName; }; + + /** + * Get the bot's version. + * + * @return a String of the bot version. + */ + public static String getBotVersion() { return botVersion; } + + /** + * Get the bot's global color. + * + * @return the Color object. + */ + public static Color getBotColor() { return botColor; } + + /** + * Set the list of registered commands. They will be sorted alphabetically. + * + * @param commands a list of registered commands. + */ + public static void setRegisteredCommands(List commands) + { + + // sort alphabetically by field getName() + List tempList = commands + .stream() + .sorted(Comparator.comparing(Command::getName)) + .toList(); + + registeredCommands.addAll(tempList); + } + + /** + * Get a list of all bot registered commands, sorted alphabetically. + * + * @return a copy of the List. + */ + public static List getRegisteredCommands() + { + return new ArrayList<>(registeredCommands); + } + + /** + * Set the bot's startup time. Generally only used at boot time. + * + * @param time a LocalDateTime of the startup moment. + */ + public static void setStartupTime(LocalDateTime time) + { startupTime = time; } + + + /** + * Get the time of when the bot was started up. + * + * @return a LocalDateTime object of the startup instant. + */ + public static LocalDateTime getStartupTime() { return startupTime; } + } diff --git a/src/main/java/wtf/beatrice/hidekobot/HidekoBot.java b/src/main/java/wtf/beatrice/hidekobot/HidekoBot.java index 61409df..8cc1451 100644 --- a/src/main/java/wtf/beatrice/hidekobot/HidekoBot.java +++ b/src/main/java/wtf/beatrice/hidekobot/HidekoBot.java @@ -16,6 +16,7 @@ import wtf.beatrice.hidekobot.utils.Logger; import wtf.beatrice.hidekobot.utils.SlashCommandsUtil; import java.io.File; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -26,7 +27,6 @@ import java.util.concurrent.TimeUnit; public class HidekoBot { private static String botToken; - private static final String version = "0.1.1-slash"; // we should probably find a way to make this consistent with Maven private static JDA jda; @@ -120,13 +120,16 @@ public class HidekoBot int periodicDelay = 5; scheduler.scheduleAtFixedRate(task, initDelay, periodicDelay, TimeUnit.SECONDS); - - // print the bot logo. - logger.log("\n\n" + logger.getLogo() + "\nv" + version + " - bot is ready!\n", 2); - // register shutdown interrupt signal listener for proper shutdown. Signal.handle(new Signal("INT"), signal -> shutdown()); + // set startup time. + Configuration.setStartupTime(LocalDateTime.now()); + + // print the bot logo. + logger.log("\n\n" + logger.getLogo() + "\nv" + Configuration.getBotVersion() + " - bot is ready!\n", 2); + + // log the invite-link to console so noob users can just click on it. logger.log("Bot User ID: " + botUserId, 3); logger.log("Invite Link: " + Configuration.getInviteUrl(), 4); diff --git a/src/main/java/wtf/beatrice/hidekobot/commands/slash/AvatarCommand.java b/src/main/java/wtf/beatrice/hidekobot/commands/slash/AvatarCommand.java index d986fbc..23e788a 100644 --- a/src/main/java/wtf/beatrice/hidekobot/commands/slash/AvatarCommand.java +++ b/src/main/java/wtf/beatrice/hidekobot/commands/slash/AvatarCommand.java @@ -7,13 +7,12 @@ import net.dv8tion.jda.api.interactions.commands.OptionMapping; import org.jetbrains.annotations.NotNull; import wtf.beatrice.hidekobot.Configuration; -import java.awt.*; - public class AvatarCommand { public void runSlashCommand(@NotNull SlashCommandInteractionEvent event) { + // defer reply because this might take a moment event.deferReply().queue(); User user; @@ -55,7 +54,7 @@ public class AvatarCommand // embed processing { - embedBuilder.setColor(Color.PINK); + embedBuilder.setColor(Configuration.getBotColor()); embedBuilder.setTitle("Profile picture"); embedBuilder.addField("User", "<@" + user.getId() + ">", false); diff --git a/src/main/java/wtf/beatrice/hidekobot/commands/slash/BotInfoCommand.java b/src/main/java/wtf/beatrice/hidekobot/commands/slash/BotInfoCommand.java new file mode 100644 index 0000000..f1ce9e5 --- /dev/null +++ b/src/main/java/wtf/beatrice/hidekobot/commands/slash/BotInfoCommand.java @@ -0,0 +1,85 @@ +package wtf.beatrice.hidekobot.commands.slash; + +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.interactions.commands.Command; +import org.jetbrains.annotations.NotNull; +import wtf.beatrice.hidekobot.Configuration; +import wtf.beatrice.hidekobot.HidekoBot; + +import java.lang.management.ManagementFactory; +import java.text.DecimalFormat; +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.List; + +public class BotInfoCommand +{ + + public void runSlashCommand(@NotNull SlashCommandInteractionEvent event) + { + // defer reply because this might take a moment + event.deferReply().queue(); + + List registeredCommands = Configuration.getRegisteredCommands(); + + EmbedBuilder embedBuilder = new EmbedBuilder(); + + // embed processing + { + embedBuilder.setColor(Configuration.getBotColor()); + embedBuilder.setTitle(Configuration.getBotName()); + + // thumbnail + String botAvatarUrl = HidekoBot.getAPI().getSelfUser().getAvatarUrl(); + if(botAvatarUrl != null) embedBuilder.setThumbnail(botAvatarUrl); + + // commands list field + StringBuilder commandsListBuilder = new StringBuilder(); + commandsListBuilder.append(registeredCommands.size()).append( " total - "); + for(int i = 0; i < registeredCommands.size(); i++) + { + Command cmd = registeredCommands.get(i); + commandsListBuilder.append("`" + cmd.getName() + "`"); + + if(i + 1 != registeredCommands.size()) // don't add comma in last iteration + { + commandsListBuilder.append(", "); + } + + } + embedBuilder.addField("Commands", commandsListBuilder.toString(), false); + + // version field + embedBuilder.addField("Version", "v" + Configuration.getBotVersion(), true); + + // jvm version field + String jvmVersion = ManagementFactory.getRuntimeMXBean().getVmVersion(); + embedBuilder.addField("JVM Version", "v" + jvmVersion, true); + + // used ram field + long usedRamBytes = (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()); + double usedRamMB = usedRamBytes / 1024.0 / 1024.0; // bytes -> kB -> MB + DecimalFormat ramMBFormatter = new DecimalFormat("#.##"); + embedBuilder.addField("RAM Usage", ramMBFormatter.format(usedRamMB) + " MB", true); + + // author field + String authorMention = "<@" + Configuration.getBotOwnerId() + ">"; + embedBuilder.addField("Author", authorMention, true); + + // uptime field + LocalDateTime now = LocalDateTime.now(); + long uptimeSeconds = ChronoUnit.SECONDS.between(Configuration.getStartupTime(), now); + Duration uptime = Duration.ofSeconds(uptimeSeconds); + uptime.toDaysPart() // todo + embedBuilder.addField("Uptime", "seconds: " + uptime, true); + + + } + + event.getHook().editOriginalEmbeds(embedBuilder.build()).queue(); + + + } +} diff --git a/src/main/java/wtf/beatrice/hidekobot/listeners/SlashCommandListener.java b/src/main/java/wtf/beatrice/hidekobot/listeners/SlashCommandListener.java index 18132c9..bce9da4 100644 --- a/src/main/java/wtf/beatrice/hidekobot/listeners/SlashCommandListener.java +++ b/src/main/java/wtf/beatrice/hidekobot/listeners/SlashCommandListener.java @@ -13,6 +13,7 @@ public class SlashCommandListener extends ListenerAdapter { switch (event.getName().toLowerCase()) { case "avatar" -> new AvatarCommand().runSlashCommand(event); + case "botinfo" -> new BotInfoCommand().runSlashCommand(event); case "clear" -> new ClearChatCommand().runSlashCommand(event); case "coinflip" -> new CoinFlipCommand().runSlashCommand(event); case "die" -> new DieCommand().runSlashCommand(event); diff --git a/src/main/java/wtf/beatrice/hidekobot/utils/SlashCommandsUtil.java b/src/main/java/wtf/beatrice/hidekobot/utils/SlashCommandsUtil.java index 1bc5871..086a891 100644 --- a/src/main/java/wtf/beatrice/hidekobot/utils/SlashCommandsUtil.java +++ b/src/main/java/wtf/beatrice/hidekobot/utils/SlashCommandsUtil.java @@ -7,6 +7,7 @@ import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions; import net.dv8tion.jda.api.interactions.commands.OptionType; import net.dv8tion.jda.api.interactions.commands.build.CommandData; import net.dv8tion.jda.api.interactions.commands.build.Commands; +import wtf.beatrice.hidekobot.Configuration; import wtf.beatrice.hidekobot.HidekoBot; import wtf.beatrice.hidekobot.listeners.MessageListener; @@ -23,7 +24,8 @@ public class SlashCommandsUtil add(Commands.slash("avatar", "Get someone's profile picture.") .addOption(OptionType.USER, "user", "User you want to grab the avatar of.") .addOption(OptionType.INTEGER, "size", "The size of the returned image.", false, true)); - add(Commands.slash("die", "Stop the bot's process") + add(Commands.slash("botinfo", "Get info about the bot.")); + add(Commands.slash("die", "Stop the bot's process.") .setDefaultPermissions(DefaultMemberPermissions.DISABLED)); add(Commands.slash("clear", "Clear the current channel's chat.") .addOption(OptionType.INTEGER, "amount", "The amount of messages to delete.") @@ -118,76 +120,7 @@ public class SlashCommandsUtil logger.log("Commands updated. New total: " + allCommands.size() + "."); } - - - /* - List toAdd = new ArrayList<>(); - List toDelete = new ArrayList<>(); - - List registeredCommands = jdaInstance.retrieveCommands().complete(); - - // for each command that we have already registered... - for(Command currRegCmd : registeredCommands) - { - // queue it for removal. - boolean toRemove = true; - - // iterate through all "recognized" commands - for(CommandData cmdData : allCommands) - { - // if we find the same command... - if(cmdData.getName().equals(currRegCmd.getName())) - { - // then don't remove it - toRemove = false; - // and quit the loop since we found it. - break; - } - } - - // if no match was found, queue this command for removal. - if(toRemove) toDelete.add(currRegCmd); - - } - - // for each "recognized" valid command - for(CommandData currCmdData : allCommands) - { - // queue it for registration. - boolean toRegister = true; - - // iterate through all already registered commands. - for(Command cmd : registeredCommands) - { - // if this command was already registered... - if(cmd.getName().equals(currCmdData.getName())) - { - // flag that we don't need to register it - toRegister = false; - // and quit the loop since we found a match. - break; - } - } - - // if no match was found, queue this command for registration. - if(toRegister) toAdd.add(currCmdData); - } - - logger.log("Found " + registeredCommands.size() + " commands."); - - // remove all commands queued for removal. - for(Command cmd : toDelete) - { - jdaInstance.deleteCommandById(cmd.getId()).queue(); - } - - logger.log("Deleted " + toDelete.size() + " commands."); - - // register all new commands. - jdaInstance.updateCommands().addCommands(toAdd).queue(); - logger.log("Registered " + toAdd.size() + " new commands."); - - - */ + // cache the registered commands. + Configuration.setRegisteredCommands(jdaInstance.retrieveCommands().complete()); } }