diff --git a/README.md b/README.md index ba8bfe2..c09a551 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,36 @@ -# smartcraft-notifier +# SmartCraft Notifier +A lightweight Paper plugin that sends player events (join, leave, server switch, and advancements) to a Gotify server via HTTP notifications. Designed for Minecraft 1.19+ networks using BungeeCord or standalone setups. + +## 🧰 Features + +- Optional BungeeCord integration via Plugin Messaging Channel +- Detects player join/leave and advancement events +- Sends customizable notifications to Gotify servers +- Configurable via `config.yml` (no in-game commands except `/gotifystatus`) +- Console/log alert if Gotify connection fails at startup + +## βš™οΈ Requirements + +- Minecraft Paper server 1.19+ +- Java 17+ +- [Gotify](https://gotify.net/) server for receiving notifications +- If using BungeeCord, set `bungeecord: true` in `spigot.yml` and enable the flag in the plugin config + +## πŸ”§ Build Instructions + +1. Install [Apache Maven](https://maven.apache.org/) +2. Run `build.bat` (Windows) or `mvn clean package` (Linux/macOS) +3. Your compiled plugin will be at `target/SmartCraftNotifier.jar` + +## πŸš€ Usage + +- Drop the plugin into your server’s `plugins/` folder +- Edit `config.yml` to set your Gotify server URL, app token, and feature toggles +- Restart your server to generate logs or test with `/gotifystatus` + +--- + +🧠 Inspired by the idea of keeping server owners effortlessly informed. + +*Built with care by Eric’s Copilot.* \ No newline at end of file diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..3b8b8ee --- /dev/null +++ b/build.bat @@ -0,0 +1,14 @@ +@echo off +echo Building SmartCraft Notifier... + +REM Run Maven clean & package +mvn clean package + +IF %ERRORLEVEL% NEQ 0 ( + echo Build failed! Check errors above. + pause + exit /b %ERRORLEVEL% +) + +echo Build completed! Check the 'target' folder for your .jar +pause \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..9b60519 --- /dev/null +++ b/pom.xml @@ -0,0 +1,59 @@ + + 4.0.0 + + com.smartcraft.notifier + SmartCraftNotifier + 1.0.0 + SmartCraftNotifier + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.10.1 + + 17 + 17 + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.3.0 + + + + com.smartcraft.notifier.SmartCraftNotifier + + + + + + + + + + spigot-repo + https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + + + + + + org.spigotmc + spigot-api + 1.20-R0.1-SNAPSHOT + provided + + + com.squareup.okhttp3 + okhttp + 4.12.0 + + + \ No newline at end of file diff --git a/src/main/java/com/smartcraft/notifier/AdvancementsListener.java b/src/main/java/com/smartcraft/notifier/AdvancementsListener.java new file mode 100644 index 0000000..c05e383 --- /dev/null +++ b/src/main/java/com/smartcraft/notifier/AdvancementsListener.java @@ -0,0 +1,23 @@ +package com.smartcraft.notifier; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerAdvancementDoneEvent; + +public class AdvancementListener implements Listener { + + @EventHandler + public void onAdvancement(PlayerAdvancementDoneEvent event) { + String advancementKey = event.getAdvancement().getKey().getKey(); + String playerName = event.getPlayer().getName(); + + // Filter out root advancements or hidden criteria + if (advancementKey.contains("root") || advancementKey.contains("recipes")) return; + + SmartCraftNotifier.instance.getGotifyClient().sendMessage( + "πŸ“œ Advancement Unlocked!", + playerName + " has earned: " + advancementKey, + 5 + ); + } +} \ No newline at end of file diff --git a/src/main/java/com/smartcraft/notifier/BungeeMessageListener.java b/src/main/java/com/smartcraft/notifier/BungeeMessageListener.java new file mode 100644 index 0000000..5acead2 --- /dev/null +++ b/src/main/java/com/smartcraft/notifier/BungeeMessageListener.java @@ -0,0 +1,50 @@ +package com.smartcraft.notifier; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.plugin.messaging.PluginMessageListener; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; + +public class BungeeMessageListener implements PluginMessageListener { + + @Override + public void onPluginMessageReceived(String channel, Player player, byte[] message) { + if (!channel.equals("BungeeCord")) return; + + try (DataInputStream in = new DataInputStream(new ByteArrayInputStream(message))) { + String subChannel = in.readUTF(); + + if (subChannel.equals("PlayerJoin")) { + String joinedPlayer = in.readUTF(); + SmartCraftNotifier.instance.getGotifyClient().sendMessage( + "πŸ‘€ Player Joined", + joinedPlayer + " connected to the network.", + 4 + ); + + } else if (subChannel.equals("PlayerQuit")) { + String leftPlayer = in.readUTF(); + SmartCraftNotifier.instance.getGotifyClient().sendMessage( + "πŸšͺ Player Left", + leftPlayer + " disconnected from the network.", + 4 + ); + + } else if (subChannel.equals("ServerSwitch")) { + String playerName = in.readUTF(); + String fromServer = in.readUTF(); + String toServer = in.readUTF(); + SmartCraftNotifier.instance.getGotifyClient().sendMessage( + "πŸ” Server Switch", + playerName + " switched from " + fromServer + " to " + toServer, + 3 + ); + } + } catch (IOException e) { + Bukkit.getLogger().warning("[SmartCraftNotifier] Failed to parse BungeeCord message: " + e.getMessage()); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/smartcraft/notifier/GotifyClient.java b/src/main/java/com/smartcraft/notifier/GotifyClient.java new file mode 100644 index 0000000..6b92b79 --- /dev/null +++ b/src/main/java/com/smartcraft/notifier/GotifyClient.java @@ -0,0 +1,75 @@ +package com.smartcraft.notifier; + +import okhttp3.*; +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.FileConfiguration; + +import java.io.IOException; + +public class GotifyClient { + + private final String serverUrl; + private final String appToken; + private final boolean enabled; + private final OkHttpClient http; + + public GotifyClient(FileConfiguration config) { + this.enabled = config.getBoolean("gotify.enabled", false); + this.serverUrl = config.getString("gotify.serverUrl", "").trim(); + this.appToken = config.getString("gotify.appToken", "").trim(); + this.http = new OkHttpClient(); + } + + public boolean isReady() { + return enabled && !serverUrl.isEmpty() && !appToken.isEmpty(); + } + + public void sendMessage(String title, String message, int priority) { + if (!isReady()) return; + + HttpUrl url = HttpUrl.parse(serverUrl + "/message"); + if (url == null) return; + + RequestBody body = new FormBody.Builder() + .add("title", title) + .add("message", message) + .add("priority", String.valueOf(priority)) + .build(); + + Request request = new Request.Builder() + .url(url) + .addHeader("X-Gotify-Key", appToken) + .post(body) + .build(); + + http.newCall(request).enqueue(new Callback() { + @Override + public void onFailure(Call call, IOException e) { + Bukkit.getLogger().warning("[SmartCraftNotifier] Failed to send Gotify message: " + e.getMessage()); + } + + @Override + public void onResponse(Call call, Response response) { + response.close(); + } + }); + } + + public boolean pingServer() { + if (!isReady()) return false; + + HttpUrl url = HttpUrl.parse(serverUrl + "/application"); + if (url == null) return false; + + Request request = new Request.Builder() + .url(url) + .addHeader("X-Gotify-Key", appToken) + .build(); + + try (Response response = http.newCall(request).execute()) { + return response.isSuccessful(); + } catch (IOException e) { + return false; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/smartcraft/notifier/SmartCraftNotifier.java b/src/main/java/com/smartcraft/notifier/SmartCraftNotifier.java new file mode 100644 index 0000000..3ea0127 --- /dev/null +++ b/src/main/java/com/smartcraft/notifier/SmartCraftNotifier.java @@ -0,0 +1,59 @@ +package com.smartcraft.notifier; + +import org.bukkit.Bukkit; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.plugin.messaging.Messenger; + +public class SmartCraftNotifier extends JavaPlugin { + + public static SmartCraftNotifier instance; + private GotifyClient gotifyClient; + + @Override + public void onEnable() { + instance = this; + + saveDefaultConfig(); + FileConfiguration config = getConfig(); + + this.gotifyClient = new GotifyClient(config); + + if (!gotifyClient.isReady()) { + getLogger().warning("Gotify is not properly configured. Notifications will not be sent."); + } else { + getLogger().info("Gotify is ready to send messages."); + } + + // Event listeners + if (config.getBoolean("events.logAdvancements", true)) { + getServer().getPluginManager().registerEvents(new AdvancementListener(), this); + } + + // BungeeCord plugin messaging setup + if (config.getBoolean("bungeecord", false)) { + Messenger messenger = getServer().getMessenger(); + messenger.registerOutgoingPluginChannel(this, "BungeeCord"); + messenger.registerIncomingPluginChannel(this, "BungeeCord", new BungeeMessageListener()); + getLogger().info("BungeeCord messaging enabled."); + } + + // Register command + getCommand("gotifystatus").setExecutor((sender, command, label, args) -> { + boolean result = gotifyClient.pingServer(); + sender.sendMessage("Gotify status: " + (result ? "βœ… Online" : "❌ Unreachable")); + return true; + }); + + getLogger().info("SmartCraft Notifier has been enabled."); + } + + @Override + public void onDisable() { + getLogger().info("SmartCraft Notifier has been disabled."); + } + + public GotifyClient getGotifyClient() { + return gotifyClient; + } +} \ No newline at end of file diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..03afc45 --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,12 @@ +bungeecord: false + +gotify: + enabled: true + serverUrl: "https://your.gotify.server" + appToken: "YOUR_SECRET_TOKEN" + +events: + logJoins: true + logLeaves: true + logServerSwitch: true + logAdvancements: true \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..be93361 --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,14 @@ +name: SmartCraftNotifier +version: 1.0.0 +main: com.smartcraft.notifier.SmartCraftNotifier +api-version: 1.20 +description: Sends player events to Gotify and supports optional BungeeCord messaging. +commands: + gotifystatus: + description: Check if Gotify is reachable. + usage: /gotifystatus + permission: smartcraftnotifier.status +permissions: + smartcraftnotifier.status: + description: Allows use of /gotifystatus command + default: op \ No newline at end of file diff --git a/temp.temp b/temp.temp deleted file mode 100644 index 255afb5..0000000 --- a/temp.temp +++ /dev/null @@ -1 +0,0 @@ -6:26 6/19/2025 \ No newline at end of file