Sueta

AutoMessage [1.21]

3.0.0

⚡ AutoMessage is a high-performance plugin for fully customizable automatic server-wide broadcasts.

·Plugin

AutoMessage - right message, right player, right moment

Latest release Build status Java 21 or newer; Java 25 for Minestom Minecraft 1.21 through 26.2 Six runtime targets

Modrinth downloads SpigotMC downloads Available on Hangar

Not another chat rotator. A complete announcement engine.
Targeted, localized and event-aware broadcasts for modern Minecraft servers and networks.

6 runtimes  ·  8 notice surfaces  ·  8 audience rules  ·  3 event triggers  ·  3 languages shipped  ·  one render per language  ·  no database

📣 Broadcasts should earn attention

Players learn to ignore announcements when every message reaches everyone. AutoMessage makes server
communication deliberate: every stream keeps its own schedule, every message chooses its own
audience, and the text arrives in the language each player actually plays in - from one shared core
running on Bukkit, Folia, Sponge, Velocity, Fabric and Minestom.

Three announcement surfaces in game at once: a boss bar countdown across the top of the screen, a full-screen title in the middle, and a chat line below it

One announcement set, in game, on one server: a boss bar countdown, a full-screen title and a chat line.

A complete announcement, start to finish

Greet returning veterans three seconds after they join - never on a timer, never the newcomers, and
in their own language. Two files say all of it:

# scheduledMessages.yml - who gets it, and when
messages:
  - name: welcome-back
    trigger:
      type: JOIN
      delay: 3s
    rules:
      - type: PLAYTIME
        min: 10h
# lang/en.yml - what it says
announcements:
  welcome-back:
    - chat: "<gray>Welcome back, <aqua>{PLAYER}<gray>."
      sound: "entity.player.levelup MASTER 1.0 1.2"

And this is what the server does with them:

Two players join. Three seconds later the veteran with 240 hours played passes the playtime rule and receives the greeting with a level-up sound; the newcomer with twelve minutes played receives nothing at all.

That is the whole feature set in miniature: an event instead of a clock, an audience instead of
everyone, and text that lives beside every other translation.

⚖️ More than a rotating list

The tagline is not marketing. A rotator answers one question - what do I say next? - and sends
the answer to everyone, in one language, on one clock.

A plain rotatorAutoMessage
Who receives itEveryone onlinePermission, group, world, online count, playtime - composed with ANY_OF, NONE_OF, NOT
When it firesOne global intervalA schedule per channel, plus JOIN, FIRST_JOIN and re-arming PLAYER_COUNT triggers
What it looks likeA line of chatChat, action bar, title, subtitle, boss bar and sound, combined in one announcement
What it saysOne text for everybodyThe player's own language, with a per-message fallback
Where it runsOne server typeBukkit, Folia, Sponge, Velocity, Fabric and Minestom, each a native artifact

The details underneath

🔀 Four rotation strategies

SHUFFLE, SEQUENTIAL, RANDOM or weighted, chosen per channel. Shuffle deals a whole deck before anything repeats.
🧩 Configuration that fits the runtime

A proxy never sees a world rule or a first-join trigger. Options the platform cannot honor are left out of the file, not written and ignored.
👁 Preview before publishing

/automessage view <name> renders the real localized message to you alone, ignoring its audience rules.
↗ Beyond the server

Mirror chat content to a Discord webhook, with player-only placeholders stripped and automatic mentions disabled.
♻ Reload, do not restart

/automessage reload applies schedules, messages and languages in place - including a translation file added while the server was running.
🪶 Lean dispatch path

No database. An empty server short-circuits, placeholders are scanned once per message rather than once per announcement, and everyone reading the same text in the same language is served from a single render.

🎯 Reach exactly who should hear it

Rules compose. Every top-level rule must pass, and ANY_OF, NONE_OF and NOT nest inside them
when the audience needs more than an AND.

Twelve players online; a rule requiring the rank.vip permission and at least ten hours of playtime selects five of them, the rest stay dark

The same rules apply to a message sent by an event, so a join greeting restricted to a permission
never reaches somebody who lacks it just because they walked through the door.

⚡ Fire on an event, not a clock

A message with a trigger leaves its channel rotation entirely and waits for something to happen.

Three lanes: a channel firing on a five-minute period, a join trigger firing three seconds after a player connects, and a player-count milestone firing once at one hundred online and re-arming only after the count falls below

