Discord API Reference
A stable, JDA-free surface that other Warring Nations addons build on to reach WN Discord, the same surface WN Discord's own built-in commands use internally.
Entry Point
Everything lives under one Java package, and every outbound method on it is non-blocking.
dev.tacyeet.wndiscord.api
DiscordApi.get() returns an Optional-style bridge handle, empty when WN Discord isn't installed or hasn't finished starting. Every actual call happens inside the ifPresent callback:
DiscordApi.get().ifPresent(bridge -> {
bridge.registerChannel("myaddon", "My addon's feed");
});
Registering a Channel
bridge.registerChannel("myaddon", "My addon's feed") declares a named feed channel for an addon, the same mechanism WN Siege uses to register its /siege command's own channel presence and the same one behind WN Discord's own built-in nation/war/diplomacy/village/government feeds.
WN BlueMap posts a map link to WN Discord whenever a war starts or ends. Registering its own channel first gives it a stable destination to send that embed to.
Sending an Embed
bridge.sendEmbed(ChannelRef.of("myaddon"), DiscordEmbed.builder()...build()) posts a rich embed to a registered channel. DiscordEmbed.builder() takes at least color(int), title(String), and description(String).
Registering a Slash Command
bridge.registerSlashCommand(SlashCommand.builder("mycmd", "Does a thing").handler(interaction -> interaction.reply("Done!")).build()) adds a new top-level slash command through the same registration path WN Discord's own 17 top-level commands use.
WN Siege registers a /siege command through this API rather than shipping its own separate Discord bot integration.
Full Example
The three pieces above, combined into one addon-startup block: register a channel, post an embed to it, and add a slash command.
DiscordApi.get().ifPresent(bridge -> {
bridge.registerChannel("myaddon", "My addon's feed");
bridge.sendEmbed(ChannelRef.of("myaddon"), DiscordEmbed.builder()
.color(0x5865F2).title("Hello").description("From my addon").build());
bridge.registerSlashCommand(SlashCommand.builder("mycmd", "Does a thing")
.handler(interaction -> interaction.reply("Done!"))
.build());
});
Registering a Placeholder
registerPlaceholder adds a new templated token an addon can use in its own embeds and messages, the same mechanism behind WN Discord's own built-in placeholders like %gov_entity%, %gov_tally%, and the bot presence text's %online%/%activewars%/%currentwar% tokens. WN Discord's own ~220 templated strings are built on this same substitution mechanism, so an addon-registered placeholder works anywhere a template string is rendered, not just in content the addon itself sends.
hubOnly() and adminOnly() Defaults
WN Discord distinguishes the shared hub guild from a nation's own linked Discord server (see Per-nation Discord servers), and staff-only commands from player-facing ones. A registered command or channel opts into the same two gates WN Discord's own built-in surface already uses:
Restricts registration to the shared hub guild, never publishing to, or running from, a nation's own linked Discord server. WN Discord's own /setup, /config, /debug, /staff, and /whitelist namespaces are hub-only for exactly this reason: they read or mutate bridge-wide identity and configuration, or exercise server-wide power a nation's own Discord has no business reaching. Any staff-only command is hub-only by default unless a maintainer deliberately opts it out.
Hides the command from non-staff automatically, the same gate WN Discord's own five staff-only namespaces (/staff, /whitelist, /setup, /config, /debug) use, discord-side, with no separate permission wiring required from the addon.
What's Already Built On This API
This isn't a theoretical surface: WN Discord's own two companion addons are the existing reference implementations.
Error Behavior
The bridge is written to be safe to call carelessly: the one check worth doing up front is DiscordApi.get() being empty, and after that every outbound method is documented as never throwing.
DiscordBridge's own class doc states it plainly: all outbound methods are non-blocking and never throw. There is no checked or runtime exception to catch around sendEmbed, registerChannel, or registerSlashCommand.
A message is queued onto the dispatch thread and delivered asynchronously, or dropped with a logged warning if the bridge is disconnected. Either way the call site gets no return-value signal; check isReady() first if the caller needs to know before sending.
registerChannel/registerSlashCommand are documented as safe to call during server start, so an addon doesn't need to guard against calling registration more than once.
Threading
Every outbound method on DiscordBridge is non-blocking by design, so calling it never stalls the caller on a Discord round trip.
