From 0b9dc5358d1d6c45319556f353fa169ea26cca07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20M=C3=BCller?= Date: Sat, 15 Nov 2025 12:50:01 +0100 Subject: [PATCH] added HTTP hooks framework with actions for signup, report, and strike events; introduced `SpawnEvent` support for event broadcasting --- .../spawn/core/api/server/HttpServer.java | 8 ++- .../spawn/core/api/server/hooks/HttpHook.java | 55 +++++++++++++++++++ .../core/api/server/hooks/JsonAction.java | 26 +++++++++ .../core/api/server/hooks/RawAction.java | 18 ++++++ .../api/server/hooks/impl/WebsiteHook.java | 43 +++++++++++++++ .../spawn/core/event/ReportCreatedEvent.java | 15 +++++ .../spawn/core/event/SpawnEvent.java | 22 ++++++++ .../spawn/core/event/StrikeCreatedEvent.java | 17 ++++++ 8 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 core/src/main/java/eu/mhsl/craftattack/spawn/core/api/server/hooks/HttpHook.java create mode 100644 core/src/main/java/eu/mhsl/craftattack/spawn/core/api/server/hooks/JsonAction.java create mode 100644 core/src/main/java/eu/mhsl/craftattack/spawn/core/api/server/hooks/RawAction.java create mode 100644 core/src/main/java/eu/mhsl/craftattack/spawn/core/api/server/hooks/impl/WebsiteHook.java create mode 100644 core/src/main/java/eu/mhsl/craftattack/spawn/core/event/ReportCreatedEvent.java create mode 100644 core/src/main/java/eu/mhsl/craftattack/spawn/core/event/SpawnEvent.java create mode 100644 core/src/main/java/eu/mhsl/craftattack/spawn/core/event/StrikeCreatedEvent.java diff --git a/core/src/main/java/eu/mhsl/craftattack/spawn/core/api/server/HttpServer.java b/core/src/main/java/eu/mhsl/craftattack/spawn/core/api/server/HttpServer.java index b6f2128..1b86556 100644 --- a/core/src/main/java/eu/mhsl/craftattack/spawn/core/api/server/HttpServer.java +++ b/core/src/main/java/eu/mhsl/craftattack/spawn/core/api/server/HttpServer.java @@ -2,6 +2,7 @@ package eu.mhsl.craftattack.spawn.core.api.server; import com.google.gson.Gson; import eu.mhsl.craftattack.spawn.core.Main; +import eu.mhsl.craftattack.spawn.core.api.server.hooks.HttpHook; import eu.mhsl.craftattack.spawn.core.appliance.Appliance; import org.bukkit.configuration.ConfigurationSection; import spark.Request; @@ -22,6 +23,11 @@ public class HttpServer { Spark.get("/ping", (request, response) -> System.currentTimeMillis()); + Spark.post("/hook/:hookId", (request, response) -> { + HttpHook.Hook hook = HttpHook.Hook.valueOf(request.params(":hookId").toUpperCase()); + return hook.getHook().runAction(request.headers(hook.getHeaderAction()), request, response); + }); + Main.instance().getAppliances().forEach(appliance -> appliance.httpApi(new ApiBuilder(appliance))); } @@ -64,7 +70,7 @@ public class HttpServer { }); } - public String buildRoute(String path) { + private String buildRoute(String path) { return String.format("/api/%s/%s", this.applianceName, path); } diff --git a/core/src/main/java/eu/mhsl/craftattack/spawn/core/api/server/hooks/HttpHook.java b/core/src/main/java/eu/mhsl/craftattack/spawn/core/api/server/hooks/HttpHook.java new file mode 100644 index 0000000..ffe357c --- /dev/null +++ b/core/src/main/java/eu/mhsl/craftattack/spawn/core/api/server/hooks/HttpHook.java @@ -0,0 +1,55 @@ +package eu.mhsl.craftattack.spawn.core.api.server.hooks; + +import eu.mhsl.craftattack.spawn.core.Main; +import eu.mhsl.craftattack.spawn.core.api.server.HttpServer; +import eu.mhsl.craftattack.spawn.core.api.server.hooks.impl.WebsiteHook; +import spark.Request; +import spark.Response; + +import java.util.HashMap; +import java.util.Map; + +public abstract class HttpHook { + public enum Hook { + WEBSITE("x-webhook-action", new WebsiteHook()); + + private final String headerAction; + private final HttpHook hook; + Hook(String headerAction, HttpHook handler) { + this.headerAction = headerAction; + this.hook = handler; + this.hook.registerHooks(); + } + + public HttpHook getHook() { + return this.hook; + } + + public String getHeaderAction() { + return this.headerAction; + } + } + + public abstract static class Action { + public abstract Object run(Request request, Response response); + } + + private final Map actions = new HashMap<>(); + + protected HttpHook() { + } + + protected abstract void registerHooks(); + + protected void addAction(String name, Action action) { + this.actions.put(name, action); + } + + public Object runAction(String action, Request request, Response response) { + if(!this.actions.containsKey(action)) { + Main.logger().warning(String.format("Webhook-Action '%s' not registered, skipping!", action)); + return HttpServer.nothing; + } + return this.actions.get(action).run(request, response); + } +} diff --git a/core/src/main/java/eu/mhsl/craftattack/spawn/core/api/server/hooks/JsonAction.java b/core/src/main/java/eu/mhsl/craftattack/spawn/core/api/server/hooks/JsonAction.java new file mode 100644 index 0000000..fa2613b --- /dev/null +++ b/core/src/main/java/eu/mhsl/craftattack/spawn/core/api/server/hooks/JsonAction.java @@ -0,0 +1,26 @@ +package eu.mhsl.craftattack.spawn.core.api.server.hooks; + +import com.google.gson.Gson; +import spark.Request; +import spark.Response; + +import java.util.function.Function; + +public class JsonAction extends HttpHook.Action { + private final Function handler; + private final Class requestClass; + + private final Gson gson = new Gson(); + + public JsonAction(Class requestClass, Function handler) { + this.requestClass = requestClass; + this.handler = handler; + } + + @Override + public Object run(Request request, Response response) { + TRequest req = this.gson.fromJson(request.body(), this.requestClass); + response.type("application/json"); + return this.gson.toJson(this.handler.apply(req)); + } +} diff --git a/core/src/main/java/eu/mhsl/craftattack/spawn/core/api/server/hooks/RawAction.java b/core/src/main/java/eu/mhsl/craftattack/spawn/core/api/server/hooks/RawAction.java new file mode 100644 index 0000000..1d1904d --- /dev/null +++ b/core/src/main/java/eu/mhsl/craftattack/spawn/core/api/server/hooks/RawAction.java @@ -0,0 +1,18 @@ +package eu.mhsl.craftattack.spawn.core.api.server.hooks; + +import spark.Request; +import spark.Response; + +import java.util.function.BiFunction; + +public class RawAction extends HttpHook.Action { + private final BiFunction handler; + public RawAction(BiFunction handler) { + this.handler = handler; + } + + @Override + public Object run(Request request, Response response) { + return this.handler.apply(request, response); + } +} diff --git a/core/src/main/java/eu/mhsl/craftattack/spawn/core/api/server/hooks/impl/WebsiteHook.java b/core/src/main/java/eu/mhsl/craftattack/spawn/core/api/server/hooks/impl/WebsiteHook.java new file mode 100644 index 0000000..73e7552 --- /dev/null +++ b/core/src/main/java/eu/mhsl/craftattack/spawn/core/api/server/hooks/impl/WebsiteHook.java @@ -0,0 +1,43 @@ +package eu.mhsl.craftattack.spawn.core.api.server.hooks.impl; + +import eu.mhsl.craftattack.spawn.core.Main; +import eu.mhsl.craftattack.spawn.core.api.server.HttpServer; +import eu.mhsl.craftattack.spawn.core.api.server.hooks.HttpHook; +import eu.mhsl.craftattack.spawn.core.api.server.hooks.JsonAction; +import eu.mhsl.craftattack.spawn.core.event.ReportCreatedEvent; +import eu.mhsl.craftattack.spawn.core.event.SpawnEvent; +import eu.mhsl.craftattack.spawn.core.event.StrikeCreatedEvent; +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.UUID; + +public class WebsiteHook extends HttpHook { + @Override + protected void registerHooks() { + record CreatedSignup( + String firstname, + String lastname, + String birthday, + @Nullable String telephone, + String username, + String edition, + @Nullable UUID uuid + ) {} + this.addAction("signup", new JsonAction<>(CreatedSignup.class, createdSignup -> { + Main.logger().info(String.format("New Website-signup from Hook: %s %s (%s)", createdSignup.firstname, createdSignup.lastname, createdSignup.username)); + return HttpServer.nothing; + })); + + record CreatedReport(String reporter, String reported, String reason) {} + this.addAction("report", new JsonAction<>(CreatedReport.class, createdReport -> { + SpawnEvent.call(new ReportCreatedEvent(new ReportCreatedEvent.CreatedReport(createdReport.reporter, createdReport.reported, createdReport.reason))); + return HttpServer.nothing; + })); + + record CreatedStrike(UUID uuid) {} + this.addAction("strike", new JsonAction<>(CreatedStrike.class, createdStrike -> { + SpawnEvent.call(new StrikeCreatedEvent(new StrikeCreatedEvent.CreatedStrike(createdStrike.uuid))); + return HttpServer.nothing; + })); + } +} diff --git a/core/src/main/java/eu/mhsl/craftattack/spawn/core/event/ReportCreatedEvent.java b/core/src/main/java/eu/mhsl/craftattack/spawn/core/event/ReportCreatedEvent.java new file mode 100644 index 0000000..d0d40cb --- /dev/null +++ b/core/src/main/java/eu/mhsl/craftattack/spawn/core/event/ReportCreatedEvent.java @@ -0,0 +1,15 @@ +package eu.mhsl.craftattack.spawn.core.event; + +public class ReportCreatedEvent extends SpawnEvent { + public record CreatedReport(String reporter, String reported, String reason) {} + + private final CreatedReport report; + public ReportCreatedEvent(CreatedReport report) { + super(true); + this.report = report; + } + + public CreatedReport getReport() { + return this.report; + } +} diff --git a/core/src/main/java/eu/mhsl/craftattack/spawn/core/event/SpawnEvent.java b/core/src/main/java/eu/mhsl/craftattack/spawn/core/event/SpawnEvent.java new file mode 100644 index 0000000..3d55016 --- /dev/null +++ b/core/src/main/java/eu/mhsl/craftattack/spawn/core/event/SpawnEvent.java @@ -0,0 +1,22 @@ +package eu.mhsl.craftattack.spawn.core.event; + +import org.bukkit.Bukkit; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +public abstract class SpawnEvent extends Event { + private static final HandlerList HANDLERS = new HandlerList(); + @Override + public @NotNull HandlerList getHandlers() { + return HANDLERS; + } + + public static void call(SpawnEvent event) { + Bukkit.getPluginManager().callEvent(event); + } + + public SpawnEvent(boolean isAsync) { + super(isAsync); + } +} diff --git a/core/src/main/java/eu/mhsl/craftattack/spawn/core/event/StrikeCreatedEvent.java b/core/src/main/java/eu/mhsl/craftattack/spawn/core/event/StrikeCreatedEvent.java new file mode 100644 index 0000000..de8dbe3 --- /dev/null +++ b/core/src/main/java/eu/mhsl/craftattack/spawn/core/event/StrikeCreatedEvent.java @@ -0,0 +1,17 @@ +package eu.mhsl.craftattack.spawn.core.event; + +import java.util.UUID; + +public class StrikeCreatedEvent extends SpawnEvent { + public record CreatedStrike(UUID playerToStrike) {} + + private final CreatedStrike strike; + public StrikeCreatedEvent(CreatedStrike strike) { + super(true); + this.strike = strike; + } + + public CreatedStrike getStrike() { + return this.strike; + } +}