Improve the way commutations, appeals, and clears work.

Specifically, commutations now create a new infraction,
and all three set a repeal reason/issuer/type.

This makes the system more flexible and makes for a better audit log.
master
James T. Martin 2020-11-25 11:18:09 -08:00
parent ea101d5c07
commit 3570b7ddab
Signed by: james
GPG Key ID: 4B7F3DA9351E577C
24 changed files with 448 additions and 244 deletions

View File

@ -31,9 +31,9 @@ This plugin provides tools which both help moderators track what rule infraction
and help server administrators hold moderators accountable through an audit log.
This plugin provides these basic commands:
* `/ban` and `/unban`
* `/ban`
* `/kick`
* `/mute` and `/unmute`
* `/mute`
* `/warn`
All bans, kicks, mutes, and warnings are logged,
@ -43,21 +43,24 @@ and to help server administrators hold moderators accountable through an audit l
It is also possible to set the maximum length of bans or mutes that a moderator can issue
in the configuration file.
You may view the currently active bans and mutes using `/bans` and `/mutes` respectively,
or see the history of infractions a player has committed using `/infractions`.
You may view the currently active sentences against any player,
the infractions that a moderator has issued,
or see the history of infractions a player has committed using `/infractions query`.
This command does not include infractions that have been cleared from a player's record,
or old versions of infractions from before they were commuted.
You can remove an infraction from a player's account using commands like `/unwarn`.
Removing an infraction from a player's account will not remove the infraction from the audit log,
and in fact will just add that the infraction was removed to the audit log.
You can commute, extend, or update a sentence using `/infractions commute`,
remove and clear an infraction from a player's record using `/infractions clear`
or remove a sentence without clearing the infraction from the player's record using `/infractions appeal`.
These commands do not remove the infraction from the audit log, and in fact create a new entry in the audit log.
You may view the audit log using `/auditlog`.
The audit log includes infractions that have been cleared, appealed, or commuted,
the history of those actions.
For more information on how to use these commands please see the in game `/help` or the `plugin.yml`
For more information on how to configure this plugin, please see the default `config.yml`.
### Infraction histories & audit logs
A set of moderator tools which logs all moderator actions,
### Manuals
There are two built-in manuals: `/rules` and `/faq`.
These manuals provide both brief overviews and more detail which players may view if they wish.
@ -66,12 +69,3 @@ It is also possible for moderators to (forcibly) show players portions of the ru
as a brief reminder of the rules or to quickly answer a question they were asking.
Please see the in-game `/help` menu or `plugin.yml` for more information on how to use these commands.
## Current Features
* Tracks how many zombies or husks a player kills.
* Supports a ranking system for players based on the U.S. Marines' ranks.
## Planned Features
* Make zombies horde more intelligently.
* Special world effects.
* A lot of stuff, generally, but it's been a few months so I don't remember it all. I'll add it here when work begins again.

View File

