Server newsletters delivered as signed books, for Paper 26.1+.
Operators write a newsletter in a book and quill, publish it with one command, and every player gets a login notice that persists until they actually read it.
| Server | Paper 26.1 or newer |
| Java (runtime) | 25 |
| Java (build) | 25 — Maven does not provision a JDK for you |
| Build tool | Maven |
| Folia | Not supported; the plugin detects it and disables cleanly |
mvn clean package
# target/Newsletter-1.0.0.jarThe Paper dependency uses the Maven range [26.2.build,), which resolves to the newest
26.2 build at build time. That is convenient but not reproducible — pin an exact version
if you need byte-identical rebuilds.
Everything hangs off /newsletter (aliases /news, /nl). Omitting a name always means
the unnamed default newsletter.
| Command | Who | What it does |
|---|---|---|
/newsletter |
everyone | Puts the default newsletter in your inventory |
/newsletter <name> |
everyone | Same, for a named newsletter |
/newsletter new [name] |
editors | Creates a newsletter |
/newsletter edit [name] |
editors | Gives you a book and quill of the current content |
/newsletter plugins |
editors | Appends the server's plugin list to the book in your hand |
/newsletter amend [name] |
editors | Publishes your draft and bumps the version |
/newsletter discard [name] |
editors | Throws the draft away |
/newsletter list |
everyone | Lists newsletters and versions |
/newsletter delete <name> |
admins | Deletes a named newsletter |
/newsletter reload |
admins | Reloads config and storage from disk |
| Node | Default | Grants |
|---|---|---|
newsletter.read |
everyone | Reading newsletters |
newsletter.notify |
everyone | Login greeting and update notices |
newsletter.edit |
op | Editing every newsletter |
newsletter.edit.<name> |
op | Editing one newsletter |
newsletter.edit.default |
op | Editing the unnamed default |
newsletter.admin |
op | Delete and reload |
Nodes are declared in plugin.yml, so LuckPerms and similar pick them up with no setup.
Granting newsletter.edit.events to a trusted player lets them run the whole edit flow on
that one newsletter without op.
Minecraft only sends a book's contents to the server when you click Done or Sign. Closing a book with Escape throws away everything you typed, and no plugin can recover it.
So the flow is:
/newsletter edit— you get a book and quill- Write, then click Done. The draft saves automatically and you get a confirmation.
/newsletter amend— publishes it
If you run amend and are told there are no unpublished changes, you almost certainly
closed the book without clicking Done.
Signing the book in-game is intercepted and turned back into a draft save, so you can't
accidentally freeze your working copy into an immutable written book. Publishing is what
amend is for.
Hold the book and quill from /newsletter edit in your main hand and run it. The plugin
list is appended to the end, chunked across pages. Click Done in the book first if you
have unsaved typing — the command works from the server's copy.
Filter it via plugins-command.exclude, include-disabled, and include-self in the
config.
Page content is MiniMessage by default, so you can type <red>Downtime Sunday</red> into
the book. You edit the raw source and see the tags; the published book shows the result.
Malformed markup never throws — it falls back to literal text.
Set book.page-format: plain to disable this entirely and treat pages literally.
Minecraft caps books at 100 pages of 1024 characters. Content over that is cut, and you are told exactly how much was dropped or truncated rather than losing it silently.
plugins/Newsletter/
config.yml
readers.yml who has read which version
newsletters/
__default__.yml the unnamed default
events.yml a named newsletter
Newsletters store pages as plain source strings rather than serialized components. This is deliberate: the Adventure component API broke between 1.21 and 26, and storage that does not depend on it survives the next such change.
readers.yml is flushed on a timer and on shutdown rather than on every join, and records
for players not seen in 90 days are pruned at startup.
- Never use
instanceofon a book meta.BookMeta(written book) extendsWritableBookMeta(book and quill), soinstanceof WritableBookMetais true for both. Branch onItemStack.getType()first, then cast. Every path inBookFactorydoes. - Commands are registered through Paper's Brigadier API at enable time, so there is no
commands:block inplugin.yml. Brigadier resolves literals before arguments, which is why/newsletter editis unambiguous — but names colliding with a subcommand are still rejected at creation, since such a newsletter could never be opened. - The book item's persistent data tag is the primary identifier for
pluginsand the edit flow, and the tag is re-applied on every edit in case the client round trip drops it. An in-memory session is kept as a backstop and to warn when a no-nameamendwould publish the default while a named newsletter is open. - Every scalar in
plugin.ymlandconfig.ymlis quoted. An unquoted value containing a colon-space crashes the server at load withmapping values are not allowed here.