From 240b691acc9d346f5589eed28a77b4a491ea856d Mon Sep 17 00:00:00 2001 From: Zoriot Date: Tue, 18 Aug 2026 13:27:00 +0200 Subject: [PATCH 1/4] Add Discord webhook notifications for plugin downloads (#157) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement basic Discord webhook support to send notifications when plugins are successfully downloaded. Uses jdwebhooks 2.0.0 library with Discord's Components V2 API. Changes: - Add jdwebhooks 2.0.0 dependency to common/impl - Create DiscordWebhookNotifier class for sending webhook notifications - Add discord-webhook configuration section to config.yml (disabled by default) - Update ConfigManager to load and reload webhook settings - Integrate webhook notifications in UpdateHandler on successful downloads - Update UpdaterImpl to initialize and manage webhook notifier lifecycle Webhook notifications include: - Plugin name - Version change (old → new) - Update type (MAJOR, MINOR, PATCH, etc.) - Changelog link (if available) Users can enable by setting discord-webhook.enabled to true and providing a webhook-url in config.yml. Closes #157 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- common/impl/build.gradle.kts | 1 + .../pluginupdater/common/UpdaterImpl.java | 20 ++++ .../common/config/ConfigManager.java | 30 ++++++ .../notifier/DiscordWebhookNotifier.java | 96 +++++++++++++++++++ .../common/updater/UpdateHandler.java | 6 ++ common/impl/src/main/resources/config.yml | 5 + 6 files changed, 158 insertions(+) create mode 100644 common/impl/src/main/java/org/lushplugins/pluginupdater/common/notifier/DiscordWebhookNotifier.java diff --git a/common/impl/build.gradle.kts b/common/impl/build.gradle.kts index 4e7af67f..78ab9bca 100644 --- a/common/impl/build.gradle.kts +++ b/common/impl/build.gradle.kts @@ -3,4 +3,5 @@ dependencies { api("io.github.revxrsal:lamp.common:4.0.0-rc.18") api("com.electronwill.night-config:yaml:3.9.0") + api("io.github.4drian3d:jdwebhooks:2.0.0") } diff --git a/common/impl/src/main/java/org/lushplugins/pluginupdater/common/UpdaterImpl.java b/common/impl/src/main/java/org/lushplugins/pluginupdater/common/UpdaterImpl.java index be27cc81..7c556f13 100644 --- a/common/impl/src/main/java/org/lushplugins/pluginupdater/common/UpdaterImpl.java +++ b/common/impl/src/main/java/org/lushplugins/pluginupdater/common/UpdaterImpl.java @@ -10,6 +10,7 @@ import org.lushplugins.pluginupdater.common.command.annotation.PluginName; import org.lushplugins.pluginupdater.common.command.response.StringMessageResponseHandler; import org.lushplugins.pluginupdater.common.config.ConfigManager; +import org.lushplugins.pluginupdater.common.notifier.DiscordWebhookNotifier; import org.lushplugins.pluginupdater.common.platform.CommandHandler; import org.lushplugins.pluginupdater.common.platform.UpdaterPlugin; import org.lushplugins.pluginupdater.common.updater.UpdateHandler; @@ -29,6 +30,7 @@ public class UpdaterImpl { private final List collectors; private final UpdateHandler updateHandler; private final ConfigManager config; + private DiscordWebhookNotifier discordWebhookNotifier; public UpdaterImpl(UpdaterPlatform platform, UpdaterPlugin updaterPlugin, CommandHandler commandPlatform, List collectors) { this.platform = platform; @@ -41,6 +43,13 @@ public UpdaterImpl(UpdaterPlatform platform, UpdaterPlugin updaterPlugin, Com config = new ConfigManager(this); config.reload(); + + // Initialize Discord webhook notifier + this.discordWebhookNotifier = new DiscordWebhookNotifier( + updaterPlugin.getComponentLogger(), + config.isDiscordWebhookEnabled(), + config.getDiscordWebhookUrl() + ); Lamp lamp = commandPlatform.prepareLamp() .permissionFactory(new CommandPermissionFactory(this)) @@ -83,6 +92,9 @@ public UpdaterImpl(UpdaterPlatform platform, UpdaterPlugin updaterPlugin, Com public void shutdown() { updateHandler.shutdown(); + if (discordWebhookNotifier != null) { + discordWebhookNotifier.close(); + } } public UpdaterPlatform platform() { @@ -109,6 +121,14 @@ public ConfigManager config() { return config; } + public DiscordWebhookNotifier discordWebhookNotifier() { + return discordWebhookNotifier; + } + + public void setDiscordWebhookNotifier(DiscordWebhookNotifier notifier) { + this.discordWebhookNotifier = notifier; + } + public CompletableFuture> collectUnknownPlugins() { return CompletableFuture.supplyAsync(() -> { Map unknownPlugins = platform.getPlugins().stream() diff --git a/common/impl/src/main/java/org/lushplugins/pluginupdater/common/config/ConfigManager.java b/common/impl/src/main/java/org/lushplugins/pluginupdater/common/config/ConfigManager.java index ca737c39..e98ff4b5 100644 --- a/common/impl/src/main/java/org/lushplugins/pluginupdater/common/config/ConfigManager.java +++ b/common/impl/src/main/java/org/lushplugins/pluginupdater/common/config/ConfigManager.java @@ -8,6 +8,7 @@ import org.lushplugins.pluginupdater.api.updater.PluginData; import org.lushplugins.pluginupdater.common.UpdaterImpl; import org.lushplugins.pluginupdater.common.config.deserializer.PluginDataDeserializer; +import org.lushplugins.pluginupdater.common.notifier.DiscordWebhookNotifier; import org.lushplugins.pluginupdater.common.updater.UpdateHandler; import org.lushplugins.pluginupdater.common.util.ConfigUtil; @@ -22,6 +23,8 @@ public class ConfigManager { private final Map plugins = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); private final Set disabledPlugins = new HashSet<>(); private Messages messages; + private boolean discordWebhookEnabled; + private String discordWebhookUrl; public ConfigManager(UpdaterImpl updater) { this.updater = updater; @@ -46,6 +49,15 @@ public void reload(boolean skipCheck) { this.allowDownloads = config.getOrElse("allow-downloads", true); + Config discordConfig = config.get("discord-webhook"); + if (discordConfig != null) { + this.discordWebhookEnabled = discordConfig.getOrElse("enabled", false); + this.discordWebhookUrl = discordConfig.getOrElse("webhook-url", ""); + } else { + this.discordWebhookEnabled = false; + this.discordWebhookUrl = ""; + } + Config messagesConfig = config.get("messages"); if (messagesConfig != null) { this.messages = new Messages(messagesConfig); @@ -90,6 +102,16 @@ public void reload(boolean skipCheck) { } }); + // Reinitialize Discord webhook notifier with new config + if (updater.discordWebhookNotifier() != null) { + updater.discordWebhookNotifier().close(); + } + updater.setDiscordWebhookNotifier(new DiscordWebhookNotifier( + updater.updaterPlugin().getComponentLogger(), + this.discordWebhookEnabled, + this.discordWebhookUrl + )); + config.close(); } @@ -141,6 +163,14 @@ public String getMessage(String name, String def) { return messages.get(name, def); } + public boolean isDiscordWebhookEnabled() { + return discordWebhookEnabled; + } + + public String getDiscordWebhookUrl() { + return discordWebhookUrl; + } + public record Messages(Map messages) { diff --git a/common/impl/src/main/java/org/lushplugins/pluginupdater/common/notifier/DiscordWebhookNotifier.java b/common/impl/src/main/java/org/lushplugins/pluginupdater/common/notifier/DiscordWebhookNotifier.java new file mode 100644 index 00000000..b45430c4 --- /dev/null +++ b/common/impl/src/main/java/org/lushplugins/pluginupdater/common/notifier/DiscordWebhookNotifier.java @@ -0,0 +1,96 @@ +package org.lushplugins.pluginupdater.common.notifier; + +import io.github._4drian3d.jdwebhooks.component.Component; +import io.github._4drian3d.jdwebhooks.component.ContainerableComponent; +import io.github._4drian3d.jdwebhooks.webhook.WebHookClient; +import io.github._4drian3d.jdwebhooks.webhook.WebHookExecution; +import net.kyori.adventure.text.logger.slf4j.ComponentLogger; +import org.jetbrains.annotations.Nullable; +import org.lushplugins.pluginupdater.api.updater.PluginData; +import org.lushplugins.pluginupdater.api.version.Version; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class DiscordWebhookNotifier { + private final ComponentLogger logger; + private final WebHookClient webhookClient; + private final boolean enabled; + + public DiscordWebhookNotifier(ComponentLogger logger, boolean enabled, @Nullable String webhookUrl) { + this.logger = logger; + this.enabled = enabled && webhookUrl != null && !webhookUrl.isBlank(); + + if (this.enabled) { + this.webhookClient = WebHookClient.fromURL(webhookUrl); + } else { + this.webhookClient = null; + } + } + + public void notifyDownload(PluginData pluginData) { + if (!enabled || webhookClient == null) { + return; + } + + try { + Version currentVersion = pluginData.currentVersion(); + Optional latestVersionOpt = pluginData.latestVersion(); + + if (latestVersionOpt.isEmpty()) { + return; + } + + Version latestVersion = latestVersionOpt.get(); + String pluginName = pluginData.pluginName(); + + String versionString = String.format("%s → %s", + currentVersion.rawVersionString(), + latestVersion.rawVersionString() + ); + + List components = new ArrayList<>(); + + components.add(Component.textDisplay("**" + pluginName + " Updated**")); + + components.add(Component.textDisplay("**Version:** " + versionString)); + + String versionDiff = pluginData.versionDifference().name(); + if (!versionDiff.equals("UNKNOWN")) { + components.add(Component.textDisplay("**Update Type:** " + versionDiff)); + } + + pluginData.getChangelogUrl().ifPresent(s -> components.add(Component.textDisplay("**Changelog:** [View Changelog](" + s + ")"))); + + WebHookExecution webHook = WebHookExecution.builder() + .username("PluginUpdater") + .avatarURL("https://cdn.modrinth.com/data/IBSpJfbm/172c14d2cdb854064160fa627f9dd0043c1b79ee_96.webp") + .component( + Component.container() + .components(components) + .accentColor(0x66b04f) + .build() + ) + .build(); + + webhookClient.executeWebHook(webHook) + .whenComplete((response, throwable) -> { + int statusCode = response.statusCode(); + + if (throwable != null) { + logger.warn("Failed to send Discord webhook notification", throwable); + } else if (statusCode < 200 || statusCode >= 300) { + logger.warn("Discord webhook returned unsuccessful response. Status code: {}, response: {}", statusCode, response); + } + }); + + } catch (Exception e) { + logger.warn("Failed to send Discord webhook notification", e); + } + } + + public void close() { + // Might come handy in the future + } +} diff --git a/common/impl/src/main/java/org/lushplugins/pluginupdater/common/updater/UpdateHandler.java b/common/impl/src/main/java/org/lushplugins/pluginupdater/common/updater/UpdateHandler.java index 875665b6..d02e2437 100644 --- a/common/impl/src/main/java/org/lushplugins/pluginupdater/common/updater/UpdateHandler.java +++ b/common/impl/src/main/java/org/lushplugins/pluginupdater/common/updater/UpdateHandler.java @@ -121,6 +121,12 @@ private void processQueue() { if (pluginData.downloadUpdate(updater.updaterPlugin().getDownloadDir())) { pluginData.versionDifference(VersionDifference.UNKNOWN); pluginData.setAlreadyDownloaded(true); + + // Send Discord webhook notification + if (updater.discordWebhookNotifier() != null) { + updater.discordWebhookNotifier().notifyDownload(pluginData); + } + processingData.getFuture().complete(true); break; } else { diff --git a/common/impl/src/main/resources/config.yml b/common/impl/src/main/resources/config.yml index 17571d3f..2484bfa7 100644 --- a/common/impl/src/main/resources/config.yml +++ b/common/impl/src/main/resources/config.yml @@ -3,6 +3,11 @@ check-updates-on-reload: true # Set to 'false' to disable all download features of PluginUpdater (May require restart) allow-downloads: true +# Discord webhook configuration for download notifications +discord-webhook: + enabled: false + webhook-url: "" + # To learn how to configure plugins that require different sources check out the wiki: https://github.com/OakLoaf/PluginUpdater/wiki/PluginUpdater-Plugin plugins: PluginUpdater: From 1e008e980a080b567cf4ebf26f2a337147092205 Mon Sep 17 00:00:00 2001 From: Zoriot Date: Thu, 17 Sep 2026 14:56:29 +0200 Subject: [PATCH 2/4] Refactor Discord webhook implementation for improved lifecycle management and config handling - Migrate `DiscordWebhookNotifier` to `DiscordWebHookNotifier` with improved error handling. - Update `ConfigManager` to streamline webhook configuration and initialization. - Replace the `isDiscordWebhookEnabled` flag with URL validation. - Revise `UpdateHandler` webhook invocation logic using `Optional`. - Improve lifecycle management by introducing `shutdown` for webhooks. - Adjust `UpdaterImpl` to handle webhook creation more robustly. --- common/impl/build.gradle.kts | 2 +- .../pluginupdater/common/UpdaterImpl.java | 39 ++++++++-------- .../common/config/ConfigManager.java | 36 +++++++-------- ...ifier.java => DiscordWebHookNotifier.java} | 44 ++++++++----------- .../common/updater/UpdateHandler.java | 7 +-- 5 files changed, 59 insertions(+), 69 deletions(-) rename common/impl/src/main/java/org/lushplugins/pluginupdater/common/notifier/{DiscordWebhookNotifier.java => DiscordWebHookNotifier.java} (71%) diff --git a/common/impl/build.gradle.kts b/common/impl/build.gradle.kts index 78ab9bca..82f42641 100644 --- a/common/impl/build.gradle.kts +++ b/common/impl/build.gradle.kts @@ -3,5 +3,5 @@ dependencies { api("io.github.revxrsal:lamp.common:4.0.0-rc.18") api("com.electronwill.night-config:yaml:3.9.0") - api("io.github.4drian3d:jdwebhooks:2.0.0") + implementation("io.github.4drian3d:jdwebhooks:2.0.0") } diff --git a/common/impl/src/main/java/org/lushplugins/pluginupdater/common/UpdaterImpl.java b/common/impl/src/main/java/org/lushplugins/pluginupdater/common/UpdaterImpl.java index 7c556f13..61348ec0 100644 --- a/common/impl/src/main/java/org/lushplugins/pluginupdater/common/UpdaterImpl.java +++ b/common/impl/src/main/java/org/lushplugins/pluginupdater/common/UpdaterImpl.java @@ -10,16 +10,13 @@ import org.lushplugins.pluginupdater.common.command.annotation.PluginName; import org.lushplugins.pluginupdater.common.command.response.StringMessageResponseHandler; import org.lushplugins.pluginupdater.common.config.ConfigManager; -import org.lushplugins.pluginupdater.common.notifier.DiscordWebhookNotifier; +import org.lushplugins.pluginupdater.common.notifier.DiscordWebHookNotifier; import org.lushplugins.pluginupdater.common.platform.CommandHandler; import org.lushplugins.pluginupdater.common.platform.UpdaterPlugin; import org.lushplugins.pluginupdater.common.updater.UpdateHandler; import revxrsal.commands.Lamp; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; @@ -30,7 +27,7 @@ public class UpdaterImpl { private final List collectors; private final UpdateHandler updateHandler; private final ConfigManager config; - private DiscordWebhookNotifier discordWebhookNotifier; + private DiscordWebHookNotifier discordWebHookNotifier; public UpdaterImpl(UpdaterPlatform platform, UpdaterPlugin updaterPlugin, CommandHandler commandPlatform, List collectors) { this.platform = platform; @@ -43,13 +40,17 @@ public UpdaterImpl(UpdaterPlatform platform, UpdaterPlugin updaterPlugin, Com config = new ConfigManager(this); config.reload(); - - // Initialize Discord webhook notifier - this.discordWebhookNotifier = new DiscordWebhookNotifier( - updaterPlugin.getComponentLogger(), - config.isDiscordWebhookEnabled(), - config.getDiscordWebhookUrl() - ); + + if (config.getDiscordWebHookUrl() != null && !config.getDiscordWebHookUrl().isBlank()) { + try { + discordWebHookNotifier = new DiscordWebHookNotifier( + updaterPlugin.getComponentLogger(), + config.getDiscordWebHookUrl() + ); + } catch (Exception e) { + discordWebHookNotifier = null; + } + } Lamp lamp = commandPlatform.prepareLamp() .permissionFactory(new CommandPermissionFactory(this)) @@ -92,8 +93,8 @@ public UpdaterImpl(UpdaterPlatform platform, UpdaterPlugin updaterPlugin, Com public void shutdown() { updateHandler.shutdown(); - if (discordWebhookNotifier != null) { - discordWebhookNotifier.close(); + if (discordWebHookNotifier != null) { + discordWebHookNotifier.shutdown(); } } @@ -121,12 +122,12 @@ public ConfigManager config() { return config; } - public DiscordWebhookNotifier discordWebhookNotifier() { - return discordWebhookNotifier; + public Optional discordWebHookNotifier() { + return Optional.ofNullable(discordWebHookNotifier); } - public void setDiscordWebhookNotifier(DiscordWebhookNotifier notifier) { - this.discordWebhookNotifier = notifier; + public void discordWebHookNotifier(DiscordWebHookNotifier notifier) { + this.discordWebHookNotifier = notifier; } public CompletableFuture> collectUnknownPlugins() { diff --git a/common/impl/src/main/java/org/lushplugins/pluginupdater/common/config/ConfigManager.java b/common/impl/src/main/java/org/lushplugins/pluginupdater/common/config/ConfigManager.java index e98ff4b5..1f4a3b4f 100644 --- a/common/impl/src/main/java/org/lushplugins/pluginupdater/common/config/ConfigManager.java +++ b/common/impl/src/main/java/org/lushplugins/pluginupdater/common/config/ConfigManager.java @@ -8,7 +8,7 @@ import org.lushplugins.pluginupdater.api.updater.PluginData; import org.lushplugins.pluginupdater.common.UpdaterImpl; import org.lushplugins.pluginupdater.common.config.deserializer.PluginDataDeserializer; -import org.lushplugins.pluginupdater.common.notifier.DiscordWebhookNotifier; +import org.lushplugins.pluginupdater.common.notifier.DiscordWebHookNotifier; import org.lushplugins.pluginupdater.common.updater.UpdateHandler; import org.lushplugins.pluginupdater.common.util.ConfigUtil; @@ -23,7 +23,6 @@ public class ConfigManager { private final Map plugins = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); private final Set disabledPlugins = new HashSet<>(); private Messages messages; - private boolean discordWebhookEnabled; private String discordWebhookUrl; public ConfigManager(UpdaterImpl updater) { @@ -51,10 +50,13 @@ public void reload(boolean skipCheck) { Config discordConfig = config.get("discord-webhook"); if (discordConfig != null) { - this.discordWebhookEnabled = discordConfig.getOrElse("enabled", false); - this.discordWebhookUrl = discordConfig.getOrElse("webhook-url", ""); + boolean discordWebhookEnabled = discordConfig.getOrElse("enabled", false); + if (!discordWebhookEnabled) { + discordWebhookUrl = ""; + } else { + this.discordWebhookUrl = discordConfig.getOrElse("webhook-url", ""); + } } else { - this.discordWebhookEnabled = false; this.discordWebhookUrl = ""; } @@ -102,15 +104,17 @@ public void reload(boolean skipCheck) { } }); - // Reinitialize Discord webhook notifier with new config - if (updater.discordWebhookNotifier() != null) { - updater.discordWebhookNotifier().close(); + updater.discordWebHookNotifier().ifPresent(DiscordWebHookNotifier::shutdown); + if (this.discordWebhookUrl != null && !this.discordWebhookUrl.isBlank()) { + try { + updater.discordWebHookNotifier(new DiscordWebHookNotifier( + updater.updaterPlugin().getComponentLogger(), + this.discordWebhookUrl + )); + } catch (Exception e) { + updater.discordWebHookNotifier(null); + } } - updater.setDiscordWebhookNotifier(new DiscordWebhookNotifier( - updater.updaterPlugin().getComponentLogger(), - this.discordWebhookEnabled, - this.discordWebhookUrl - )); config.close(); } @@ -163,11 +167,7 @@ public String getMessage(String name, String def) { return messages.get(name, def); } - public boolean isDiscordWebhookEnabled() { - return discordWebhookEnabled; - } - - public String getDiscordWebhookUrl() { + public String getDiscordWebHookUrl() { return discordWebhookUrl; } diff --git a/common/impl/src/main/java/org/lushplugins/pluginupdater/common/notifier/DiscordWebhookNotifier.java b/common/impl/src/main/java/org/lushplugins/pluginupdater/common/notifier/DiscordWebHookNotifier.java similarity index 71% rename from common/impl/src/main/java/org/lushplugins/pluginupdater/common/notifier/DiscordWebhookNotifier.java rename to common/impl/src/main/java/org/lushplugins/pluginupdater/common/notifier/DiscordWebHookNotifier.java index b45430c4..15d1cf52 100644 --- a/common/impl/src/main/java/org/lushplugins/pluginupdater/common/notifier/DiscordWebhookNotifier.java +++ b/common/impl/src/main/java/org/lushplugins/pluginupdater/common/notifier/DiscordWebHookNotifier.java @@ -5,60 +5,52 @@ import io.github._4drian3d.jdwebhooks.webhook.WebHookClient; import io.github._4drian3d.jdwebhooks.webhook.WebHookExecution; import net.kyori.adventure.text.logger.slf4j.ComponentLogger; -import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.NotNull; import org.lushplugins.pluginupdater.api.updater.PluginData; import org.lushplugins.pluginupdater.api.version.Version; +import org.lushplugins.pluginupdater.api.version.VersionDifference; import java.util.ArrayList; import java.util.List; import java.util.Optional; -public class DiscordWebhookNotifier { +public class DiscordWebHookNotifier { private final ComponentLogger logger; - private final WebHookClient webhookClient; - private final boolean enabled; + private final WebHookClient webHookClient; - public DiscordWebhookNotifier(ComponentLogger logger, boolean enabled, @Nullable String webhookUrl) { + public DiscordWebHookNotifier(ComponentLogger logger, @NotNull String webhookUrl) { this.logger = logger; - this.enabled = enabled && webhookUrl != null && !webhookUrl.isBlank(); - if (this.enabled) { - this.webhookClient = WebHookClient.fromURL(webhookUrl); - } else { - this.webhookClient = null; + try { + this.webHookClient = WebHookClient.fromURL(webhookUrl); + } catch (Exception e) { + logger.warn("Failed to create Discord webhook client. Webhook notifications will be disabled. Possible wrong url.", e); + throw e; } } public void notifyDownload(PluginData pluginData) { - if (!enabled || webhookClient == null) { - return; - } - try { Version currentVersion = pluginData.currentVersion(); - Optional latestVersionOpt = pluginData.latestVersion(); - - if (latestVersionOpt.isEmpty()) { + Optional latestVersionOptional = pluginData.latestVersion(); + if (latestVersionOptional.isEmpty()) { return; } - Version latestVersion = latestVersionOpt.get(); String pluginName = pluginData.pluginName(); String versionString = String.format("%s → %s", currentVersion.rawVersionString(), - latestVersion.rawVersionString() + latestVersionOptional.get().rawVersionString() ); List components = new ArrayList<>(); - components.add(Component.textDisplay("**" + pluginName + " Updated**")); - components.add(Component.textDisplay("**Version:** " + versionString)); - String versionDiff = pluginData.versionDifference().name(); - if (!versionDiff.equals("UNKNOWN")) { - components.add(Component.textDisplay("**Update Type:** " + versionDiff)); + VersionDifference versionDiff = pluginData.versionDifference(); + if (versionDiff != VersionDifference.UNKNOWN) { + components.add(Component.textDisplay("**Update Type:** " + versionDiff.name())); } pluginData.getChangelogUrl().ifPresent(s -> components.add(Component.textDisplay("**Changelog:** [View Changelog](" + s + ")"))); @@ -74,7 +66,7 @@ public void notifyDownload(PluginData pluginData) { ) .build(); - webhookClient.executeWebHook(webHook) + webHookClient.executeWebHook(webHook) .whenComplete((response, throwable) -> { int statusCode = response.statusCode(); @@ -90,7 +82,7 @@ public void notifyDownload(PluginData pluginData) { } } - public void close() { + public void shutdown() { // Might come handy in the future } } diff --git a/common/impl/src/main/java/org/lushplugins/pluginupdater/common/updater/UpdateHandler.java b/common/impl/src/main/java/org/lushplugins/pluginupdater/common/updater/UpdateHandler.java index d02e2437..34f9da5f 100644 --- a/common/impl/src/main/java/org/lushplugins/pluginupdater/common/updater/UpdateHandler.java +++ b/common/impl/src/main/java/org/lushplugins/pluginupdater/common/updater/UpdateHandler.java @@ -121,11 +121,8 @@ private void processQueue() { if (pluginData.downloadUpdate(updater.updaterPlugin().getDownloadDir())) { pluginData.versionDifference(VersionDifference.UNKNOWN); pluginData.setAlreadyDownloaded(true); - - // Send Discord webhook notification - if (updater.discordWebhookNotifier() != null) { - updater.discordWebhookNotifier().notifyDownload(pluginData); - } + + updater.discordWebHookNotifier().ifPresent(notifier -> notifier.notifyDownload(pluginData)); processingData.getFuture().complete(true); break; From a07b634fdc1a2a861c7ca5df636f4df43a1b359e Mon Sep 17 00:00:00 2001 From: Oak Date: Tue, 22 Sep 2026 22:24:14 +0100 Subject: [PATCH 3/4] Implemented Notifier to allow for future alternative notification types --- .../pluginupdater/common/UpdaterImpl.java | 20 +----- .../common/config/ConfigManager.java | 28 ++++---- .../deserializer/NotifierDeserializer.java | 15 +++++ .../notifier/DiscordWebHookNotifier.java | 12 ++-- .../common/notifier/Notifier.java | 10 +++ .../common/notifier/NotifierRegistry.java | 65 +++++++++++++++++++ .../common/updater/UpdateHandler.java | 2 +- common/impl/src/main/resources/config.yml | 11 ++-- 8 files changed, 119 insertions(+), 44 deletions(-) create mode 100644 common/impl/src/main/java/org/lushplugins/pluginupdater/common/config/deserializer/NotifierDeserializer.java create mode 100644 common/impl/src/main/java/org/lushplugins/pluginupdater/common/notifier/Notifier.java create mode 100644 common/impl/src/main/java/org/lushplugins/pluginupdater/common/notifier/NotifierRegistry.java diff --git a/common/impl/src/main/java/org/lushplugins/pluginupdater/common/UpdaterImpl.java b/common/impl/src/main/java/org/lushplugins/pluginupdater/common/UpdaterImpl.java index 61348ec0..85ca68fc 100644 --- a/common/impl/src/main/java/org/lushplugins/pluginupdater/common/UpdaterImpl.java +++ b/common/impl/src/main/java/org/lushplugins/pluginupdater/common/UpdaterImpl.java @@ -27,7 +27,6 @@ public class UpdaterImpl { private final List collectors; private final UpdateHandler updateHandler; private final ConfigManager config; - private DiscordWebHookNotifier discordWebHookNotifier; public UpdaterImpl(UpdaterPlatform platform, UpdaterPlugin updaterPlugin, CommandHandler commandPlatform, List collectors) { this.platform = platform; @@ -41,17 +40,6 @@ public UpdaterImpl(UpdaterPlatform platform, UpdaterPlugin updaterPlugin, Com config = new ConfigManager(this); config.reload(); - if (config.getDiscordWebHookUrl() != null && !config.getDiscordWebHookUrl().isBlank()) { - try { - discordWebHookNotifier = new DiscordWebHookNotifier( - updaterPlugin.getComponentLogger(), - config.getDiscordWebHookUrl() - ); - } catch (Exception e) { - discordWebHookNotifier = null; - } - } - Lamp lamp = commandPlatform.prepareLamp() .permissionFactory(new CommandPermissionFactory(this)) .parameterTypes(parameterTypes -> parameterTypes @@ -93,9 +81,7 @@ public UpdaterImpl(UpdaterPlatform platform, UpdaterPlugin updaterPlugin, Com public void shutdown() { updateHandler.shutdown(); - if (discordWebHookNotifier != null) { - discordWebHookNotifier.shutdown(); - } + config.shutdownNotifiers(); } public UpdaterPlatform platform() { @@ -126,10 +112,6 @@ public Optional discordWebHookNotifier() { return Optional.ofNullable(discordWebHookNotifier); } - public void discordWebHookNotifier(DiscordWebHookNotifier notifier) { - this.discordWebHookNotifier = notifier; - } - public CompletableFuture> collectUnknownPlugins() { return CompletableFuture.supplyAsync(() -> { Map unknownPlugins = platform.getPlugins().stream() diff --git a/common/impl/src/main/java/org/lushplugins/pluginupdater/common/config/ConfigManager.java b/common/impl/src/main/java/org/lushplugins/pluginupdater/common/config/ConfigManager.java index 1f4a3b4f..decc0f48 100644 --- a/common/impl/src/main/java/org/lushplugins/pluginupdater/common/config/ConfigManager.java +++ b/common/impl/src/main/java/org/lushplugins/pluginupdater/common/config/ConfigManager.java @@ -8,7 +8,8 @@ import org.lushplugins.pluginupdater.api.updater.PluginData; import org.lushplugins.pluginupdater.common.UpdaterImpl; import org.lushplugins.pluginupdater.common.config.deserializer.PluginDataDeserializer; -import org.lushplugins.pluginupdater.common.notifier.DiscordWebHookNotifier; +import org.lushplugins.pluginupdater.common.notifier.Notifier; +import org.lushplugins.pluginupdater.common.notifier.NotifierRegistry; import org.lushplugins.pluginupdater.common.updater.UpdateHandler; import org.lushplugins.pluginupdater.common.util.ConfigUtil; @@ -24,6 +25,7 @@ public class ConfigManager { private final Set disabledPlugins = new HashSet<>(); private Messages messages; private String discordWebhookUrl; + private List notifiers; public ConfigManager(UpdaterImpl updater) { this.updater = updater; @@ -104,16 +106,11 @@ public void reload(boolean skipCheck) { } }); - updater.discordWebHookNotifier().ifPresent(DiscordWebHookNotifier::shutdown); - if (this.discordWebhookUrl != null && !this.discordWebhookUrl.isBlank()) { - try { - updater.discordWebHookNotifier(new DiscordWebHookNotifier( - updater.updaterPlugin().getComponentLogger(), - this.discordWebhookUrl - )); - } catch (Exception e) { - updater.discordWebHookNotifier(null); - } + this.notifiers.forEach(Notifier::shutdown); + if (config.contains("notifications")) { + this.notifiers = NotifierRegistry.deserializeNotifiers(updater, config.get("notifications")); + } else { + this.notifiers = Collections.emptyList(); } config.close(); @@ -171,6 +168,15 @@ public String getDiscordWebHookUrl() { return discordWebhookUrl; } + public List getNotifiers() { + return notifiers; + } + + public void shutdownNotifiers() { + notifiers.forEach(Notifier::shutdown); + notifiers.clear(); + } + public record Messages(Map messages) { diff --git a/common/impl/src/main/java/org/lushplugins/pluginupdater/common/config/deserializer/NotifierDeserializer.java b/common/impl/src/main/java/org/lushplugins/pluginupdater/common/config/deserializer/NotifierDeserializer.java new file mode 100644 index 00000000..2c83e4b8 --- /dev/null +++ b/common/impl/src/main/java/org/lushplugins/pluginupdater/common/config/deserializer/NotifierDeserializer.java @@ -0,0 +1,15 @@ +package org.lushplugins.pluginupdater.common.config.deserializer; + +import com.electronwill.nightconfig.core.Config; +import org.jetbrains.annotations.Nullable; +import org.lushplugins.pluginupdater.common.UpdaterImpl; +import org.lushplugins.pluginupdater.common.notifier.DiscordWebHookNotifier; +import org.lushplugins.pluginupdater.common.notifier.Notifier; + +public class NotifierDeserializer { + + public static @Nullable Notifier discordWebHookNotifier(UpdaterImpl updater, Config config) { + String webHookUrl = config.get("webhook-url"); + return !webHookUrl.isBlank() ? new DiscordWebHookNotifier(updater.updaterPlugin().getComponentLogger(), webHookUrl) : null; + } +} diff --git a/common/impl/src/main/java/org/lushplugins/pluginupdater/common/notifier/DiscordWebHookNotifier.java b/common/impl/src/main/java/org/lushplugins/pluginupdater/common/notifier/DiscordWebHookNotifier.java index 15d1cf52..20665688 100644 --- a/common/impl/src/main/java/org/lushplugins/pluginupdater/common/notifier/DiscordWebHookNotifier.java +++ b/common/impl/src/main/java/org/lushplugins/pluginupdater/common/notifier/DiscordWebHookNotifier.java @@ -14,7 +14,7 @@ import java.util.List; import java.util.Optional; -public class DiscordWebHookNotifier { +public class DiscordWebHookNotifier implements Notifier { private final ComponentLogger logger; private final WebHookClient webHookClient; @@ -29,6 +29,7 @@ public DiscordWebHookNotifier(ComponentLogger logger, @NotNull String webhookUrl } } + @Override public void notifyDownload(PluginData pluginData) { try { Version currentVersion = pluginData.currentVersion(); @@ -39,10 +40,9 @@ public void notifyDownload(PluginData pluginData) { String pluginName = pluginData.pluginName(); - String versionString = String.format("%s → %s", + String versionString = "%s → %s".formatted( currentVersion.rawVersionString(), - latestVersionOptional.get().rawVersionString() - ); + latestVersionOptional.get().rawVersionString()); List components = new ArrayList<>(); components.add(Component.textDisplay("**" + pluginName + " Updated**")); @@ -81,8 +81,4 @@ public void notifyDownload(PluginData pluginData) { logger.warn("Failed to send Discord webhook notification", e); } } - - public void shutdown() { - // Might come handy in the future - } } diff --git a/common/impl/src/main/java/org/lushplugins/pluginupdater/common/notifier/Notifier.java b/common/impl/src/main/java/org/lushplugins/pluginupdater/common/notifier/Notifier.java new file mode 100644 index 00000000..5bf405c0 --- /dev/null +++ b/common/impl/src/main/java/org/lushplugins/pluginupdater/common/notifier/Notifier.java @@ -0,0 +1,10 @@ +package org.lushplugins.pluginupdater.common.notifier; + +import org.lushplugins.pluginupdater.api.updater.PluginData; + +public interface Notifier { + + void notifyDownload(PluginData pluginData); + + default void shutdown() {} +} diff --git a/common/impl/src/main/java/org/lushplugins/pluginupdater/common/notifier/NotifierRegistry.java b/common/impl/src/main/java/org/lushplugins/pluginupdater/common/notifier/NotifierRegistry.java new file mode 100644 index 00000000..859669e6 --- /dev/null +++ b/common/impl/src/main/java/org/lushplugins/pluginupdater/common/notifier/NotifierRegistry.java @@ -0,0 +1,65 @@ +package org.lushplugins.pluginupdater.common.notifier; + +import com.electronwill.nightconfig.core.Config; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.lushplugins.pluginupdater.common.UpdaterImpl; +import org.lushplugins.pluginupdater.common.config.deserializer.NotifierDeserializer; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.BiFunction; + +public class NotifierRegistry { + private static final Map, Config, Notifier>> notifiers = new HashMap<>(); + + static { + register("discord-webhook", NotifierDeserializer::discordWebHookNotifier); + } + + public static @Nullable Notifier deserializeNotifier(UpdaterImpl updater, String id, Config config) { + if (config.get("enabled") == Boolean.FALSE) { + return null; + } + + try { + return notifiers.containsKey(id) ? notifiers.get(id).apply(updater, config) : null; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + public static List deserializeNotifiers(UpdaterImpl updater, Config config) { + return config.entrySet().stream() + .map(section -> { + if (section.getValue() instanceof Config sectionConfig) { + String key = section.getKey(); + if (notifiers.containsKey(key)) { + return deserializeNotifier(updater, key, sectionConfig); + } else { + String type = sectionConfig.get("type"); + return deserializeNotifier(updater, type, sectionConfig); + } + } + + return null; + }) + .filter(Objects::nonNull) + .toList(); + } + + public static void register(@NotNull String id, BiFunction, Config, Notifier> constructor) { + if (notifiers.containsKey(id)) { + throw new IllegalStateException("This notifier is already registered"); + } + + notifiers.put(id, constructor); + } + + public static void unregister(String id) { + notifiers.remove(id); + } +} diff --git a/common/impl/src/main/java/org/lushplugins/pluginupdater/common/updater/UpdateHandler.java b/common/impl/src/main/java/org/lushplugins/pluginupdater/common/updater/UpdateHandler.java index 34f9da5f..093214bd 100644 --- a/common/impl/src/main/java/org/lushplugins/pluginupdater/common/updater/UpdateHandler.java +++ b/common/impl/src/main/java/org/lushplugins/pluginupdater/common/updater/UpdateHandler.java @@ -122,7 +122,7 @@ private void processQueue() { pluginData.versionDifference(VersionDifference.UNKNOWN); pluginData.setAlreadyDownloaded(true); - updater.discordWebHookNotifier().ifPresent(notifier -> notifier.notifyDownload(pluginData)); + updater.config().getNotifiers().forEach(notifier -> notifier.notifyDownload(pluginData)); processingData.getFuture().complete(true); break; diff --git a/common/impl/src/main/resources/config.yml b/common/impl/src/main/resources/config.yml index 2484bfa7..ec0dab65 100644 --- a/common/impl/src/main/resources/config.yml +++ b/common/impl/src/main/resources/config.yml @@ -3,11 +3,6 @@ check-updates-on-reload: true # Set to 'false' to disable all download features of PluginUpdater (May require restart) allow-downloads: true -# Discord webhook configuration for download notifications -discord-webhook: - enabled: false - webhook-url: "" - # To learn how to configure plugins that require different sources check out the wiki: https://github.com/OakLoaf/PluginUpdater/wiki/PluginUpdater-Plugin plugins: PluginUpdater: @@ -17,6 +12,12 @@ plugins: ExamplePlugin: enabled: false +# Notifications for downloads +notifications: + discord-webhook: + enabled: false + webhook-url: "" + messages: unchecked-color: "" update-available-color: "<#ffda54>" From bc75436a9283d0b33d71f50b2e743bfae56b5d5e Mon Sep 17 00:00:00 2001 From: Oak Date: Tue, 22 Sep 2026 22:24:29 +0100 Subject: [PATCH 4/4] Removed outdated code --- .../org/lushplugins/pluginupdater/common/UpdaterImpl.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/common/impl/src/main/java/org/lushplugins/pluginupdater/common/UpdaterImpl.java b/common/impl/src/main/java/org/lushplugins/pluginupdater/common/UpdaterImpl.java index 85ca68fc..6f6a6829 100644 --- a/common/impl/src/main/java/org/lushplugins/pluginupdater/common/UpdaterImpl.java +++ b/common/impl/src/main/java/org/lushplugins/pluginupdater/common/UpdaterImpl.java @@ -108,10 +108,6 @@ public ConfigManager config() { return config; } - public Optional discordWebHookNotifier() { - return Optional.ofNullable(discordWebHookNotifier); - } - public CompletableFuture> collectUnknownPlugins() { return CompletableFuture.supplyAsync(() -> { Map unknownPlugins = platform.getPlugins().stream()