Use case

An AI chat filter for Minecraft servers

Word-list chat filters on Minecraft servers block innocent words and miss insults with no bad words in them. VigiMod reads the meaning of each message, in 26 languages, and checks your server's rules, like “No advertising other servers”.

What makes it hard

Word lists fail both ways

They block “Scunthorpe” and let “nobody wants you here” through. A model scores the meaning, not single words.

Players of every age

Many players are young. The opt-in child_safety category (beta) flags attempts to groom a minor, as a signal for a staff member.

Server ads and griefing talk

Advertising other servers or selling accounts is a rule, not a bad word. Write it as a rule and VigiMod scores it.

How it works with VigiMod

  1. 1

    A plugin listens to chat (AsyncChatEvent on Paper), which runs off the main thread.

  2. 2

    It sends the message, the player's UUID and your rules to VigiMod, with a short timeout.

  3. 3

    On a flag it cancels the message and tells the player; repeated flags can trigger your mute or warn command.

Rules you can copy

Send them in the rules field: each one comes back with its own score, like a built-in category.

{
  "rules": {
    "no-server-ads": "No advertising other Minecraft servers or Discord servers",
    "no-selling": "No selling accounts, items or ranks for real money",
    "no-personal-info": "No asking players for their age, address or phone number",
    "english-only": "Only English in global chat"
  }
}

Example

A Paper plugin listener in Java: the chat event is asynchronous, so the HTTP call does not slow the server tick.

Java (Paper plugin)

// A listener for a Paper plugin: register it in onEnable() with
// getServer().getPluginManager().registerEvents(new ChatFilter(), this);
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import io.papermc.paper.event.player.AsyncChatEvent;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;

public final class ChatFilter implements Listener {
  private final HttpClient http = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(2)).build();
  private final String key = System.getenv("VIGIMOD_KEY");

  // AsyncChatEvent runs off the main thread: waiting for the answer does not slow the server
  @EventHandler(ignoreCancelled = true)
  public void onChat(AsyncChatEvent event) {
    JsonObject rules = new JsonObject();
    rules.addProperty("no-server-ads", "No advertising other Minecraft servers or Discord servers");
    JsonObject body = new JsonObject();
    body.addProperty("text", PlainTextComponentSerializer.plainText().serialize(event.message()));
    body.addProperty("user_id", event.getPlayer().getUniqueId().toString());
    body.addProperty("scope", "my-server");
    body.add("rules", rules);

    HttpRequest request = HttpRequest.newBuilder(URI.create("https://api.vigimod.com/moderate"))
        .timeout(Duration.ofSeconds(2))
        .header("X-API-Key", key)
        .header("Content-Type", "application/json")
        .POST(HttpRequest.BodyPublishers.ofString(body.toString()))
        .build();
    try {
      HttpResponse<String> response = http.send(request, HttpResponse.BodyHandlers.ofString());
      if (response.statusCode() != 200) return;
      JsonObject result = JsonParser.parseString(response.body()).getAsJsonObject();
      if (result.get("flagged").getAsBoolean()) {
        event.setCancelled(true);
        event.getPlayer().sendMessage(Component.text("Your message was not sent: it breaks the server rules."));
      }
    } catch (Exception e) {
      // slow or down: let the message through
    }
  }
}

Tips

  • Use a short timeout (about two seconds) and let the message through if the check fails, so chat never freezes.
  • Add child_safety to the categories on servers with young players, and send its flags to staff instead of acting alone.
  • Pass the player's UUID as user_id: the same message spammed over and over raises the spam score.

Questions

Does it work on Spigot, Paper and Velocity?
The example uses Paper's AsyncChatEvent. Spigot has AsyncPlayerChatEvent, and proxies like Velocity have their own chat events; the HTTP call to VigiMod is the same everywhere.
Will it lag the server?
No, if you call it from the asynchronous chat event as in the example: the main thread is never blocked.
Can it catch spelling tricks like l33t speak?
Often, because the model reads whole sentences, but not always. Add your own blocked words with your existing plugin for the worst cases.
How many messages can it check?
The free beta allows 120 messages per minute per API key. Bigger networks can ask support for a higher limit.

Try it on your community

Create a free API key in two minutes. No card, no trial period.