From 2b3f5e60f1d95fc23ef19eae90b26d14d4e94b32 Mon Sep 17 00:00:00 2001 From: beanbeanjuice Date: Tue, 16 Jul 2024 12:19:31 -0400 Subject: [PATCH] Added Update Checker and Commands --- build.gradle.kts | 2 +- .../com/beanbeanjuice/cafebot/CafeBot.java | 11 ++- .../settings/update/UpdateCommand.java | 71 ++++++++++++++++ .../update/UpdateStatusSubCommand.java | 55 ++++++++++++ .../UpdateChannelRemoveSubCommand.java | 44 ++++++++++ .../channel/UpdateChannelSetSubCommand.java | 61 ++++++++++++++ .../api/GitHubVersionEndpointWrapper.java | 22 ++++- .../utility/helper/UpdateCheckHelper.java | 83 +++++++++++++++++++ 8 files changed, 345 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/beanbeanjuice/cafebot/commands/settings/update/UpdateCommand.java create mode 100644 src/main/java/com/beanbeanjuice/cafebot/commands/settings/update/UpdateStatusSubCommand.java create mode 100644 src/main/java/com/beanbeanjuice/cafebot/commands/settings/update/channel/UpdateChannelRemoveSubCommand.java create mode 100644 src/main/java/com/beanbeanjuice/cafebot/commands/settings/update/channel/UpdateChannelSetSubCommand.java create mode 100644 src/main/java/com/beanbeanjuice/cafebot/utility/helper/UpdateCheckHelper.java diff --git a/build.gradle.kts b/build.gradle.kts index f596307e..1e247d42 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,7 +6,7 @@ plugins { } group = "com.beanbeanjuice" -version = "v4.0.0" +version = "4.0.0" allprojects { group = "com.beanbeanjuice" diff --git a/src/main/java/com/beanbeanjuice/cafebot/CafeBot.java b/src/main/java/com/beanbeanjuice/cafebot/CafeBot.java index d1e643be..78faffcd 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/CafeBot.java +++ b/src/main/java/com/beanbeanjuice/cafebot/CafeBot.java @@ -20,12 +20,15 @@ import com.beanbeanjuice.cafebot.commands.moderation.ClearChatCommand; import com.beanbeanjuice.cafebot.commands.settings.AICommand; import com.beanbeanjuice.cafebot.commands.settings.goodbye.GoodbyeCommand; +import com.beanbeanjuice.cafebot.commands.settings.update.UpdateCommand; import com.beanbeanjuice.cafebot.commands.settings.welcome.WelcomeCommand; import com.beanbeanjuice.cafebot.commands.social.MemberCountCommand; import com.beanbeanjuice.cafebot.commands.social.vent.VentCommand; import com.beanbeanjuice.cafebot.commands.twitch.TwitchCommand; +import com.beanbeanjuice.cafebot.utility.api.GitHubVersionEndpointWrapper; import com.beanbeanjuice.cafebot.utility.commands.CommandHandler; import com.beanbeanjuice.cafebot.utility.helper.Helper; +import com.beanbeanjuice.cafebot.utility.helper.UpdateCheckHelper; import com.beanbeanjuice.cafebot.utility.listeners.*; import com.beanbeanjuice.cafebot.utility.logging.LogLevel; import com.beanbeanjuice.cafebot.utility.logging.LogManager; @@ -254,7 +257,8 @@ private void setupCommands() { // Settings new AICommand(this), new WelcomeCommand(this), - new GoodbyeCommand(this) + new GoodbyeCommand(this), + new UpdateCommand(this) // new EmbedCommand(this) ); @@ -262,6 +266,9 @@ private void setupCommands() { this.JDA.addEventListener(commandHandler); this.helpHandler = new HelpHandler(commandHandler); this.twitchHandler = new TwitchHandler(System.getenv("CAFEBOT_TWITCH_ACCESS_TOKEN"), this); + + UpdateCheckHelper updateCheckHelper = new UpdateCheckHelper(this); + updateCheckHelper.checkUpdate(); } public void addEventListener(final ListenerAdapter listener) { @@ -289,7 +296,7 @@ private String getVersion() { try (InputStream resourceStream = loader.getResourceAsStream(resourceName)) { props.load(resourceStream); } catch (IOException e) { throw new RuntimeException(e); } - return props.get("version").toString(); + return "v" + props.get("version").toString(); } private void startUpdateTimer() { diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/update/UpdateCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/update/UpdateCommand.java new file mode 100644 index 00000000..fa32829b --- /dev/null +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/update/UpdateCommand.java @@ -0,0 +1,71 @@ +package com.beanbeanjuice.cafebot.commands.settings.update; + +import com.beanbeanjuice.cafebot.CafeBot; +import com.beanbeanjuice.cafebot.commands.settings.update.channel.UpdateChannelRemoveSubCommand; +import com.beanbeanjuice.cafebot.commands.settings.update.channel.UpdateChannelSetSubCommand; +import com.beanbeanjuice.cafebot.utility.commands.*; +import net.dv8tion.jda.api.Permission; + +public class UpdateCommand extends Command implements ICommand { + + public UpdateCommand(final CafeBot cafeBot) { + super(cafeBot); + } + + @Override + public String getName() { + return "update"; + } + + @Override + public String getDescription() { + return "Edit the bot update settings!"; + } + + @Override + public CommandCategory getCategory() { + return CommandCategory.SETTINGS; + } + + @Override + public Permission[] getPermissions() { + return new Permission[] { + Permission.MANAGE_SERVER + }; + } + + @Override + public boolean isEphemeral() { + return true; + } + + @Override + public boolean isNSFW() { + return false; + } + + @Override + public boolean allowDM() { + return false; + } + + @Override + public ISubCommand[] getSubCommands() { + return new ISubCommand[] { + new UpdateStatusSubCommand(cafeBot) + }; + } + + @Override + public SubCommandGroup[] getSubCommandGroups() { + SubCommandGroup channelGroup = new SubCommandGroup("channel", "Edit the update channel."); + channelGroup.addSubCommands(new ISubCommand[]{ + new UpdateChannelSetSubCommand(cafeBot), + new UpdateChannelRemoveSubCommand(cafeBot) + } + ); + + return new SubCommandGroup[]{ channelGroup }; + } + +} diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/update/UpdateStatusSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/update/UpdateStatusSubCommand.java new file mode 100644 index 00000000..534cb65b --- /dev/null +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/update/UpdateStatusSubCommand.java @@ -0,0 +1,55 @@ +package com.beanbeanjuice.cafebot.commands.settings.update; + +import com.beanbeanjuice.cafeapi.wrapper.endpoints.guilds.GuildInformationType; +import com.beanbeanjuice.cafebot.CafeBot; +import com.beanbeanjuice.cafebot.utility.commands.Command; +import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; +import com.beanbeanjuice.cafebot.utility.helper.Helper; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.interactions.commands.OptionType; +import net.dv8tion.jda.api.interactions.commands.build.OptionData; + +public class UpdateStatusSubCommand extends Command implements ISubCommand { + + public UpdateStatusSubCommand(final CafeBot cafeBot) { + super(cafeBot); + } + + @Override + public void handle(SlashCommandInteractionEvent event) { + boolean status = event.getOption("enabled").getAsBoolean(); + + cafeBot.getCafeAPI().getGuildsEndpoint().updateGuildInformation(event.getGuild().getId(), GuildInformationType.NOTIFY_ON_UPDATE, status) + .thenAcceptAsync((ignored) -> { + event.getHook().sendMessageEmbeds(Helper.successEmbed( + "Bot Update Notification Status Changed", + "The bot update notification status has been successfully changed." + )).queue(); + }) + .exceptionallyAsync((e) -> { + event.getHook().sendMessageEmbeds(Helper.errorEmbed( + "Error Changing Bot Notification Status", + String.format("There was an error changing the bot notification status: %s", e.getMessage()) + )).queue(); + return null; + }); + } + + @Override + public String getName() { + return "status"; + } + + @Override + public String getDescription() { + return "Enable or disable bot update notifications."; + } + + @Override + public OptionData[] getOptions() { + return new OptionData[] { + new OptionData(OptionType.BOOLEAN, "enabled", "Enable or disable bot update notifications.", true) + }; + } + +} diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/update/channel/UpdateChannelRemoveSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/update/channel/UpdateChannelRemoveSubCommand.java new file mode 100644 index 00000000..a482c582 --- /dev/null +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/update/channel/UpdateChannelRemoveSubCommand.java @@ -0,0 +1,44 @@ +package com.beanbeanjuice.cafebot.commands.settings.update.channel; + +import com.beanbeanjuice.cafeapi.wrapper.endpoints.guilds.GuildInformationType; +import com.beanbeanjuice.cafebot.CafeBot; +import com.beanbeanjuice.cafebot.utility.commands.Command; +import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; +import com.beanbeanjuice.cafebot.utility.helper.Helper; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; + +public class UpdateChannelRemoveSubCommand extends Command implements ISubCommand { + + public UpdateChannelRemoveSubCommand(final CafeBot cafeBot) { + super(cafeBot); + } + + @Override + public void handle(SlashCommandInteractionEvent event) { + cafeBot.getCafeAPI().getGuildsEndpoint().updateGuildInformation(event.getGuild().getId(), GuildInformationType.UPDATE_CHANNEL_ID, "0") + .thenAcceptAsync((ignored) -> { + event.getHook().sendMessageEmbeds(Helper.successEmbed( + "Update Channel Removed", + "The update channel has been successfully removed. Don't forget to disable update notifications too!" + )).queue(); + }) + .exceptionallyAsync((e) -> { + event.getHook().sendMessageEmbeds(Helper.errorEmbed( + "Error Removing Update Channel", + String.format("There was an error removing the update channel: %s", e.getMessage()) + )).queue(); + return null; + }); + } + + @Override + public String getName() { + return "remove"; + } + + @Override + public String getDescription() { + return "Remove the update channel."; + } + +} diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/update/channel/UpdateChannelSetSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/update/channel/UpdateChannelSetSubCommand.java new file mode 100644 index 00000000..03b1007f --- /dev/null +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/update/channel/UpdateChannelSetSubCommand.java @@ -0,0 +1,61 @@ +package com.beanbeanjuice.cafebot.commands.settings.update.channel; + +import com.beanbeanjuice.cafeapi.wrapper.endpoints.guilds.GuildInformationType; +import com.beanbeanjuice.cafebot.CafeBot; +import com.beanbeanjuice.cafebot.utility.commands.Command; +import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; +import com.beanbeanjuice.cafebot.utility.helper.Helper; +import net.dv8tion.jda.api.entities.channel.unions.GuildChannelUnion; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.interactions.commands.OptionMapping; +import net.dv8tion.jda.api.interactions.commands.OptionType; +import net.dv8tion.jda.api.interactions.commands.build.OptionData; + +import java.util.Optional; + +public class UpdateChannelSetSubCommand extends Command implements ISubCommand { + + public UpdateChannelSetSubCommand(final CafeBot cafeBot) { + super(cafeBot); + } + + @Override + public void handle(SlashCommandInteractionEvent event) { + Optional channelMapping = Optional.ofNullable(event.getOption("channel")); + + GuildChannelUnion channel = channelMapping.map(OptionMapping::getAsChannel).orElse((GuildChannelUnion) event.getChannel()); + + cafeBot.getCafeAPI().getGuildsEndpoint().updateGuildInformation(event.getGuild().getId(), GuildInformationType.UPDATE_CHANNEL_ID, channel.getId()) + .thenAcceptAsync((ignored) -> { + event.getHook().sendMessageEmbeds(Helper.successEmbed( + "Update Channel Set", + String.format("The update channel has been successfully set to %s. Don't forget to enable bot updates!", channel.getAsMention()) + )).queue(); + }) + .exceptionallyAsync((e) -> { + event.getHook().sendMessageEmbeds(Helper.errorEmbed( + "Error Setting Update Channel", + String.format("There was an error setting the update channel: %s", e.getMessage()) + )).queue(); + return null; + }); + } + + @Override + public String getName() { + return "set"; + } + + @Override + public String getDescription() { + return "Set the bot update channel!"; + } + + @Override + public OptionData[] getOptions() { + return new OptionData[] { + new OptionData(OptionType.CHANNEL, "channel", "The channel you want to set the update channel to.", false) + }; + } + +} diff --git a/src/main/java/com/beanbeanjuice/cafebot/utility/api/GitHubVersionEndpointWrapper.java b/src/main/java/com/beanbeanjuice/cafebot/utility/api/GitHubVersionEndpointWrapper.java index 8a88f7b8..b948d9a8 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/utility/api/GitHubVersionEndpointWrapper.java +++ b/src/main/java/com/beanbeanjuice/cafebot/utility/api/GitHubVersionEndpointWrapper.java @@ -42,7 +42,27 @@ public CompletableFuture getVersion(final String versionTag) { } } - private String parse(String responseBody) { + public CompletableFuture getLatestVersionString() { + try (HttpClient client = HttpClient.newHttpClient()) { + HttpRequest request = HttpRequest.newBuilder().setHeader("User-Agent", cafeBot.getBotUserAgent()).uri(URI.create("https://api.github.com/repos/beanbeanjuice/cafeBot/tags")).build(); + + return client.sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenApply(HttpResponse::body) + .thenApply(this::versionNumberFromResponse); + } + } + + private String versionNumberFromResponse(final String responseBody) { + ObjectMapper defaultObjectMapper = new ObjectMapper(); + try { + JsonNode node = defaultObjectMapper.readTree(responseBody); + return node.get(0).get("name").textValue(); + } catch (JsonProcessingException e) { + throw new CompletionException(e); + } + } + + private String parse(final String responseBody) { ObjectMapper defaultObjectMapper = new ObjectMapper(); try { JsonNode node = defaultObjectMapper.readTree(responseBody); diff --git a/src/main/java/com/beanbeanjuice/cafebot/utility/helper/UpdateCheckHelper.java b/src/main/java/com/beanbeanjuice/cafebot/utility/helper/UpdateCheckHelper.java new file mode 100644 index 00000000..d57b5f26 --- /dev/null +++ b/src/main/java/com/beanbeanjuice/cafebot/utility/helper/UpdateCheckHelper.java @@ -0,0 +1,83 @@ +package com.beanbeanjuice.cafebot.utility.helper; + +import com.beanbeanjuice.cafeapi.wrapper.endpoints.guilds.GuildInformation; +import com.beanbeanjuice.cafeapi.wrapper.endpoints.guilds.GuildInformationType; +import com.beanbeanjuice.cafebot.CafeBot; +import com.beanbeanjuice.cafebot.utility.api.GitHubVersionEndpointWrapper; +import com.beanbeanjuice.cafebot.utility.logging.LogLevel; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; + +import java.util.ArrayList; + +public class UpdateCheckHelper { + + private final CafeBot cafeBot; + private final GitHubVersionEndpointWrapper githubVersionWrapper; + private final ArrayList guildsToNotify; + + public UpdateCheckHelper(final CafeBot cafeBot) { + this.cafeBot = cafeBot; + this.githubVersionWrapper = new GitHubVersionEndpointWrapper(this.cafeBot); + this.guildsToNotify = new ArrayList<>(); + } + + public void checkUpdate() { + this.githubVersionWrapper.getLatestVersionString().thenAcceptAsync((gitHubVersion) -> { + if (!gitHubVersion.equals(this.cafeBot.getBotVersion())) { + this.cafeBot.getLogger().log(UpdateCheckHelper.class, LogLevel.INFO, "No update found. Not sending update messages."); + return; + } + + this.cafeBot.getCafeAPI().getVersionsEndpoint().getCurrentCafeBotVersion().thenAcceptAsync((databaseVersion) -> { + if (databaseVersion.equals(gitHubVersion)) { + this.cafeBot.getLogger().log(UpdateCheckHelper.class, LogLevel.INFO, "Versions match. Not sending update messages."); + return; + } + + this.cafeBot.getLogger().log(UpdateCheckHelper.class, LogLevel.WARN, String.format("There is an update (%s)... sending update messages.", gitHubVersion)); + this.cafeBot.getCafeAPI().getVersionsEndpoint().updateCurrentCafeBotVersion(gitHubVersion); + + this.githubVersionWrapper.getVersion(gitHubVersion).thenAcceptAsync((embed) -> { + this.guildsToNotify.addAll(this.cafeBot.getJDA().getGuilds()); + handleUpdateNotifications(); + }); + }); + }); + } + + private void handleUpdateNotifications() { + this.cafeBot.getCafeAPI().getGuildsEndpoint().getAllGuildInformation().thenAcceptAsync((guildsMap) -> { + this.guildsToNotify.forEach((guild) -> { + if (!guildsMap.containsKey(guild.getId())) return; + + handleGuildUpdate(guild, guildsMap.get(guild.getId())); + }); + }); + } + + private void handleGuildUpdate(final Guild guild, final GuildInformation information) { + if (!Boolean.parseBoolean(information.getSetting(GuildInformationType.NOTIFY_ON_UPDATE))) return; + + String updateChannelID = information.getSetting(GuildInformationType.UPDATE_CHANNEL_ID); + + this.githubVersionWrapper.getVersion(this.cafeBot.getBotVersion()) + .thenAcceptAsync((versionEmbed) -> sendVersionEmbed(guild, updateChannelID, versionEmbed)); + } + + private void sendVersionEmbed(final Guild guild, final String updateChannelID, final MessageEmbed embed) { + guild.retrieveOwner().queue((owner) -> { + TextChannel updateChannel = guild.getTextChannelById(updateChannelID); + if (updateChannel == null) { + try { updateChannel = guild.getDefaultChannel().asTextChannel(); } + catch (NullPointerException | IllegalStateException e) { return; } + } + + updateChannel.sendMessage(String.format("%s there was an update!", owner.getAsMention())) + .addEmbeds(embed) + .queue(); + }); + } + +}