diff --git a/src/main/java/dev/pgm/community/moderation/feature/ModerationFeature.java b/src/main/java/dev/pgm/community/moderation/feature/ModerationFeature.java index ed41d670..ed8d7510 100644 --- a/src/main/java/dev/pgm/community/moderation/feature/ModerationFeature.java +++ b/src/main/java/dev/pgm/community/moderation/feature/ModerationFeature.java @@ -60,6 +60,15 @@ Punishment punish( */ CompletableFuture pardon(String target, Optional issuer); + /** + * Deactivate an active punishment + * + * @param target A username or UUID string + * @param punishmentType PunishmentType to deactivate + * @return True if any punishments were deactivated, false if none + */ + CompletableFuture deactivate(String target, PunishmentType punishmentType); + /** * Get whether the target is currently banned * diff --git a/src/main/java/dev/pgm/community/moderation/feature/ModerationFeatureBase.java b/src/main/java/dev/pgm/community/moderation/feature/ModerationFeatureBase.java index 28978ea7..aacb5425 100644 --- a/src/main/java/dev/pgm/community/moderation/feature/ModerationFeatureBase.java +++ b/src/main/java/dev/pgm/community/moderation/feature/ModerationFeatureBase.java @@ -209,6 +209,10 @@ public void onPunishmentEvent(PlayerPunishmentEvent event) { .build()); return; } + } else if (PunishmentType.WARN.equals(punishment.getType()) + || PunishmentType.KICK.equals(punishment.getType())) { + // If its a warn or kick set it to active so the player sees it when they next login + punishment.setActive(true); } save(punishment); // Save punishment to database diff --git a/src/main/java/dev/pgm/community/moderation/feature/types/SQLModerationFeature.java b/src/main/java/dev/pgm/community/moderation/feature/types/SQLModerationFeature.java index ee75c895..f8417836 100644 --- a/src/main/java/dev/pgm/community/moderation/feature/types/SQLModerationFeature.java +++ b/src/main/java/dev/pgm/community/moderation/feature/types/SQLModerationFeature.java @@ -14,12 +14,14 @@ import java.time.Duration; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.logging.Logger; +import java.util.stream.Collectors; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.configuration.Configuration; @@ -112,6 +114,24 @@ public CompletableFuture pardon(String target, Optional issuer) { }); } + @Override + public CompletableFuture deactivate(String target, PunishmentType punishmentType) { + CompletableFuture> playerId = + NameUtils.isMinecraftName(target) + ? getUsers().getStoredId(target) + : CompletableFuture.completedFuture(Optional.of(UUID.fromString(target))); + return playerId.thenApplyAsync( + uuid -> { + if (uuid.isPresent()) { + if (service.deactivate(uuid.get(), punishmentType).join()) { + sendRefresh(uuid.get()); + return true; + } + } + return false; + }); + } + @Override public CompletableFuture isBanned(String target) { if (NameUtils.isMinecraftName(target)) { @@ -160,6 +180,22 @@ public void onPreLogin(AsyncPlayerPreLoginEvent event) { addMute(event.getUniqueId(), mute.get()); } + Set deferredPunishments = getDeferredPunishments(punishments); + for (Punishment punishment : deferredPunishments) { + Bukkit.getScheduler() + .runTaskLater( + Community.get(), + () -> { + if (PunishmentType.WARN.equals(punishment.getType()) + || PunishmentType.KICK.equals(punishment.getType())) { + if (punishment.punish(true)) { + deactivate(event.getUniqueId().toString(), punishment.getType()); + } + } + }, + 20 * 5); + } + logger.info( punishments.size() + " Punishments have been fetched for " @@ -239,6 +275,16 @@ private Optional hasActiveBan(List punishments) { .findAny(); } + private Set getDeferredPunishments(List punishments) { + return punishments.stream() + .filter( + p -> + p.isActive() + && (PunishmentType.WARN.equals(p.getType()) + || PunishmentType.KICK.equals(p.getType()))) + .collect(Collectors.toSet()); + } + @Override public CompletableFuture> isMuted(UUID target) { return service.isMuted(target); diff --git a/src/main/java/dev/pgm/community/moderation/punishments/Punishment.java b/src/main/java/dev/pgm/community/moderation/punishments/Punishment.java index 63ba955b..65e5c5ea 100644 --- a/src/main/java/dev/pgm/community/moderation/punishments/Punishment.java +++ b/src/main/java/dev/pgm/community/moderation/punishments/Punishment.java @@ -37,6 +37,7 @@ import tc.oc.pgm.util.UsernameFormatUtils; import tc.oc.pgm.util.named.NameStyle; import tc.oc.pgm.util.player.PlayerComponent; +import tc.oc.pgm.util.text.TemporalComponent; import tc.oc.pgm.util.text.TextTranslations; public class Punishment implements Comparable { @@ -115,6 +116,10 @@ public boolean isActive() { return active; } + public void setActive(boolean active) { + this.active = active; + } + public Instant getLastUpdated() { return lastUpdated; } @@ -178,7 +183,19 @@ public boolean kick(boolean silent) { public void sendWarning(Audience target, String reason) { Component titleWord = translatable("misc.warning", NamedTextColor.DARK_RED); Component title = text().append(WARN_SYMBOL).append(titleWord).append(WARN_SYMBOL).build(); - Component subtitle = text(reason, NamedTextColor.GOLD); + Component subtitle; + if (Duration.between(timeIssued, Instant.now()).getSeconds() >= 60) { + subtitle = + text() + .append( + TemporalComponent.relativePastApproximate(timeIssued) + .color(NamedTextColor.YELLOW) + .append(text(": ", NamedTextColor.YELLOW))) + .append(text(reason, NamedTextColor.GOLD)) + .build(); + } else { + subtitle = text(reason, NamedTextColor.GOLD); + } target.showTitle( title( @@ -227,7 +244,18 @@ public String formatPunishmentScreen( List lines = Lists.newArrayList(); lines.add(empty()); - lines.add(getType().getScreenComponent(text(getReason(), NamedTextColor.RED))); + lines.add( + getType() + .getScreenComponent( + Duration.between(timeIssued, Instant.now()).getSeconds() >= 60 + ? text() + .append( + TemporalComponent.relativePastApproximate(timeIssued) + .color(NamedTextColor.YELLOW) + .append(text(": ", NamedTextColor.YELLOW))) + .append(text(reason, NamedTextColor.RED)) + .build() + : text(reason, NamedTextColor.RED))); // If punishment expires, display when if (this instanceof ExpirablePunishment) { diff --git a/src/main/java/dev/pgm/community/moderation/services/ModerationQuery.java b/src/main/java/dev/pgm/community/moderation/services/ModerationQuery.java index 422fd5a1..3291be9f 100644 --- a/src/main/java/dev/pgm/community/moderation/services/ModerationQuery.java +++ b/src/main/java/dev/pgm/community/moderation/services/ModerationQuery.java @@ -20,6 +20,8 @@ public interface ModerationQuery { "UPDATE " + TABLE_NAME + " SET active = ?, last_updated = ?, updated_by = ? WHERE active = ? AND punished = ? "; + static final String DEACTIVATE_QUERY = + "UPDATE " + TABLE_NAME + " SET active = ? WHERE active = ? AND punished = ? "; static final String SELECT_RECENT_QUERY = "SELECT * from " + TABLE_NAME + " WHERE time > ? LIMIT ?"; diff --git a/src/main/java/dev/pgm/community/moderation/services/SQLModerationService.java b/src/main/java/dev/pgm/community/moderation/services/SQLModerationService.java index 6d2edec8..5f4e66b0 100644 --- a/src/main/java/dev/pgm/community/moderation/services/SQLModerationService.java +++ b/src/main/java/dev/pgm/community/moderation/services/SQLModerationService.java @@ -167,6 +167,17 @@ public CompletableFuture pardon(UUID id, Optional issuer) { .thenApplyAsync(result -> result != 0); } + public CompletableFuture deactivate(UUID id, PunishmentType punishmentType) { + punishmentCache.invalidate(id); + return DB.executeUpdateAsync( + DEACTIVATE_QUERY + SINGLE_PARDON_TYPE, + false, + true, + id.toString(), + punishmentType.toString()) + .thenApplyAsync(result -> result != 0); + } + public CompletableFuture unmute(UUID id, Optional issuer) { punishmentCache.invalidate(id);