Discord

Send global chat, joins, leaves, server switches, and private messages to Discord with a bot or webhooks, and show Discord chat in Minecraft.

Discord settings are in plugins/nexusshout/discord.yml (plugins/NexusShout/discord.yml on BungeeCord), apart from config.yml, so you can keep tokens and webhook URLs out of shared or version-controlled files. The integration is off by default (enabled: false). Run nshoutreload after editing.

The default discord.yml explains every setting in its comments.

Bot or webhook

Each message is sent either by a bot or by a webhook. You choose per event, so you can mix them, for example the bot for chat and webhooks for joins and leaves.

BotWebhook
Minecraft to DiscordYesYes
Discord to MinecraftYesNo
Name and avatarThe bot's ownThe player's name and head
EmbedsYesYes
NeedsA bot token, your server ID, and channel IDsWebhook URLs

Quick start: webhook

  1. In Discord, open the channel settings, go to Integrations, then Webhooks, and create a webhook. Copy its URL.
  2. In discord.yml:
enabled: true
channel-routing:
  default-route:
    webhook-url: "https://discord.com/api/webhooks/..."
events:
  global-chat:
    enabled: true
    transport: "webhook"
  1. Run nshoutreload.

With the default file, joins and leaves also go to this webhook. Turn them off under events if you do not want them.

Quick start: bot

  1. Create an application at the Discord Developer Portal, add a bot, and copy its token.
  2. To show Discord chat in Minecraft, turn on Message Content Intent and Server Members Intent on the Bot page.
  3. Invite the bot to your Discord server with View Channels, Send Messages, Read Message History, and Add Reactions. Add Embed Links if you use embeds.
  4. Turn on Developer Mode in Discord (User Settings, Advanced). Then right-click your server and your channel to copy their IDs.
  5. In discord.yml:
enabled: true
bot:
  enabled: true
  token: "your-bot-token"
  guild-id: "111111111111111111"
  inbound:
    enabled: true
channel-routing:
  default-route:
    channel-id: "222222222222222222"
events:
  global-chat:
    enabled: true
    transport: "bot"
  1. Run nshoutreload. The console shows Discord bot logged in as ... once the bot is connected.

Keep the token secret. Anyone with it can control the bot.

Routing

channel-routing:
  default-route:
    channel-id: "111111111111111111"
    webhook-url: ""
  overrides:
    Lobby-1:
      channel-id: "222222222222222222"
    SkyBlock:
      channel-id: "333333333333333333"
      webhook-url: "https://discord.com/api/webhooks/..."

Keys under overrides are server names from the proxy config (velocity.toml, or config.yml on BungeeCord). channel-id is used by bot events, and webhook-url by webhook events.

EventWhere it goes
global-chatThe sender's server in overrides, otherwise default-route
server-switchThe server the player moved to, in overrides, otherwise default-route
player-join, player-leavedefault-route
private-messagesIts own channel-id or webhook-url under events.private-messages

For webhook events other than private-messages, when no webhook URL is found, webhooks.default-url is used.

Events

EventDefaultTransportPlaceholders
global-chatOnbot{name}, {uuid}, {server}, {server_alias}, {message}, {custom_rank}, {custom_name}
private-messagesOffwebhook{sender}, {receiver}, {message}
player-joinOnwebhook{name}, {uuid}
player-leaveOnwebhook{name}, {uuid}
server-switchOffwebhook{name}, {uuid}, {from_server}, {to_server}, {from_server_alias}, {to_server_alias}

Join and leave mean joining and leaving the network, not a single server. {server_alias} is the display name from server-aliases in config.yml. The join is posted when the player reaches their first server. Joins, leaves, and server switches of vanished players (PremiumVanish or SayanVanish) are not posted.

Each event can have these settings:

