From c2204790524462b7ee7ed0988a98cb81d1a92a52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20M=C3=BCller?= Date: Mon, 27 Oct 2025 14:25:44 +0100 Subject: [PATCH] WIP: refactored report and feedback systems; updated repository models, APIs, and component utilities --- .../CraftAttackReportRepository.java | 5 +- .../api/repositories/ReportRepository.java | 17 +++-- .../metaGameplay/report/Report.java | 72 +++++++++++-------- .../spawn/core/util/text/ComponentUtil.java | 8 ++- .../api/repositories/FeedbackRepository.java | 18 +++-- .../api/repositories/WhitelistRepository.java | 13 ++-- .../metaGameplay/feedback/Feedback.java | 29 +++----- .../feedback/FeedbackCommand.java | 1 + .../feedback/RequestFeedbackCommand.java | 1 + .../appliances/tooling/strike/Strike.java | 21 ++++++ .../tooling/whitelist/Whitelist.java | 8 +-- 11 files changed, 110 insertions(+), 83 deletions(-) create mode 100644 craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/tooling/strike/Strike.java diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/api/repositories/CraftAttackReportRepository.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/api/repositories/CraftAttackReportRepository.java index 46723f5..232f4c7 100644 --- a/common/src/main/java/eu/mhsl/craftattack/spawn/common/api/repositories/CraftAttackReportRepository.java +++ b/common/src/main/java/eu/mhsl/craftattack/spawn/common/api/repositories/CraftAttackReportRepository.java @@ -11,15 +11,14 @@ public class CraftAttackReportRepository extends ReportRepository { public ReqResp queryReports(UUID player) { return this.get( - "report", - (parameters) -> parameters.addParameter("uuid", player.toString()), + "users/%s/reports".formatted(player.toString()), PlayerReports.class ); } public ReqResp createReport(ReportCreationInfo data) { return this.post( - "report", + "reports", data, ReportUrl.class ); diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/api/repositories/ReportRepository.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/api/repositories/ReportRepository.java index 4ed05cb..0e3fec5 100644 --- a/common/src/main/java/eu/mhsl/craftattack/spawn/common/api/repositories/ReportRepository.java +++ b/common/src/main/java/eu/mhsl/craftattack/spawn/common/api/repositories/ReportRepository.java @@ -23,19 +23,18 @@ public abstract class ReportRepository extends HttpRepository { public record PlayerReports( List from_self, - Object to_self + List to_self ) { public record Report( - @Nullable Reporter reported, - @NotNull String subject, - boolean draft, - @NotNull String status, + @Nullable UUID reported, + @NotNull String reason, + @Nullable Long created, + @Nullable Status status, @NotNull String url ) { - public record Reporter( - @NotNull String username, - @NotNull String uuid - ) { + public enum Status { + open, + closed, } } } diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/report/Report.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/report/Report.java index d5b1551..543abe0 100644 --- a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/report/Report.java +++ b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/report/Report.java @@ -6,6 +6,7 @@ import eu.mhsl.craftattack.spawn.core.api.client.ReqResp; import eu.mhsl.craftattack.spawn.common.api.repositories.CraftAttackReportRepository; import eu.mhsl.craftattack.spawn.core.appliance.Appliance; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand; +import eu.mhsl.craftattack.spawn.core.util.text.ComponentUtil; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.ComponentBuilder; import net.kyori.adventure.text.TextComponent; @@ -19,7 +20,9 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; +import java.util.Objects; import java.util.Optional; +import java.util.function.Function; public class Report extends Appliance { public static Component helpText() { @@ -78,7 +81,7 @@ public class Report extends Appliance { .appendNewline() .append( Component - .text(createdReport.data().url(), NamedTextColor.GRAY) // URL mit Weltkugel-Emoji + .text(createdReport.data().url(), NamedTextColor.GRAY) .clickEvent(ClickEvent.clickEvent(ClickEvent.Action.OPEN_URL, createdReport.data().url())) ) .appendNewline() @@ -128,43 +131,50 @@ public class Report extends Appliance { return; } - List reports = userReports - .data() - .from_self() - .stream() - .filter(report -> !report.draft()) - .toList() - .reversed(); + Function, List> filterClosed = reports -> reports.stream() + .filter(report -> Objects.equals(report.status(), ReportRepository.PlayerReports.Report.Status.closed)) + .toList(); - if(reports.isEmpty()) { - issuer.sendMessage( - Component.text() - .append(Component.text("Du hast noch niemanden reportet.", NamedTextColor.RED)) - .appendNewline() - .append(Component.text("Um jemanden zu melden, nutze /report", NamedTextColor.GRAY)) - ); - return; - } + List reportsToOthers = filterClosed.apply(userReports.data().from_self()).reversed(); + List reportsToSelf = filterClosed.apply(userReports.data().to_self()).reversed(); ComponentBuilder component = Component.text() - .append(Component.newline()) - .append(Component.text("Von dir erstellte Reports: ", NamedTextColor.GOLD)) - .appendNewline(); + .append(Component.text( + !reportsToSelf.isEmpty() + ? "Du wurdest insgesamt %d mal von anderen Spielern gemeldet.".formatted(reportsToSelf.size()) + : "Du wurdest von keinem anderen Spieler gemeldet.", + NamedTextColor.GOLD) + ); - reports.forEach(report -> { - component - .append(Component.text(" - ", NamedTextColor.WHITE)) - .append( - report.reported() != null - ? Component.text(report.reported().username(), NamedTextColor.WHITE) - : Component.text("Unbekannt", NamedTextColor.YELLOW) - ) - .append(Component.text(String.format(": %s", report.subject()), NamedTextColor.GRAY)) + component.appendNewline(); + + component.append(Component.text("Von dir erstellte Reports: ", NamedTextColor.GOLD)); + reportsToOthers.forEach(report -> { + Component button = Component.text("[\uD83D\uDC41/\uD83D\uDD8A]") .clickEvent(ClickEvent.openUrl(report.url())) - .hoverEvent(HoverEvent.showText(Component.text("Klicke, um den Report einzusehen.", NamedTextColor.GOLD))); - component.appendNewline(); + .hoverEvent(HoverEvent.showText(ComponentUtil.clickLink(report.url()))); + + Component reportedDisplayName = report.reported() != null + ? Component.text(Optional.ofNullable(Bukkit.getOfflinePlayer(report.reported()).getName()).orElse(report.reported().toString()), NamedTextColor.WHITE) + : Component.text("Unbekannt", NamedTextColor.YELLOW); + + component + .appendNewline() + .append(Component.text(" \u27A1 ", NamedTextColor.GRAY)) + .append(button) + .append(Component.text(" du gegen ", NamedTextColor.GRAY)) + .append(reportedDisplayName) + .append(Component.text(String.format(": %s", report.reason()), NamedTextColor.GRAY)); }); + if(reportsToOthers.isEmpty()) { + component + .appendNewline() + .append(Component.text("Du hast noch niemanden reportet.", NamedTextColor.RED)) + .appendNewline() + .append(Component.text("Um jemanden zu melden, nutze /report", NamedTextColor.GRAY)); + } + issuer.sendMessage(component.build()); } diff --git a/core/src/main/java/eu/mhsl/craftattack/spawn/core/util/text/ComponentUtil.java b/core/src/main/java/eu/mhsl/craftattack/spawn/core/util/text/ComponentUtil.java index 6be7844..d802b36 100644 --- a/core/src/main/java/eu/mhsl/craftattack/spawn/core/util/text/ComponentUtil.java +++ b/core/src/main/java/eu/mhsl/craftattack/spawn/core/util/text/ComponentUtil.java @@ -5,6 +5,7 @@ import eu.mhsl.craftattack.spawn.core.util.statistics.ServerMonitor; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.ComponentBuilder; import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.event.ClickEvent; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.TextColor; import org.bukkit.Bukkit; @@ -19,7 +20,12 @@ import java.util.stream.Stream; public class ComponentUtil { public static TextComponent pleaseWait() { - return Component.text("Bitte warte einen Augenblick...", NamedTextColor.GRAY); + return Component.text("\uD83D\uDCBE Daten werden geladen... Warte einen Augenblick!", NamedTextColor.GRAY); + } + + public static TextComponent clickLink(String url) { + return Component.text("Klicke, um zu öffnen: \uD83D\uDD17[%s]".formatted(url)) + .clickEvent(ClickEvent.openUrl(url)); } public static Component clearedSpace() { diff --git a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/api/repositories/FeedbackRepository.java b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/api/repositories/FeedbackRepository.java index d8243a9..3e551a7 100644 --- a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/api/repositories/FeedbackRepository.java +++ b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/api/repositories/FeedbackRepository.java @@ -1,13 +1,10 @@ package eu.mhsl.craftattack.spawn.craftattack.api.repositories; -import com.google.common.reflect.TypeToken; import eu.mhsl.craftattack.spawn.core.api.client.HttpRepository; import eu.mhsl.craftattack.spawn.core.api.client.ReqResp; import eu.mhsl.craftattack.spawn.common.api.CraftAttackApi; -import java.lang.reflect.Type; import java.util.List; -import java.util.Map; import java.util.UUID; public class FeedbackRepository extends HttpRepository { @@ -15,14 +12,15 @@ public class FeedbackRepository extends HttpRepository { super(CraftAttackApi.getBaseUri(), new RequestModifier(null, CraftAttackApi::withAuthorizationHeader)); } - public record Request(String event, List users) { + public record Request(String event, String title, List users) { } - public ReqResp> createFeedbackUrls(Request data) { - final Type responseType = new TypeToken>() { - }.getType(); - ReqResp rawData = this.post("feedback", data, Object.class); - // TODO: use convertToTypeToken from ReqResp - return new ReqResp<>(rawData.status(), this.gson.fromJson(this.gson.toJson(rawData.data()), responseType)); + public record Response(List feedback) { + public record Feedback(UUID uuid, String url) { + } + } + + public ReqResp createFeedbackUrls(Request data) { + return this.post("feedback", data, Response.class); } } diff --git a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/api/repositories/WhitelistRepository.java b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/api/repositories/WhitelistRepository.java index 8e3eda4..81aea86 100644 --- a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/api/repositories/WhitelistRepository.java +++ b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/api/repositories/WhitelistRepository.java @@ -4,6 +4,7 @@ import eu.mhsl.craftattack.spawn.core.api.client.HttpRepository; import eu.mhsl.craftattack.spawn.core.api.client.ReqResp; import eu.mhsl.craftattack.spawn.common.api.CraftAttackApi; +import java.util.List; import java.util.UUID; public class WhitelistRepository extends HttpRepository { @@ -16,17 +17,15 @@ public class WhitelistRepository extends HttpRepository { String username, String firstname, String lastname, - Long banned_until, - Long outlawed_until + List strikes ) { + public record Strike(int at, int weight) { + } } - private record UserQuery(UUID uuid) {} - public ReqResp getUserData(UUID userId) { - return this.post( - "player", - new UserQuery(userId), + return this.get( + "users/%s".formatted(userId.toString()), UserData.class ); } diff --git a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/metaGameplay/feedback/Feedback.java b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/metaGameplay/feedback/Feedback.java index b588c96..37eb437 100644 --- a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/metaGameplay/feedback/Feedback.java +++ b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/metaGameplay/feedback/Feedback.java @@ -1,7 +1,7 @@ package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.feedback; -import eu.mhsl.craftattack.spawn.core.Main; import eu.mhsl.craftattack.spawn.core.api.client.ReqResp; +import eu.mhsl.craftattack.spawn.core.util.text.ComponentUtil; import eu.mhsl.craftattack.spawn.craftattack.api.repositories.FeedbackRepository; import eu.mhsl.craftattack.spawn.core.appliance.Appliance; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand; @@ -9,7 +9,6 @@ import eu.mhsl.craftattack.spawn.core.api.HttpStatus; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.ComponentBuilder; import net.kyori.adventure.text.TextComponent; -import net.kyori.adventure.text.event.ClickEvent; import net.kyori.adventure.text.event.HoverEvent; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.entity.Entity; @@ -18,32 +17,27 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; -import java.util.Map; -import java.util.UUID; public class Feedback extends Appliance { public Feedback() { super("feedback"); } - public void requestFeedback(String eventName, List receivers, @Nullable String question) { - ReqResp> response = this.queryRepository(FeedbackRepository.class).createFeedbackUrls( - new FeedbackRepository.Request(eventName, receivers.stream().map(Entity::getUniqueId).toList()) + public void requestFeedback(String eventName, String title, List receivers, @Nullable String question) { + ReqResp response = this.queryRepository(FeedbackRepository.class).createFeedbackUrls( + new FeedbackRepository.Request(eventName, title, receivers.stream().map(Entity::getUniqueId).toList()) ); - System.out.println(response.toString()); - System.out.println(response.status()); - - if(response.status() != HttpStatus.CREATED) throw new RuntimeException(); + if(response.status() != HttpStatus.OK) throw new RuntimeException(); Component border = Component.text("-".repeat(40), NamedTextColor.GRAY); receivers.forEach(player -> { - String feedbackUrl = response.data().get(player.getUniqueId()); - if(feedbackUrl == null) { - Main.logger().warning(String.format("FeedbackUrl not found for player '%s' from backend!", player.getUniqueId())); - return; - } + String feedbackUrl = response.data().feedback().stream() + .filter(feedback -> feedback.uuid().equals(player.getUniqueId())) + .findFirst() + .orElseThrow() + .url(); ComponentBuilder message = Component.text() .append(border) @@ -58,8 +52,7 @@ public class Feedback extends Appliance { message .append(Component.text("Klicke hier und gib uns Feedback, damit wir dein Spielerlebnis verbessern können!", NamedTextColor.DARK_GREEN) - .clickEvent(ClickEvent.openUrl(feedbackUrl))) - .hoverEvent(HoverEvent.showText(Component.text("Klicke, um Feedback zu geben."))) + .hoverEvent(HoverEvent.showText(ComponentUtil.clickLink(feedbackUrl)))) .appendNewline() .append(border); diff --git a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/metaGameplay/feedback/FeedbackCommand.java b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/metaGameplay/feedback/FeedbackCommand.java index a95f0b0..59abc5e 100644 --- a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/metaGameplay/feedback/FeedbackCommand.java +++ b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/metaGameplay/feedback/FeedbackCommand.java @@ -22,6 +22,7 @@ class FeedbackCommand extends ApplianceCommand.PlayerChecked { Main.instance(), () -> this.getAppliance().requestFeedback( "self-issued-ingame", + "Dein Feedback an uns", List.of(this.getPlayer()), null ) diff --git a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/metaGameplay/feedback/RequestFeedbackCommand.java b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/metaGameplay/feedback/RequestFeedbackCommand.java index bc5df1c..f7be48c 100644 --- a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/metaGameplay/feedback/RequestFeedbackCommand.java +++ b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/metaGameplay/feedback/RequestFeedbackCommand.java @@ -20,6 +20,7 @@ class RequestFeedbackCommand extends ApplianceCommand { Main.instance(), () -> this.getAppliance().requestFeedback( "admin-issued-ingame", + "Hilf uns dein Spielerlebnis zu verbessern!", new ArrayList<>(Bukkit.getOnlinePlayers()), String.join(" ", args) ) ); diff --git a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/tooling/strike/Strike.java b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/tooling/strike/Strike.java new file mode 100644 index 0000000..59e5010 --- /dev/null +++ b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/tooling/strike/Strike.java @@ -0,0 +1,21 @@ +package eu.mhsl.craftattack.spawn.craftattack.appliances.tooling.strike; + +import eu.mhsl.craftattack.spawn.core.appliance.Appliance; + +import java.time.Duration; +import java.util.Map; + +public class Strike extends Appliance { + public Strike() { + super("strike"); + } + + private final Map strikePunishmentMap = Map.of( + 1, Duration.ofHours(1), + 2, Duration.ofHours(24), + 3, Duration.ofDays(3), + 4, Duration.ofDays(7) + ); + + +} diff --git a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/tooling/whitelist/Whitelist.java b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/tooling/whitelist/Whitelist.java index be76411..0e55b0a 100644 --- a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/tooling/whitelist/Whitelist.java +++ b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/tooling/whitelist/Whitelist.java @@ -47,7 +47,7 @@ public class Whitelist extends Appliance { player.getUniqueId() ); } - this.queryAppliance(Outlawed.class).updateForcedStatus(player, this.timestampRelevant(user.outlawed_until())); + this.queryAppliance(Outlawed.class).updateForcedStatus(player, this.timestampRelevant(0L)); // TODO String purePlayerName = Floodgate.isBedrock(player) ? Floodgate.getBedrockPlayer(player).getUsername() @@ -67,14 +67,14 @@ public class Whitelist extends Appliance { Main.instance().getLogger().info(String.format("Running integrityCheck for %s", name)); boolean overrideCheck = this.localConfig().getBoolean("overrideIntegrityCheck", false); WhitelistRepository.UserData user = overrideCheck - ? new WhitelistRepository.UserData(uuid, name, "", "", 0L, 0L) + ? new WhitelistRepository.UserData(uuid, name, "", "", List.of()) : this.fetchUserData(uuid); this.userData.put(uuid, user); Main.logger().info(String.format("got userdata %s", user.toString())); - if(this.timestampRelevant(user.banned_until())) { - Instant bannedDate = new Date(user.banned_until() * 1000L) + if(this.timestampRelevant(0L)) { //TODO + Instant bannedDate = new Date(0 * 1000L) // TODO .toInstant() .plus(1, ChronoUnit.HOURS);