PLAYER_COUNT is the one worth reading twice: it fires once on the way up and re-arms only
after the server drops back below the threshold, so a population hovering on the boundary does not
announce the same milestone every time somebody logs in.

🪶 Costs your server almost nothing

Formatting is the expensive part of an announcement. Colors, gradients, hover text and placeholders
all have to be parsed before a single character reaches anybody - and the obvious way to do that is
once for each player who receives it. Five hundred players online means the same sentence built five
hundred times to say one thing, on the main thread, while the rest of the tick waits its turn.

AutoMessage builds it once per language and hands the finished result to everyone reading it.

One announcement reaching 500 players. Formatting once per player means 500 parses of the same text and costs 5.16 ms, about a tenth of a server tick. AutoMessage formats once per language and costs 0.34 ms.

Formatting per playerAutoMessage
Parses per announcementOne per player, so 500One per language actually being read
Time on the main thread5.16 ms0.34 ms
Share of a 50 ms tick~10%, every announcement~0.7%

That is 15× less main-thread time per announcement, measured on the real send path with 500
viewers online - not on a microbenchmark of one function. What remains is handing the finished text
to each connection, which no plugin can avoid.

The saving grows with your player count, and it costs you nothing to configure: there is no cache to
size, no setting to tune, and no behavior to give up.

It never trades correctness for speed. A message that reads differently for each player - one
naming {PLAYER}, or carrying any PlaceholderAPI token - is still built for each of them, because
only the expansion behind that token knows whether two players would see the same thing. Grouping
applies exactly where the text is genuinely identical, and nowhere else.

Three more places the dispatch path refuses to do needless work:

  • Placeholders are scanned once per message, not once per announcement. Which tokens a message
    contains is decided by the text, so it is worked out when the files are read and remembered until
    a reload - rather than re-derived every time the channel fires.
  • An empty server short-circuits. Nobody online means no selection, no formatting and no work.
  • Nothing touches disk while announcing. No database, no player records, no I/O on the hot path.

🌐 One engine. Six runtimes

AutoMessage uses a shared platform-neutral core and a small runtime adapter for each server type.
That means the behavior stays familiar while scheduling, viewers, permissions and lifecycle hooks
remain native to the platform underneath.

One platform-neutral core connected to six artifacts: Bukkit, Folia, Sponge, Velocity, Fabric and Minestom

ArtifactRuntimeSupported lineJavaPlatform-specific notes
BukkitSpigot, Paper, Purpur and other Bukkit-derived serversMinecraft 1.21–26.221+Full feature set; optional PlaceholderAPI integration
FoliaFoliaMinecraft 1.21–26.221+Dedicated regionized scheduling; optional PlaceholderAPI integration
SpongeSponge API 17Minecraft 1.21.1021+Full feature set except PlaceholderAPI
VelocityVelocity 3.5.1 proxyProxy runtime21+No world, playtime, first-join or PlaceholderAPI capabilities
FabricDedicated Fabric serverMinecraft 1.21.1121+Server-side mod; Fabric API required; no PlaceholderAPI
MinestomEmbedded Minestom libraryProtocol 1.21.1125+Classpath library; no world, playtime, first-join or PlaceholderAPI; permission/group rules require your callback

[!NOTE]
Java 21 is the bytecode floor for the plugin artifacts, not a promise that every Minecraft
server version runs on Java 21. Use the JVM required by your server; Minestom itself requires
Java 25.

Each artifact writes a platform-aware configuration. Options the runtime cannot honor are left out
instead of being generated and silently ignored.

The same generated file on Paper and on a Velocity proxy: the world rule, playtime rule, first-join trigger, world placeholder, PlaceholderAPI note and playtime example are absent from the proxy file

What about Quilt?

There is no separate Quilt artifact. Quilt can read Fabric metadata, but this branch does not claim
a tested Quilt version. Treat the Fabric jar on Quilt as experimental and bring a compatible
Fabric API yourself.

🗂 Configuration model

AutoMessage separates when, who and what. Adding a language never requires copying
scheduling rules, and changing a channel interval never touches the message text.