SettingDefaultNotes
enabledsee above
transportsee abovebot or webhook
formatsee the default fileThe Discord text. Discord markdown works here
strip-formattingtrueRemoves Minecraft colors from the values
allowed-mentions["users"] ([] for private-messages)Which mentions can ping: users, roles, everyone. Use [] for none
use-embedfalseSends the text as an embed. Not used for private-messages
embed-colorsee the default fileEmbed color in hex, for example #2ecc71
enabled-servers[]global-chat and server-switch only. Empty: all servers

Player names and messages are escaped, so players cannot use Discord markdown such as bold text or quotes. When {name} or {message} is inside backticks in your format, only backticks in the value are replaced.

Webhook settings

webhooks:
  default-url: ""
  use-player-head-avatar: true
  crafatar-url-template: "https://crafatar.com/avatars/{uuid}?overlay=true"
  fallback-avatar-url: ""
  username-template: "{name}"
SettingNotes
default-urlUsed when no route has a webhook URL
use-player-head-avatarShows the player's head as the avatar, from crafatar-url-template
fallback-avatar-urlAvatar used when use-player-head-avatar is false. Empty: the webhook's own avatar
username-templateName shown on the message. Placeholders: {name}, {server}, {server_alias}

These apply to webhooks only. Bot messages use the bot's own name and avatar.

Discord to Minecraft

Messages from Discord need the bot, with bot.inbound.enabled: true and the two intents from the quick start.

bot:
  inbound:
    enabled: false
    channels: []
    allowed-role-ids: []
    mc-format: "&9[Discord] &b{discord_display_name}&7: &f{message}"
    show-replies: true
    convert-markdown: true
    sanitize-mentions: true
    ignore-bots: true
    max-length: 256
SettingNotes
channelsChannel IDs whose messages go to Minecraft. Empty: every channel-id in channel-routing
allowed-role-idsOnly members with one of these role IDs reach Minecraft. Empty: everyone in the channel
mc-formatHow the message looks in Minecraft. Placeholders: {discord_name} (username), {discord_display_name} (server nickname), {role} (their top role), {channel}, {message}
show-repliesWhen the message is a reply, shows the original above it in gray italics
convert-markdownTurns bold, italic, underline, strikethrough, and code into Minecraft formatting
sanitize-mentionsShows mentions as @name and #channel, custom emoji as :name:, and stops @everyone and @here
ignore-botsSkips messages from bots, including other bridge bots. Keep it on to avoid loops
max-lengthLonger messages are cut short and end with ...
deny-feedbackWhat happens when a member without an allowed role posts: react with reaction-emoji, and reply with reply-message. By default it only reacts
  • When guild-id is set, only messages from that Discord server are used.
  • Discord text cannot add Minecraft formatting of its own.
  • Messages from Discord follow enabled-servers in config.yml, and they are never sent back to Discord.

Bot status

bot:
  activity:
    type: "PLAYING"
    text: "Minecraft on {player_count} servers"
    update-interval: 60
  • type: PLAYING, WATCHING, LISTENING, COMPETING, STREAMING, or NONE. STREAMING uses a generic Twitch link.
  • text: {player_count} is the number of players on this proxy, and {server_count} the number of servers registered on it.
  • update-interval: seconds between updates. 0 sets the status once at startup.

Intents

SetupPrivileged intents needed
Bot only sends to DiscordNone
Discord to Minecraft is onMessage Content Intent and Server Members Intent

Delivery

delivery:
  queue-size: 500
  min-interval-ms: 250
  max-retries: 3
  retry-backoff-ms: 1000
SettingNotes
queue-sizeMessages waiting per channel or webhook. When full, the oldest is dropped and a warning is logged
min-interval-msShortest time between two sends to the same channel or webhook. Discord allows about 5 messages per 5 seconds per channel
max-retriesWebhooks only. How many times a failed send is tried again, after a Discord server error or a rate limit
retry-backoff-msWebhooks only. Wait before the first retry, doubled each time. After a rate limit, the wait Discord asks for is used

More than one proxy

You can use the same discord.yml on every proxy. Each chat message reaches Discord once, and each Discord message reaches Minecraft once. See multi-proxy.