Skip to content

Actions, warnings, and punishments

When a message is censored or blocked, the player can receive a chat message (configurable per surface in messages.yml). Warnings are rate-limited per player so a burst of violations does not spam them.

Alerts are permission-gated (daisyfilter.alerts) and include a masked preview plus an Inspect button linked to the exact incident. Staff with daisyfilter.view.raw can hover for exact encrypted evidence; other staff never receive it. Severe incidents are never hidden by grouping cooldowns. Each staff member can mute alerts with /daisyfilter alerts off.

webhook.yml can mirror incidents to a Discord webhook for off-server visibility. It is disabled until you set a URL, and only ever sends privacy-safe metadata — player, surface, action, severity, categories, the masked preview, and the record id — never raw content. Posts are asynchronous and best-effort, rate-limited (max-per-minute) to respect Discord, and gated by min-severity and enforced-only. All Discord mention parsing is disabled, so a crafted name can never ping the server.

DaisyFilter does not implement its own ban/mute system — it hands sanctions to the punishment plugin you already run. Each profile in actions.yml dispatches one of two ways:

  • Typed kind (recommended, default): set kind to MUTE, BAN, KICK, or WARN and DaisyFilter routes the action through PunishBridge, which auto-detects the installed provider (LiteBans or the vanilla ban/kick fallback) and applies it with the correct semantics. No command syntax to maintain. The shipped warn, mute, kick, tempban, and ban profiles use this.
  • Raw commands (custom): leave kind blank and list console commands instead, for server-specific actions PunishBridge doesn’t model. These run as allowlisted, safe-templated console commands.

Both paths support durations, confirmation screens, per-profile permissions, and an audit row. Command placeholders are {player}, {uuid}, {staff}, {duration}, {reason}, {record_id}, {category}, {severity}; raw evidence is deliberately unavailable. Namespaced command roots must be listed exactly in allowed-command-roots. A rejected request, missing permission, invalid duration, or failed dispatch is audited.

PunishBridge is shaded into the plugin; only the Apache-2.0 LiteBans provider is bundled, so DaisyFilter ships LiteBans + vanilla support out of the box.

Command-based presets (for the custom commands path)

Section titled “Command-based presets (for the custom commands path)”

If you prefer raw commands (or run a provider PunishBridge doesn’t cover), clear kind and copy the block for your plugin into actions.yml.

LiteBans (recommended):

punishments:
allowed-command-roots: [warn, mute, tempmute, kick, ban, tempban]
profiles:
warn: { display-name: "Warn", permission: daisyfilter.punish.warn, confirmation: false, commands: ["warn {player} {reason}"] }
mute: { display-name: "Mute", permission: daisyfilter.punish.mute, durations: ["10m","1h","1d","7d"], commands: ["mute {player} {duration} {reason}"] }
kick: { display-name: "Kick", permission: daisyfilter.punish.kick, commands: ["kick {player} {reason}"] }
tempban: { display-name: "Temp Ban", enabled: false, permission: daisyfilter.punish.ban, durations: ["1d","7d","30d"], commands: ["tempban {player} {duration} {reason}"] }
ban: { display-name: "Ban", enabled: false, permission: daisyfilter.punish.ban, commands: ["ban {player} {reason}"] }

EssentialsX (no /warn; mute is permanent unless a duration is given):

punishments:
allowed-command-roots: [mute, kick, ban, tempban]
profiles:
mute: { display-name: "Mute", permission: daisyfilter.punish.mute, durations: ["10m","1h","1d"], commands: ["mute {player} {duration} {reason}"] }
kick: { display-name: "Kick", permission: daisyfilter.punish.kick, commands: ["kick {player} {reason}"] }
tempban: { display-name: "Temp Ban", enabled: false, permission: daisyfilter.punish.ban, durations: ["1d","7d","30d"], commands: ["tempban {player} {duration} {reason}"] }
ban: { display-name: "Ban", enabled: false, permission: daisyfilter.punish.ban, commands: ["ban {player} {reason}"] }

