BlazeBans API/Working with data

Reading punishments

Everything on `api.punishments()` that reads. All of it returns a `CompletableFuture`, so read [Threading](/docs/blazebans-api/threading) first if you have not.

Boolean checks

The cheapest question: is this player punished right now?

java
api.punishments().isBanned(name, uuid).thenAccept(banned -> { /* ... */ });
api.punishments().isMuted(name, uuid).thenAccept(muted -> { /* ... */ });

Both have an overload that also matches IP punishments:

java
api.punishments().isBanned(name, uuid, ipAddress);
api.punishments().isMuted(name, uuid, ipAddress);

Pass the address raw, exactly as player.getAddress() gives it to you. BlazeBans applies its own hashing and privacy rules on the way in, so pre-hashing it will just fail to match.

These answer only about punishments that apply on this server. A record scoped to another server on the network returns false here, which is the same answer enforcement would give.

Fetching the record

When you need the reason, the expiry, or who issued it:

java
api.punishments().activeBan(name, uuid)
    .thenAccept(activeBan -> activeBan.ifPresent(punishment ->
        getLogger().info("Banned for " + punishment.reason())
    ));

api.punishments().activeMute(name, uuid);

activeBan covers BAN and IP_BAN; activeMute covers MUTE and IP_MUTE. Each returns the newest applicable one. Both have the same IP-aware overload as the boolean checks.

For exactly one type and nothing else:

java
api.punishments().activePunishment(PunishmentType.WARN, name, uuid);
api.punishments().activePunishment(PunishmentType.VOICE_MUTE, name, uuid);

This is how you check for an active warning, which has no dedicated accessor, and how you check voice mutes when the voice chat addon is installed.

By ID

java
api.punishments().punishmentById("#ABC123")
    .thenAccept(found -> found.ifPresent(punishment ->
        getLogger().info(punishment.targetName() + ": " + punishment.reason())
    ));

The # is optional. ABC123 and #ABC123 both resolve, so you can pass an ID straight from user input without stripping it.

This finds the record whatever its state: active, expired, or revoked. Check status() if that matters.

Full history

java
api.punishments().history(target)
    .thenAccept(records -> records.forEach(punishment ->
        getLogger().info(punishment.id() + " " + punishment.type() + " " + punishment.status())
    ));

target accepts a player name, a UUID string, or a raw IP address. Results are newest first and include every record: active, expired, and revoked, from every server scope.

That last part is worth remembering. Unlike the boolean checks, history is not filtered to this server, because a moderation decision usually should account for what happened elsewhere.

Server-wide lists

java
api.punishments().activeBans();
api.punishments().activeMutes();
api.punishments().activeWarns();

Every active record of that kind applying to this server. These are the same sets /banlist, /mutelist, and /warnlist show.

They are unbounded. On a server with a long history this can be a large list and a heavy query, so do not call them on a timer. Once at startup, or in response to a command, is the right shape.

What a record contains

Punishment is an immutable record.

FieldTypeNotes
id()StringIncludes the # prefix
type()PunishmentTypeSee below
targetName()StringThe punished player
targetUuid()UUIDMay be null for an old or name-only record
staffName()StringWho issued it
staffUuid()UUIDnull for console and automated actions
reason()StringThe stored reason
createdAt()InstantWhen it was issued
expiresAt()Instantnull means permanent
revokedAt()Instantnull if never revoked
revokedBy()StringWho revoked it
revokeReason()StringWhy
proofUrl()StringBlank when there is none, never null
serverScope()String"global" or "server"
serverId()StringBlank for a global record
silent()booleanWhether announcements were suppressed

PunishmentType is BAN, MUTE, WARN, KICK, IP_BAN, IP_MUTE, or VOICE_MUTE.

Derived state

Three convenience methods, all computed when you call them:

java
punishment.active();     // true only while neither revoked nor expired
punishment.permanent();  // true when expiresAt is null
punishment.status();     // ACTIVE, REVOKED, or EXPIRED

status() compares expiresAt against the current time on every call, so a temporary punishment you fetched a minute ago will start reporting EXPIRED the moment it lapses. You never need to re-fetch a record to notice it expired.

The precedence is revoked, then expired, then active. A punishment that was revoked before its expiry reports REVOKED.

Remaining time

There is no helper for this, because Instant already has one:

java
if (punishment.permanent()) {
    return "Permanent";
}
Duration remaining = Duration.between(Instant.now(), punishment.expiresAt());
if (remaining.isNegative()) {
    return "Expired";
}
return remaining.toDays() + "d " + remaining.toHoursPart() + "h";

Format it however suits your plugin. BlazeBans' own compact format is two units at most.

Scope

serverScope() and serverId() tell you where a record applies.

java
boolean global = "global".equalsIgnoreCase(punishment.serverScope());
String appliesTo = global ? "the whole network" : punishment.serverId();

The PunishmentScope enum (GLOBAL, SERVER) exists in the API for typed use, but records carry these as strings, so compare case-insensitively rather than assuming a case.

You rarely need this. The boolean checks and activeBan already filter to what applies here; scope matters when you are displaying history and want to say where each record came from.

Filtering history yourself

There is no query-by-type-and-date method. Pull the history and filter in Java:

java
api.punishments().history(playerName).thenAccept(records -> {
    List<Punishment> recentBans = records.stream()
        .filter(punishment -> punishment.type() == PunishmentType.BAN)
        .filter(punishment -> punishment.createdAt().isAfter(Instant.now().minus(Duration.ofDays(30))))
        .toList();

    long activeWarnings = records.stream()
        .filter(punishment -> punishment.type() == PunishmentType.WARN)
        .filter(Punishment::active)
        .count();
});

One player's history is small enough that this costs nothing worth avoiding.