@ -4,17 +4,13 @@ import org.bukkit.plugin.java.JavaPlugin;
import me.jamestmartin.wasteland.manual.ManualState;
import me.jamestmartin.wasteland.modtools.commands.CommandBan;
import me.jamestmartin.wasteland.modtools.commands.CommandBans;
import me.jamestmartin.wasteland.modtools.commands.CommandInfractions;
import me.jamestmartin.wasteland.modtools.commands.CommandKick;
import me.jamestmartin.wasteland.modtools.commands.CommandMute;
import me.jamestmartin.wasteland.modtools.commands.CommandMutes;
import me.jamestmartin.wasteland.modtools.commands.CommandUnban;
import me.jamestmartin.wasteland.modtools.commands.CommandUnmute;
import me.jamestmartin.wasteland.modtools.commands.CommandWarn;
import me.jamestmartin.wasteland.modtools.config.ModToolsConfig;
import me.jamestmartin.wasteland.modtools.infraction.InfractionStore;
import me.jamestmartin.wasteland.modtools.log.CommandModLog;
import me.jamestmartin.wasteland.modtools.log.CommandAuditLog;
import me.jamestmartin.wasteland.offlineplayer.CommandLastSeen;
import me.jamestmartin.wasteland.offlineplayer.CommandUUID;
@ -22,15 +18,11 @@ class ModToolsState {
private final ManualState manualState;
private final CommandBan commandBan;
private final CommandBans commandBans;
private final CommandInfractions commandInfractions;
private final CommandKick commandKick;
private final CommandLastSeen commandLastSeen;
private final CommandModLog commandModLog;
private final CommandAuditLog commandAuditLog;
private final CommandMute commandMute;
private final CommandMutes commandMutes;
private final CommandUnban commandUnban;
private final CommandUnmute commandUnmute;
private final CommandUUID commandUUID;
private final CommandWarn commandWarn;
@ -41,15 +33,11 @@ class ModToolsState {
InfractionStore store = null;
this.commandBan = new CommandBan(store, config.getDurations().getBansConfig());
this.commandBans = new CommandBans();
this.commandInfractions = new CommandInfractions();
this.commandKick = new CommandKick(store);
this.commandLastSeen = new CommandLastSeen();
this.commandModLog = new CommandModLog();
this.commandAuditLog = new CommandAuditLog();
this.commandMute = new CommandMute(store, config.getDurations().getMutesConfig());
this.commandMutes = new CommandMutes();
this.commandUnban = new CommandUnban();
this.commandUnmute = new CommandUnmute();
this.commandUUID = new CommandUUID();
this.commandWarn = new CommandWarn(store);
}
@ -58,15 +46,11 @@ class ModToolsState {
manualState.register(plugin);
plugin.getCommand("ban").setExecutor(commandBan);
plugin.getCommand("bans").setExecutor(commandBans);
plugin.getCommand("infractions").setExecutor(commandInfractions);
plugin.getCommand("kick").setExecutor(commandKick);
plugin.getCommand("lastseen").setExecutor(commandLastSeen);
plugin.getCommand("modlog").setExecutor(commandModLog);
plugin.getCommand("auditlog").setExecutor(commandAuditLog);
plugin.getCommand("mute").setExecutor(commandMute);
plugin.getCommand("mutes").setExecutor(commandMutes);
plugin.getCommand("unban").setExecutor(commandUnban);
plugin.getCommand("unmute").setExecutor(commandUnmute);
plugin.getCommand("uuid").setExecutor(commandUUID);
plugin.getCommand("warn").setExecutor(commandWarn);
}
@ -79,7 +63,7 @@ class ModToolsState {
plugin.getCommand("infractions").setExecutor(null);
plugin.getCommand("kick").setExecutor(null);
plugin.getCommand("lastseen").setExecutor(null);
plugin.getCommand("modlog").setExecutor(null);
plugin.getCommand("auditlog").setExecutor(null);
plugin.getCommand("mute").setExecutor(null);
plugin.getCommand("mutes").setExecutor(null);
plugin.getCommand("unban").setExecutor(null);

View File

@ -4,9 +4,9 @@ import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import me.jamestmartin.wasteland.modtools.config.DurationsConfig;
import me.jamestmartin.wasteland.modtools.infraction.Infraction;
import me.jamestmartin.wasteland.modtools.infraction.InfractionStore;
import me.jamestmartin.wasteland.modtools.infraction.InfractionType;
import me.jamestmartin.wasteland.modtools.infraction.NewInfraction;
import me.jamestmartin.wasteland.modtools.infraction.SentenceType;
public class CommandBan extends CommandIssueInfraction {
public CommandBan(InfractionStore store, DurationsConfig durations) {
@ -14,12 +14,12 @@ public class CommandBan extends CommandIssueInfraction {
}
@Override
protected InfractionType getType() {
return InfractionType.BAN;
protected SentenceType getType() {
return SentenceType.BAN;
}
@Override
protected void applyInfraction(CommandSender sender, Infraction infraction) {
protected void applyInfraction(CommandSender sender, NewInfraction infraction) {
Player recipient = infraction.getRecipient().getPlayer();
if (recipient != null) {
recipient.kickPlayer(infraction.getMessage());

View File

@ -1,15 +0,0 @@
package me.jamestmartin.wasteland.modtools.commands;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
public class CommandBans implements CommandExecutor {
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
// TODO Auto-generated method stub
return false;
}
}

View File

@ -10,9 +10,9 @@ import org.bukkit.entity.Player;
import me.jamestmartin.wasteland.modtools.config.DurationsConfig;
import me.jamestmartin.wasteland.modtools.infraction.Duration;
import me.jamestmartin.wasteland.modtools.infraction.Infraction;
import me.jamestmartin.wasteland.modtools.infraction.InfractionStore;
import me.jamestmartin.wasteland.modtools.infraction.InfractionType;
import me.jamestmartin.wasteland.modtools.infraction.NewInfraction;
import me.jamestmartin.wasteland.modtools.infraction.SentenceType;
abstract class CommandIssueInfraction implements CommandExecutor {
protected final InfractionStore store;
@ -41,13 +41,13 @@ abstract class CommandIssueInfraction implements CommandExecutor {
}
Player issuer = sender instanceof Player ? (Player) sender : null;
Optional<Infraction> maybe = parseArgs(getType(), issuer, args);
Optional<NewInfraction> maybe = parseArgs(getType(), issuer, args);
if (maybe.isEmpty()) {
// TOOD: better error messages
sender.sendMessage("Invalid syntax.");
return false;
}
Infraction infraction = maybe.get();
NewInfraction infraction = maybe.get();
if (infraction.getDuration().compareTo(maxDuration) == 1) {
sender.sendMessage("You are not allowed you issue an infraction of duration " + infraction.getDuration() + ".");
@ -57,15 +57,15 @@ abstract class CommandIssueInfraction implements CommandExecutor {
}
applyInfraction(sender, infraction);
store.addInfraction(infraction);
store.issueInfraction(infraction);
return false;
}
protected abstract InfractionType getType();
protected abstract SentenceType getType();
protected abstract void applyInfraction(CommandSender sender, Infraction infraction);
protected abstract void applyInfraction(CommandSender sender, NewInfraction infraction);
private static Optional<Infraction> parseArgs(InfractionType type, OfflinePlayer issuer, String[] args) {
private static Optional<NewInfraction> parseArgs(SentenceType type, OfflinePlayer issuer, String[] args) {
// TODO
return null;
}

View File

@ -3,9 +3,9 @@ package me.jamestmartin.wasteland.modtools.commands;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import me.jamestmartin.wasteland.modtools.infraction.Infraction;
import me.jamestmartin.wasteland.modtools.infraction.NewInfraction;
import me.jamestmartin.wasteland.modtools.infraction.InfractionStore;
import me.jamestmartin.wasteland.modtools.infraction.InfractionType;
import me.jamestmartin.wasteland.modtools.infraction.SentenceType;
public class CommandKick extends CommandIssueInfraction {
public CommandKick(InfractionStore store) {
@ -13,12 +13,12 @@ public class CommandKick extends CommandIssueInfraction {
}
@Override
protected InfractionType getType() {
return InfractionType.KICK;
protected SentenceType getType() {
return SentenceType.KICK;
}
@Override
protected void applyInfraction(CommandSender sender, Infraction infraction) {
protected void applyInfraction(CommandSender sender, NewInfraction infraction) {
Player player = infraction.getRecipient().getPlayer();
if (player == null) {
sender.sendMessage("That player is not online!");

View File

@ -4,9 +4,9 @@ import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import me.jamestmartin.wasteland.modtools.config.DurationsConfig;
import me.jamestmartin.wasteland.modtools.infraction.Infraction;
import me.jamestmartin.wasteland.modtools.infraction.NewInfraction;
import me.jamestmartin.wasteland.modtools.infraction.InfractionStore;
import me.jamestmartin.wasteland.modtools.infraction.InfractionType;
import me.jamestmartin.wasteland.modtools.infraction.SentenceType;
public class CommandMute extends CommandIssueInfraction {
public CommandMute(InfractionStore store, DurationsConfig durations) {
@ -14,12 +14,12 @@ public class CommandMute extends CommandIssueInfraction {
}
@Override
protected InfractionType getType() {
return InfractionType.MUTE;
protected SentenceType getType() {
return SentenceType.MUTE;
}
@Override
protected void applyInfraction(CommandSender sender, Infraction infraction) {
protected void applyInfraction(CommandSender sender, NewInfraction infraction) {
Player player = infraction.getRecipient().getPlayer();
if (player != null) {
player.sendMessage(infraction.getMessage());

View File

@ -1,15 +0,0 @@
package me.jamestmartin.wasteland.modtools.commands;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
public class CommandMutes implements CommandExecutor {
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
// TODO Auto-generated method stub
return false;
}
}

View File

@ -1,46 +0,0 @@
package me.jamestmartin.wasteland.modtools.commands;
import java.util.Optional;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import me.jamestmartin.wasteland.modtools.infraction.Infraction;
import me.jamestmartin.wasteland.modtools.infraction.InfractionStore;
import me.jamestmartin.wasteland.modtools.infraction.InfractionType;
public abstract class CommandRemoveInfraction implements CommandExecutor {
protected final InfractionStore store;
public CommandRemoveInfraction(InfractionStore store) {
this.store = store;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
Player issuer = sender instanceof Player ? (Player) sender : null;
Optional<Infraction> maybe = parseArgs(getType(), issuer, args);
if (maybe.isEmpty()) {
// TOOD: better error messages
sender.sendMessage("Invalid syntax.");
return false;
}
Infraction infraction = maybe.get();
applyInfraction(sender, infraction);
store.addInfraction(infraction);
return false;
}
protected abstract InfractionType getType();
protected abstract void applyInfraction(CommandSender sender, Infraction infraction);
private static Optional<Infraction> parseArgs(InfractionType type, OfflinePlayer issuer, String[] args) {
// TODO
return null;
}
}

View File

@ -1,15 +0,0 @@
package me.jamestmartin.wasteland.modtools.commands;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
public class CommandUnban implements CommandExecutor {
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
// TODO Auto-generated method stub
return false;
}
}

View File

@ -1,15 +0,0 @@
package me.jamestmartin.wasteland.modtools.commands;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
public class CommandUnmute implements CommandExecutor {
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
// TODO Auto-generated method stub
return false;
}
}

View File

@ -3,9 +3,9 @@ package me.jamestmartin.wasteland.modtools.commands;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import me.jamestmartin.wasteland.modtools.infraction.Infraction;
import me.jamestmartin.wasteland.modtools.infraction.NewInfraction;
import me.jamestmartin.wasteland.modtools.infraction.InfractionStore;
import me.jamestmartin.wasteland.modtools.infraction.InfractionType;
import me.jamestmartin.wasteland.modtools.infraction.SentenceType;
public class CommandWarn extends CommandIssueInfraction {
public CommandWarn(InfractionStore store) {
@ -13,12 +13,12 @@ public class CommandWarn extends CommandIssueInfraction {
}
@Override
protected InfractionType getType() {
return InfractionType.WARN;
protected SentenceType getType() {
return SentenceType.WARN;
}
@Override
protected void applyInfraction(CommandSender sender, Infraction infraction) {
protected void applyInfraction(CommandSender sender, NewInfraction infraction) {
Player player = infraction.getRecipient().getPlayer();
if (player != null) {
player.sendMessage(infraction.getMessage());

View File

@ -1,6 +1,7 @@
package me.jamestmartin.wasteland.modtools.infraction;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Optional;
@ -170,4 +171,15 @@ public class Duration implements Comparable<Duration> {
return Long.compare(getSeconds().get(), other.getSeconds().get());
}
public static Duration fromDates(Date begin, Date end) {
return new Duration(end.getTime() - begin.getTime());
}
public static Duration fromDates(Date begin, Optional<Date> end) {
if (end.isEmpty()) {
return INFINITY;
}
return fromDates(begin, end.get());
}
}

View File

@ -1,56 +1,139 @@
package me.jamestmartin.wasteland.modtools.infraction;
import java.util.Date;
import java.util.Optional;
import org.bukkit.OfflinePlayer;
public class Infraction {
private final InfractionType type;
private final OfflinePlayer issuer;
/** The unique identifier of this infraction. */
private final int id;
/** The type of sentence the player received: ban, kick, mute, or warning. */
private final SentenceType type;
/** The moderator who issued the sentence. (Or empty, if it was issued by the console.) */
private final Optional<OfflinePlayer> issuer;
/** The player who committed the infraction. */
private final OfflinePlayer recipient;
/** The time that the sentence was issued. */
private final Date issued;
private final Duration duration;
/** The time that the sentence takes effect. */
private final Date start;
/** The time that the sentence naturally expires, if ever. */
private final Optional<Date> expiry;
/** The information about this infraction's repeal, if it was repealed. */
private final Optional<Repeal> repeal;
/** If this sentence was commuted, the identifier for the original infraction. */
private final Optional<Integer> original;
/** The section identifier for the rule that was violated. */
private final String rule;
/** The reason that an infraction was issued. */
private final String reason;
public Infraction(InfractionType type, OfflinePlayer issuer, OfflinePlayer recipient, Date issued, Duration duration, String rule, String reason) {
public Infraction(
int id,
SentenceType type,
Optional<OfflinePlayer> issuer,
OfflinePlayer recipient,
Date issued,
Date start,
Optional<Date> expiry,
Optional<Repeal> repeal,
Optional<Integer> original,
String rule,
String reason
) {
this.id = id;
this.type = type;
this.issuer = issuer;
this.recipient = recipient;
this.issued = issued;
this.duration = duration;
this.start = start;
this.expiry = expiry;
this.repeal = repeal;
this.original = original;
this.rule = rule;
this.reason = reason;
}
public InfractionType getType() {
/** @return The unique identifier for this infraction. */
public int getId() {
return id;
}
/** @return The {@link SentenceType type of sentence} the player received: ban, kick, mute, or warning. */
public SentenceType getType() {
return type;
}
public OfflinePlayer getIssuer() {
/** @return The moderator who issued the sentence. (Or empty, if it was issued by the console.) */
public Optional<OfflinePlayer> getIssuer() {
return issuer;
}
/** @return The player who committed the infraction. */
public OfflinePlayer getRecipient() {
return recipient;
}
/** @return The time the sentence was issued. */
public Date getIssued() {
return issued;
}
public Duration getDuration() {
return duration;
/**
* @return
* <p>The time that the sentence first takes effect.
* <p>
* Generally, this will be the same time that it was issued;
* however, if a sentence is commuted, the new infraction starts at the same time,
* but will actually have a time issued *after* the sentence started!
*/
public Date getStart() {
return start;
}
/** @return The time the infraction naturally expires if not repealed, if ever. */
public Optional<Date> getExpiry() {
return expiry;
}
/**
* @return The duration that the sentence would last if not repealed.
* @see #getActualDuration()
*/
public Duration getOriginalDuration() {
return Duration.fromDates(getStart(), getExpiry());
}
/** @return The information about this infraction's repeal, if it was repealed. */
public Optional<Repeal> getRepeal() {
return repeal;
}
/** @return If this sentence was commuted, the identifier for the original infraction. */
public Optional<Integer> getOriginal() {
return original;
}
/**
* @return The duration the sentence would last if it has been repealed, or otherwise the original duration.
* @see #getOriginalDuration()
*/
public Duration getActualDuration() {
return Duration.fromDates(getStart(), getRepeal().map(Repeal::getIssued).or(() -> getExpiry()));
}
/** @return The rule that was violated. */
public String getRule() {
return rule;
}
/** @return The reason that the infraction was issued. */
public String getReason() {
return reason;
}
/** @return The message that the player will get sent with information about their sentence. */
public String getMessage() {
// TODO
return null;

View File

@ -5,21 +5,42 @@ import java.util.Set;
import java.util.UUID;
public interface InfractionProvider {
Set<Infraction> getActiveInfractions(Optional<UUID> player, Optional<InfractionType> type);
/**
* @param player The player who committed the infractions, or any player.
* @param type The type of sentence given, or any sentence type.
* @return The un-expired, un-repealed sentences matching the provide criteria.
*/
Set<Infraction> getActiveInfractions(Optional<UUID> player, Optional<SentenceType> type);
/** @return All un-expired, un-repealed sentences. */
default Set<Infraction> getActiveInfractions() {
return getActiveInfractions(Optional.empty(), Optional.empty());
}
/**
* @param player The player who committed the infractions.
* @return The player's un-expired, un-repealed sentences.
*/
default Set<Infraction> getActiveInfractions(UUID player) {
return getActiveInfractions(Optional.of(player), Optional.empty());
}
default Set<Infraction> getActiveInfractions(UUID player, InfractionType type) {
/**
* @param player The player who committed the infractions.
* @param type The type of sentence given.
* @return The player's un-expired, un-repealed sentences of the given type.
*/
default Set<Infraction> getActiveInfractions(UUID player, SentenceType type) {
return getActiveInfractions(Optional.of(player), Optional.of(type));
}
default Set<Infraction> getActiveInfractions(InfractionType type) {
/**
* @param type The type of sentence given.
* @return All un-expired, un-repealed sentences of the given type.
*/
default Set<Infraction> getActiveInfractions(SentenceType type) {
return getActiveInfractions(Optional.empty(), Optional.of(type));
}
// TODO: Provide *in*active infractions which were never cleared.
}

View File

@ -1,8 +1,36 @@
package me.jamestmartin.wasteland.modtools.infraction;
import java.util.Optional;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
public interface InfractionStore extends InfractionProvider {
void addInfraction(Infraction infraction);
void removeInfractions(OfflinePlayer player, InfractionType type);
/**
* @param infraction The new infraction to add to the database.
* @see NewInfraction#NewInfraction(SentenceType, java.util.Optional, OfflinePlayer, Duration, String, String) Create a new infraction.
* @see NewInfraction#NewInfraction(java.util.Optional, Duration, Infraction) Commute an old infraction.
* @see NewInfraction#NewInfraction(SentenceType, java.util.Optional, OfflinePlayer, java.util.Optional, Duration, java.util.Optional, String, String) Update an old infraction.
*/
void issueInfraction(NewInfraction infraction);
/**
* <p>Clear a previously-issued infraction.
* <p>This will clear the infraction from the player's record, but not from the audit log.
* @param id The identifier of the infraction to clear.
* @param issuer The moderator who cleared the infraction.
* @see RepealType#CLEARED
* @see #appealInfraction(OfflinePlayer, SentenceType)
*/
void clearInfraction(int id, Optional<Player> issuer);
/**
* <p>Accept an appeal for a previously-issued infraction.
* <p>This will remove the sentence from the player, but not remove it from their record.
* @param id The identifier of the infraction which was appealed.
* @param issuer The moderator who accepted the appeal.
* @see RepealType#APPEALED
* @see #clearInfraction(int, Optional)
*/
void appealInfraction(int id, Optional<Player> issuer);
}

View File

@ -0,0 +1,145 @@
package me.jamestmartin.wasteland.modtools.infraction;
import java.util.Date;
import java.util.Optional;
import org.bukkit.OfflinePlayer;
public class NewInfraction {
/** The type of sentence the player received: ban, kick, mute, or warning. */
private final SentenceType type;
/** The moderator who issued the sentence. (Or empty, if it was issued by the console.) */
private final Optional<OfflinePlayer> issuer;
/** The player who committed the infraction. */
private final OfflinePlayer recipient;
/** The time that the sentence takes effect, or empty if it starts immediately. */
private final Optional<Date> start;
/** The duration of the sentence. */
private final Duration duration;
/** If this sentence was commuted, the identifier for the original infraction. */
private final Optional<Integer> original;
/** The section identifier for the rule that was violated. */
private final String rule;
/** The reason that an infraction was issued. */
private final String reason;
public NewInfraction(
SentenceType type,
Optional<OfflinePlayer> issuer,
OfflinePlayer recipient,
Optional<Date> start,
Duration duration,
Optional<Integer> original,
String rule,
String reason
) {
this.type = type;
this.issuer = issuer;
this.recipient = recipient;
this.start = start;
this.duration = duration;
this.original = original;
this.rule = rule;
this.reason = reason;
}
public NewInfraction(
SentenceType type,
Optional<OfflinePlayer> issuer,
OfflinePlayer recipient,
Duration duration,
String rule,
String reason
) {
this.type = type;
this.issuer = issuer;
this.recipient = recipient;
this.start = Optional.empty();
this.duration = duration;
this.original = Optional.empty();
this.rule = rule;
this.reason = reason;
}
/** Update an old infraction with a new expiry. */
public NewInfraction(
Optional<OfflinePlayer> issuer,
Duration duration,
Infraction original
) {
this.type = original.getType();
this.issuer = issuer;
this.recipient = original.getRecipient();
this.start = Optional.of(original.getStart());
this.duration = duration;
this.original = Optional.of(original.getId());
this.rule = original.getRule();
this.reason = original.getReason();
}
/** Update an old infraction with a new expiry and reason. */
public NewInfraction(
Optional<OfflinePlayer> issuer,
Duration duration,
Infraction original,
String rule,
String reason
) {
this.type = original.getType();
this.issuer = issuer;
this.recipient = original.getRecipient();
this.start = Optional.of(original.getStart());
this.duration = duration;
this.original = Optional.of(original.getId());
this.rule = rule;
this.reason = reason;
}
/** @return The {@link SentenceType type of sentence} the player received: ban, kick, mute, or warning. */
public SentenceType getType() {
return type;
}
/** @return The moderator who issued the sentence. (Or empty, if it was issued by the console.) */
public Optional<OfflinePlayer> getIssuer() {
return issuer;
}
/** @return The player who committed the infraction. */
public OfflinePlayer getRecipient() {
return recipient;
}
/**
* @return The time that the sentence first takes effect, or empty if it starts immediately.
*/
public Optional<Date> getStart() {
return start;
}
/** @return The time the infraction naturally expires if not repealed, if ever. */
public Duration getDuration() {
return duration;
}
/** @return If this sentence was commuted, the identifier for the original infraction. */
public Optional<Integer> getOriginal() {
return original;
}
/** @return The rule that was violated. */
public String getRule() {
return rule;
}
/** @return The reason that the infraction was issued. */
public String getReason() {
return reason;
}
/** @return The message that the player will get sent with information about their sentence. */
public String getMessage() {
// TODO
return null;
}
}

View File

@ -0,0 +1,33 @@
package me.jamestmartin.wasteland.modtools.infraction;
import java.util.Date;
import java.util.Optional;
import org.bukkit.OfflinePlayer;
public class Repeal {
private final RepealType type;
private final Optional<OfflinePlayer> issuer;
private final Date issued;
public Repeal(RepealType type, Optional<OfflinePlayer> issuer, Date issued) {
this.type = type;
this.issuer = issuer;
this.issued = issued;
}
/** The way in which the infraction was repealed. */
public RepealType getType() {
return type;
}
/** The player who issued the repeal. */
public Optional<OfflinePlayer> getIssuer() {
return issuer;
}
/** The time the repeal was issued. */
public Date getIssued() {
return issued;
}
}

View File

@ -0,0 +1,11 @@
package me.jamestmartin.wasteland.modtools.infraction;
/** The way in which which an infraction was removed. */
public enum RepealType {
/** The player was cleared of wrongdoing, and this infraction will be removed from their record. */
CLEARED,
/** The player's sentence was reduced, and replaced with another infraction. */
COMMUTED,
/** The player's sentence was removed based on appeal, but the infraction will remain on their record. */
APPEALED;
}

View File

@ -1,6 +1,6 @@
package me.jamestmartin.wasteland.modtools.infraction;
public enum InfractionType {
public enum SentenceType {
BAN(true),
KICK(false),
MUTE(true),
@ -8,7 +8,7 @@ public enum InfractionType {
private final boolean hasduration;
private InfractionType(boolean hasDuration) {
private SentenceType(boolean hasDuration) {
this.hasduration = hasDuration;
}

View File

@ -6,37 +6,60 @@ import java.util.UUID;
import me.jamestmartin.wasteland.modtools.infraction.Duration;
import me.jamestmartin.wasteland.modtools.infraction.Infraction;
import me.jamestmartin.wasteland.modtools.infraction.InfractionType;
import me.jamestmartin.wasteland.modtools.infraction.SentenceType;
public interface ModLogProvider {
public interface AuditLogProvider {
/**
* @param duration The maximum time ago that an infraction was issued or repealed.
* @param issuer The moderator who issued an infraction.
* @param recipient The player who committed the infraction.
* @param type The type of sentence issued.
* @return All infractions that were issued or repealed fitting the provided criteria.
*/
Set<Infraction> getInfractions(
Duration duration,
Optional<UUID> issuer,
Optional<UUID> recipient,
Optional<InfractionType> type
Optional<SentenceType> type
);
/** All infractions that have ever been issued. */
default Set<Infraction> getInfractions() {
return getInfractions(Duration.INFINITY);
}
default Set<Infraction> getInfractions(InfractionType type) {
/**
* @param type The type of sentence issued.
* @return All sentences of the given type that have ever been issued.
*/
default Set<Infraction> getInfractions(SentenceType type) {
return getInfractions(Duration.INFINITY, type);
}
/**
* @param duration The maximum time ago that an infraction was issued or repealed.
* @return All sentences that have ever been issued or repealed within the duration provided.
*/
default Set<Infraction> getInfractions(Duration duration) {
return getInfractions(duration, Optional.empty(), Optional.empty(), Optional.empty());
}
default Set<Infraction> getInfractions(Duration duration, InfractionType type) {
/**
* @param duration The maximum time ago that an infraction was issued or repealed.
* @param type The type of sentence issued.
* @return All sentences of the given type that have ever been issued or repealed within the duration provided.
*/
default Set<Infraction> getInfractions(Duration duration, SentenceType type) {
return getInfractions(duration, Optional.empty(), Optional.empty(), Optional.of(type));
}
// TODO: Javadoc for all these variants.
default Set<Infraction> getInfractionsIssuedTo(UUID player) {
return getInfractionsIssuedTo(Duration.INFINITY, player);
}
default Set<Infraction> getInfractionsIssuedTo(UUID player, InfractionType type) {
default Set<Infraction> getInfractionsIssuedTo(UUID player, SentenceType type) {
return getInfractionsIssuedTo(Duration.INFINITY, player, type);
}
@ -44,7 +67,7 @@ public interface ModLogProvider {
return getInfractions(duration, Optional.empty(), Optional.of(player), Optional.empty());
}
default Set<Infraction> getInfractionsIssuedTo(Duration duration, UUID player, InfractionType type) {
default Set<Infraction> getInfractionsIssuedTo(Duration duration, UUID player, SentenceType type) {
return getInfractions(duration, Optional.empty(), Optional.of(player), Optional.of(type));
}
@ -52,7 +75,7 @@ public interface ModLogProvider {
return getInfractionsIssuedBy(Duration.INFINITY, player);
}
default Set<Infraction> getInfractionsIssuedBy(UUID player, InfractionType type) {
default Set<Infraction> getInfractionsIssuedBy(UUID player, SentenceType type) {
return getInfractionsIssuedBy(Duration.INFINITY, player, type);
}
@ -60,8 +83,7 @@ public interface ModLogProvider {
return getInfractions(duration, Optional.of(player), Optional.empty(), Optional.empty());
}
default Set<Infraction> getInfractionsIssuedBy(Duration duration, UUID player, InfractionType type) {
default Set<Infraction> getInfractionsIssuedBy(Duration duration, UUID player, SentenceType type) {
return getInfractions(duration, Optional.of(player), Optional.empty(), Optional.of(type));
}
}

View File

@ -1,7 +1,8 @@
package me.jamestmartin.wasteland.modtools.log;
import me.jamestmartin.wasteland.modtools.infraction.Infraction;
import me.jamestmartin.wasteland.modtools.infraction.InfractionStore;
public interface ModLogStore extends ModLogProvider {
public interface AuditLogStore extends AuditLogProvider, InfractionStore {
void addInfraction(Infraction infraction);
}

View File

@ -4,7 +4,7 @@ import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
public class CommandModLog implements CommandExecutor {
public class CommandAuditLog implements CommandExecutor {
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {

View File

@ -5,14 +5,19 @@ version: 0.1.0
api-version: 1.16
commands:
modlog:
auditlog:
description: See the list of recent actions taken by moderators.
usage: "Usage: /<command> [<duration>]"
permission: wasteland.modtools.log
permission-message: You do not have permission to view the moderator log.
permission-message: You do not have permission to view the aduit log.
infractions:
description: See the list of infractions against the rules a player has committed.
usage: "Usage: /<infractions> <player> [<duration>]"
usage: |
Usage:
/<command> query <player> [<duration>]
/<command> commute <id> <duration> [<rule>] [<reason> ...]
/<command> clear <id>
/<command> appeal <id>
permission: wasteland.modtools.infractions
permission-message: You do not have permission to view players' infractions against the rules.
@ -43,16 +48,6 @@ commands:
usage: "Usage: /<command> <player> <duration> [<rule>] [<reason> ...]"
permission: wasteland.modtools.ban.issue
permission-message: You do not have permission to ban players.
unban:
description: Unban a player, allowing them to join again.
usage: "Usage: /<command> <player> [<reason> ...]"
permission: wasteland.modtools.ban.pardon
permission-message: You do not have permission to unban players.
bans:
description: See the list of all players currently banned from the server.
usage: "Usage: /<command> [<player>]"
permission: wasteland.modtools.ban.list
permission-message: You do not have permission to list banned players.
kick:
description: Kick a player from the server.
@ -65,16 +60,6 @@ commands:
usage: "Usage: /<command> <player> <duration> [<rule>] [<reason> ...]"
permission: wasteland.modtools.mute.issue
permission-message: You do not have permission to mute players.
unmute:
description: Unmute a player, allowing them to talk again.
usage: "Usage: /<command> <player> [<reason> ...]"
permission: wasteland.modtools.mute.pardon
permission-message: You do not have permission to unmute players.
mutes:
description: See the list of all players currently muted on the server.
usage: "Usage: /<command> [<player>]"
permission: wasteland.modtools.mutes.list
permission-message: You do not have permission to list muted players.
warn:
description: Give a player an official (logged) warning.
@ -97,12 +82,12 @@ permissions:
default: op
children:
wasteland.modtools.infractions: true
wasteland.modtools.ban.list: true
wasteland.modtools.mute.list: true
wasteland.modtools.warn.list: true
wasteland.modtools.infractions:
description: Allows you to see the list of infractions against the rules a player has committed.
default: op
wasteland.modtools.infractions.clear:
description: Allows you to clear infractions from players' permanent records.
default: op
wasteland.modtools.uuid:
description: Allows you to see the UUIDs of players with a given name on this server.
@ -151,20 +136,16 @@ permissions:
wasteland.manual.faq: true
wasteland.modtools.ban:
description: Allows you to ban or unban a player, or list active bans.
description: Allows you to ban or unban a player.
default: op
children:
wasteland.modtools.ban.issue: true
wasteland.modtools.ban.list: true
wasteland.modtools.ban.pardon: true
wasteland.modtools.ban.issue:
description: Allows you to issue bans.
default: op
wasteland.modtools.ban.list:
description: Allows you to list all active bans.
default: op
wasteland.modtools.ban.pardon:
description: Allows you to remove bans.
wasteland.modtools.ban.commute:
description: Allows you to commute bans.
default: op
wasteland.modtools.kick:
@ -172,20 +153,15 @@ permissions:
default: op
wasteland.modtools.mute:
description: Allows you to mute or unmute a player, or list active mutes.
default: op
description: Allows you to mute or unmute a player.
children:
wasteland.modtools.mute.issue: true
wasteland.modtools.mute.list: true
wasteland.modtools.mute.pardon: true
wasteland.modtools.mute.issue:
description: Allows you to issue mutes.
default: op
wasteland.modtools.mute.list:
description: Allows you to list active mutes.
default: op
wasteland.modtools.mute.pardon:
description: Allows you to remove mutes.
wasteland.modtools.mute.commute:
description: Allows you to commute mutes.
default: op
wasteland.modtools.warn: