Skip to content

Commit

Permalink
feat: add update checker
Browse files Browse the repository at this point in the history
  • Loading branch information
yusshu committed Feb 3, 2024
1 parent 1eff78b commit 2cbe660
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import team.unnamed.creativeglyphs.plugin.listener.chat.ChatCompletionsListener;
import team.unnamed.creativeglyphs.plugin.listener.bus.EventBus;
import team.unnamed.creativeglyphs.plugin.listener.ListenerFactory;
import team.unnamed.creativeglyphs.plugin.util.GitHubUpdateChecker;
import team.unnamed.creativeglyphs.resourcepack.ResourcePackGlyphWriter;

import java.io.IOException;
Expand Down Expand Up @@ -130,6 +131,9 @@ public void onEnable() {

// Metrics
new Metrics(this, 17168);

// GitHub Update Checker
GitHubUpdateChecker.checkAsync(this);
}

private void listen(Listener listener) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package team.unnamed.creativeglyphs.plugin.util;

import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;

import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;

public final class GitHub {
private static final JsonParser JSON_PARSER = new JsonParser();

private GitHub() {
}

/**
* Fetches and returns the latest release tag name from the specified repository
* using the GitHub API.
*
* <p>Note that, conventionally, the returned tag version may differ from the plugin
* version because tags may start with 'v' while the plugin version may not, but still
* be the same version.</p>
*
* @param repositoryOwner The owner of the repository
* @param repositoryName The name of the repository
* @return The latest release tag name
* @throws IOException If an I/O error occurs
*/
public static @NotNull String fetchLatestReleaseTagName(final @NotNull String repositoryOwner, final @NotNull String repositoryName) throws IOException {
Plugin plugin;
try {
plugin = JavaPlugin.getProvidingPlugin(GitHub.class);
} catch (final IllegalArgumentException e) {
// not provided by a plugin
plugin = null;
}

final var url = new URL("https://api.github.com/repos/" + repositoryOwner + "/" + repositoryName + "/releases/latest");
final var connection = (HttpURLConnection) url.openConnection();

connection.setRequestMethod("GET");
connection.setRequestProperty("Accept", "application/vnd.github+json");

if (plugin != null) {
//noinspection deprecation
connection.setRequestProperty("User-Agent", plugin.getName() + "/" + plugin.getDescription().getVersion());
}

// Execute and read response
final JsonObject json;
try (final var responseReader = new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8)) {
json = JSON_PARSER.parse(responseReader).getAsJsonObject();
}

return json.get("tag_name").getAsString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package team.unnamed.creativeglyphs.plugin.util;

import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.format.TextColor;
import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionDefault;
import org.jetbrains.annotations.NotNull;
import team.unnamed.creativeglyphs.plugin.CreativeGlyphsPlugin;

import java.io.IOException;
import java.util.logging.Level;

public final class GitHubUpdateChecker {
private static final Permission UPDATE_NOTIFICATION_PERMISSION = new Permission(
"creativeglyphs.update",
"Allows the player to receive update notifications",
PermissionDefault.OP
);

private static final String DOWNLOAD_URL = "https://spigotmc.org/resources/113722/";

public static void checkAsync(final @NotNull CreativeGlyphsPlugin plugin) {
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> check(plugin));
}

public static void check(final @NotNull CreativeGlyphsPlugin plugin) {
// Don't check for updates if the user has disabled it
if (!plugin.getConfig().getBoolean("check-for-updates")) {
return;
}

// Check for updates
final String latestTagName;
try {
latestTagName = GitHub.fetchLatestReleaseTagName("unnamed", "creative-glyphs");
} catch (final IOException e) {
plugin.getLogger().log(Level.WARNING, "Failed to check for updates", e);
return;
}

//noinspection deprecation
final String currentVersion = plugin.getDescription().getVersion();

if (currentVersion.equals(latestTagName)
|| (latestTagName.startsWith("v") && currentVersion.equals(latestTagName.substring(1)))) {
// We are up-to-date!
return;
}

// Register the update notifier
Bukkit.getPluginManager().registerEvents(new UpdateAvailableNotifier(latestTagName), plugin);

// Notify the console and online players
notifyUpdateAvailable(Bukkit.getConsoleSender(), latestTagName);

for (final var player : Bukkit.getOnlinePlayers()) {
if (player.hasPermission(UPDATE_NOTIFICATION_PERMISSION)) {
notifyUpdateAvailable(player, latestTagName);
}
}
}

private static void notifyUpdateAvailable(final @NotNull Audience audience, final @NotNull String latestVersion) {
final var pink = TextColor.color(0xff8df8);
final var purple = TextColor.color(0xB545FF);
audience.sendMessage(
Component.text()
.append(Component.text(" [!] ", pink))
.append(Component.text("An update is available for "))
.append(Component.text("creative-glyphs", pink))
.append(Component.text(" ("))
.append(Component.text(latestVersion, pink))
.append(Component.text(")"))
.append(Component.newline())
.append(Component.text()
.content("Click to download")
.color(purple)
.hoverEvent(HoverEvent.showText(Component.text(DOWNLOAD_URL, pink)))
.clickEvent(ClickEvent.openUrl(DOWNLOAD_URL)))
.build());
}

private static class UpdateAvailableNotifier implements Listener {
private final String latestVersion;

public UpdateAvailableNotifier(final @NotNull String latestVersion) {
this.latestVersion = latestVersion;
}

@EventHandler
public void onJoin(final @NotNull PlayerJoinEvent event) {
final var player = event.getPlayer();
if (player.hasPermission(UPDATE_NOTIFICATION_PERMISSION)) {
notifyUpdateAvailable(player, latestVersion);
}
}
}
}
3 changes: 3 additions & 0 deletions plugin/src/main/resources/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
# - Discord: https://discord.gg/xbba2fy
#

# Should the plugin check for updates? (Only checks at startup)
check-for-updates: true

format:

# The text displayed when the player hovers an emoji
Expand Down

0 comments on commit 2cbe660

Please sign in to comment.