AdvancedBan:

punishments:
allowed-command-roots: [warn, mute, tempmute, kick, ban, tempban]
profiles:
warn: { display-name: "Warn", permission: daisyfilter.punish.warn, confirmation: false, commands: ["warn {player} {reason}"] }
mute: { display-name: "Temp Mute", permission: daisyfilter.punish.mute, durations: ["10m","1h","1d"], commands: ["tempmute {player} {duration} {reason}"] }
kick: { display-name: "Kick", permission: daisyfilter.punish.kick, commands: ["kick {player} {reason}"] }
tempban: { display-name: "Temp Ban", enabled: false, permission: daisyfilter.punish.ban, durations: ["1d","7d","30d"], commands: ["tempban {player} {duration} {reason}"] }
ban: { display-name: "Ban", enabled: false, permission: daisyfilter.punish.ban, commands: ["ban {player} {reason}"] }

LibertyBans (%DURATION% style uses the same flat syntax; namespaced fallback shown):

punishments:
allowed-command-roots: [warn, mute, kick, ban, libertybans]
profiles:
mute: { display-name: "Mute", permission: daisyfilter.punish.mute, durations: ["10m","1h","1d"], commands: ["mute {player} {duration} {reason}"] }
kick: { display-name: "Kick", permission: daisyfilter.punish.kick, commands: ["kick {player} {reason}"] }
ban: { display-name: "Ban", enabled: false, permission: daisyfilter.punish.ban, durations: ["1d","7d","perm"], commands: ["ban {player} {duration} {reason}"] }

CMI (namespaced root cmi):

punishments:
allowed-command-roots: [cmi]
profiles:
mute: { display-name: "Mute", permission: daisyfilter.punish.mute, durations: ["10m","1h","1d"], commands: ["cmi mute {player} {duration} {reason}"] }
kick: { display-name: "Kick", permission: daisyfilter.punish.kick, commands: ["cmi kick {player} {reason}"] }
ban: { display-name: "Ban", enabled: false, permission: daisyfilter.punish.ban, commands: ["cmi ban {player} {reason}"] }

Vanilla (no mute command — use the built-in fallback mute for muting):

punishments:
allowed-command-roots: [kick, ban]
profiles:
kick: { display-name: "Kick", permission: daisyfilter.punish.kick, commands: ["kick {player} {reason}"] }
ban: { display-name: "Ban", enabled: false, permission: daisyfilter.punish.ban, commands: ["ban {player} {reason}"] }

For any other plugin: add its exact command root to allowed-command-roots and write a profile using only the supported placeholders. Use essentials:/litebans: namespaced roots if two plugins both register /mute or /ban and you need a specific one.

Configured in escalation.yml and disabled by default. When enabled, DaisyFilter accumulates severity points per player within a rolling window and fires each threshold once as it is crossed. A threshold re-arms after the points decay below it.

enabled: false
window-seconds: 600
thresholds:
- { points: 8, action: WARN }
- { points: 16, action: TEMP_MUTE, duration-seconds: 300 }
- { points: 30, action: KICK }

Actions:

  • WARN — sends a stronger notice.
  • TEMP_MUTE — mutes the player. Routed through PunishBridge to your punishment plugin (e.g. LiteBans); if no provider can mute (vanilla), it falls back to the built-in mute, which is persisted and managed with /daisyfilter mutes, /unmute, /extend, and the opt-in player /appeal command.
  • KICK — removes the player with a configurable message.
  • RUN_COMMAND — runs an allowlisted console command template, for anything PunishBridge does not cover.

RUN_COMMAND only runs templates listed in escalation.command-allowlist. The {player} placeholder is replaced with a sanitized name (letters, digits, and underscores only). No command text is ever derived from player input, and everything runs from the console.