flowchart LR
    schedule["config.yml<br/>WHEN<br/>channels · periods · selectors"]
    audience["scheduledMessages.yml<br/>WHO<br/>rules · weights · triggers"]
    content["lang/*.yml<br/>WHAT<br/>localized MiniMessage notices"]
    minecraft["Minecraft<br/>PLAYER OUTPUT"]
    discord["discordWebhook.yml<br/>OPTIONAL MIRROR"]

    schedule --> audience --> content --> minecraft
    content -. "chat text" .-> discord

    classDef config fill:#11182d,stroke:#8b7cf6,color:#eef0ff,stroke-width:1px;
    classDef output fill:#0d2028,stroke:#45d7e8,color:#ecfeff,stroke-width:1px;
    class schedule,audience,content config;
    class minecraft,discord output;
FileResponsibility
config.ymlMaster switch, fallback language, channels, startup delays, periods and selectors
scheduledMessages.ymlMessage names, channel assignment, weights, audience rules and event triggers
lang/<code>.ymlLocalized command replies and the actual MiniMessage announcement payloads
discordWebhook.ymlOpt-in Discord webhook, display name and avatar

📥 Get AutoMessage

What you needWhere to get it
Current public Bukkit buildModrinth · SpigotMC · Hangar
Release history and changelogsGitHub Releases
Multiplatform artifacts from this branchBuild all six from source

[!IMPORTANT]
Release status: this README documents the local multiplatform branch. The current public
listings still represent the earlier Bukkit-focused release line. Until a new GitHub release
publishes all six artifacts, build the multiplatform jars with ./gradlew dist.

🚀 Quick start

  1. Get the artifact built for your runtime. Platform jars are intentionally separate.
  2. Put it in your platform's plugins/ or mods/ directory. For Minestom, add the jar to the
    application classpath and use the builder shown below.
  3. Start the server once. AutoMessage generates annotated configuration plus working example
    announcements.
  4. Edit config.yml, scheduledMessages.yml and the files inside lang/.
  5. Run /automessage reload, then test a message with /automessage view <name>.

[!TIP]
Start with the generated files. They are not empty templates: every available field is explained
next to a working example, and the contents adapt to the platform that created them.

🛠 Build your first announcement

The example below creates an independent event stream, adds one announcement to it, and renders
multiple notice types from a single localized payload.

1. Schedule the stream

# config.yml
enabled: true
fallbackLanguage: en

channels:
  - name: default
    enabled: true
    initialDelay: 1m
    period: 5m
    selector: SHUFFLE

  - name: events
    enabled: true
    initialDelay: 30s
    period: 15m
    selector: SEQUENTIAL

2. Register the announcement

# scheduledMessages.yml
messages:
  - name: event-night
    channel: events

3. Design what players receive

# lang/en.yml
announcements:
  event-night:
    - chat:
        - "<dark_gray>[<gold>EVENT<dark_gray>] <gray>The arena opens in <gold>5 minutes<gray>."
        - "<gray>Click <click:run_command:'/warp arena'><hover:show_text:'<yellow>Teleport'><aqua><u>here</u></aqua></hover></click> to join."
      actionbar: "<yellow>Prepare your loadout - the event starts soon"
      title: "<gradient:#ffd166:#ff6b6b><bold>EVENT NIGHT</bold></gradient>"
      subtitle: "<gray>The arena opens in 05:00"
      times: "500ms 3s 500ms"
      bossbar:
        message: "<light_purple>Event countdown"
        duration: 10s
        color: PURPLE
        overlay: PROGRESS
        progress: 0.75
      sound: "block.note_block.pling MASTER 1.0 1.0"

The same announcement on four surfaces at once: a boss bar countdown, a gradient title and subtitle, two chat lines and an action bar above the hotbar

Use only the parts that fit the message. A simple chat announcement can be a single string; a
showcase announcement can combine every surface above.

📖 Reference

Announcement channels and selectors

Every channel has its own schedule and independent selector state:

channels:
  - name: tips
    enabled: true
    initialDelay: 1m
    period: 5m
    selector: SHUFFLE

  - name: promotions
    enabled: true
    initialDelay: 2m
    period: 20m
    selector: WEIGHTED
SelectorBehavior
SHUFFLERandomizes the order and shows every message once before anything repeats
SEQUENTIALWalks straight through the configured list
RANDOMDraws independently; the same message may appear twice in a row
WEIGHTEDDraws randomly in proportion to each message's weight

SEQUENTIAL and SHUFFLE keep their position across a normal configuration reload. A weight of
0 parks a message only in a WEIGHTED channel; other selectors do not consult weights.

<det

Similar projects

Plugin →