Platform setup#
Viscord talks to Discord through Javacord, to Fluxer through a raw WebSocket gateway client, and to both through webhooks for outbound chat. This page covers the console work on each platform and the tridirectional wiring that connects them.
Discord#
1. Create the application and bot#
- Open https://discord.com/developers/applications and create a new application.
- In the left sidebar open Bot and add a bot user.
- Under Privileged Gateway Intents, enable Message Content Intent.
The intent is not optional. Viscord requests all intents except
GUILD_PRESENCES and GUILD_MEMBERS, and Discord only delivers message bodies
to a bot that has been granted the message content intent. With it off, the
gateway connects, the bot appears online, and every incoming message arrives with
empty content — so Discord → Minecraft silently produces nothing.
Viscord deliberately does not request presence or member intents, so you do not need to enable those, and the bot cannot read your member list.
- Copy the bot token into
discord.bot_token.
The token is a password
A bot token grants full control of the bot. Keep it out of screenshots, issue trackers, and pasted config files. If it leaks, reset it in the developer portal and update the config.
2. Invite the bot#
Use OAuth2 → URL Generator:
- Scopes:
bot, plusapplications.commandsif you want the native slash commands registered. - Bot permissions in the bridged channel:
View Channel,Send Messages,Read Message History,Embed Links.
Manage Webhooks is only needed if you want Viscord or a helper to create and
manage the webhook for you. If you create the webhook by hand and paste its URL
into the config, the bot does not need that permission.
The bot does not need Administrator, and giving it that permission widens the
blast radius of a leaked token for no functional gain.
3. Get the channel ID#
Enable Developer Mode in Discord's Advanced settings, then right-click the target channel and choose Copy Channel ID.
[discord]
channel_id = "123456789012345678"
A comma-separated list watches several channels at once:
channel_id = "123456789012345678,234567890123456789"
Every listed channel is both read and written. The list also determines which guilds get the native command registration: Viscord resolves each channel to its guild and synchronises commands there.
4. Create the webhook#
The webhook is what makes Minecraft chat appear as the player instead of as the bot. Without it, outbound chat is dropped entirely.
- Channel settings → Integrations → Webhooks → New Webhook.
- Name it something recognisable — the name is cosmetic, because Viscord
overrides the display name per message with
messages.webhook_username. - Copy Webhook URL and paste it into
discord.webhook_url.
https://discord.com/api/webhooks/123456789012345678/YOUR_WEBHOOK_TOKEN_HERE
That URL is a placeholder pattern, not a real webhook. Treat the real one as a secret: anyone holding it can post into the channel under any name.
Viscord parses the numeric ID out of this URL on every self-origin check. There
is no separate key to configure, and the webhook_id line the generator writes
is unused.
5. Optional: split events to a second channel#
Chat and event embeds can live in different channels:
[discord.events]
channel_id = "987654321098765432"
Event embeds are sent by the bot rather than the webhook, so the bot needs
Send Messages and Embed Links in that channel too. Leaving this empty sends
events to discord.channel_id.
Setting it also makes Viscord read that channel. In a multi-server setup that is how one server's join/leave embeds become another server's in-game notifications — see Multi-server.
6. Optional: invite link for /discord#
[discord]
invite_url = "https://discord.gg/your-invite-code"
When set, /discord prints a clickable link and the [Discord] tag in bridged
chat becomes clickable as well. When empty, /discord reports that no invite is
configured and the tag stays plain text.
Fluxer#
Fluxer needs no inbound ports. The bot opens an outbound WebSocket to
wss://gateway.fluxer.app/?v=1&encoding=json, authenticates with the token, and
receives MESSAGE_CREATE events over that socket. Nothing has to be forwarded,
and the server can sit behind NAT.
1. Get a bot token and channel ID#
From the Fluxer developer portal, create a bot application and copy its token. Then copy the numeric channel ID for the channel the bot should use.
[general]
platform = "fluxer"
[fluxer]
bot_token = "YOUR_FLUXER_BOT_TOKEN"
channel_id = "YOUR_FLUXER_CHANNEL_ID"
Both values are validated against their placeholder strings as well as against
empty, so leaving either at its default produces a clear startup error:
Bot token not configured! or Channel ID not configured! The platform then
fails to initialise and isRunning() stays false, which means in-game chat is
not forwarded at all rather than being queued.
2. Optional: separate event channel#
[fluxer]
event_channel_id = "YOUR_FLUXER_EVENT_CHANNEL_ID"
Empty means events go to channel_id. The event channel is used by the bot API,
so the bot must be able to post there. Both channel_id and event_channel_id
accept comma-separated lists; every ID in either list is added to the gateway
client's allowed-channel set, and messages from any other channel are ignored
before they reach Viscord's logic.
3. Optional: webhook for per-player identity#
[fluxer]
webhook_url = "https://api.fluxer.app/v1/webhooks/{id}/{token}"
That is the documented shape — substitute the real numeric ID and token. With a
webhook configured, Minecraft chat posts with the player's name and avatar.
Without one, Viscord falls back to the bot API and posts **name**: text, which
still bridges but loses the per-player identity.
The webhook URL is also how Viscord learns its own webhook ID. It registers that ID with the gateway client and drops only messages carrying it, which is what lets other servers' Fluxer webhooks through as legitimate cross-server chat.
4. What Fluxer filtering does on its own#
Before Viscord's config filters ever run, the Fluxer client drops:
- Empty-content messages.
- Messages whose author is flagged as a bot.
- Messages whose author ID matches the bot's own ID from the READY payload.
- Messages whose
webhook_idmatches the configured webhook, when one is set. - Messages from any channel outside the allowed set.
Fluxer usernames cannot contain [ or ], so prefix-based self-detection is
unreliable there; the client does the self-check by user ID instead. That is also
why a Fluxer message never carries the [MC]-style prefix that Discord
cross-server chat relies on.
Tridirectional mode#
Tridirectional relay makes all three legs live: Discord to Minecraft, Fluxer to Minecraft, Minecraft to both, and Discord to Fluxer and Fluxer to Discord without the message having to pass through Minecraft chat first.
Required settings#
[general]
enabled = true
platform = "both"
[general.tridirectional]
enabled = true
discord_to_fluxer = true
fluxer_to_discord = true
show_source = true
[discord]
bot_token = "YOUR_BOT_TOKEN_HERE"
channel_id = "123456789012345678"
webhook_url = "https://discord.com/api/webhooks/123456789012345678/YOUR_WEBHOOK_TOKEN_HERE"
[fluxer]
bot_token = "YOUR_FLUXER_BOT_TOKEN"
channel_id = "YOUR_FLUXER_CHANNEL_ID"
webhook_url = "https://api.fluxer.app/v1/webhooks/YOUR_WEBHOOK_ID/YOUR_WEBHOOK_TOKEN"
Setting general.tridirectional.enabled = true takes the tridirectional branch in
the initialiser regardless of platform. Fluxer is initialised first and, if it
fails, the whole integration stays down: running is set back to false and
Discord is never connected. A bad Fluxer token therefore takes Discord offline
too — check Fluxer first when a tridirectional setup is completely silent.
show_source#
With show_source = true, a Discord message relayed into Fluxer is sent as
[Discord] author: message, and a Fluxer message relayed into Discord is sent
through the Discord webhook under the name [Fluxer]<username>. With it false,
the Fluxer-side relay loses the [Discord] prefix and the source is only
inferable from the author name.
Keep the tag on. The Fluxer → Discord path refuses to relay any message that
already contains the literal [Discord], which is what stops a Discord message
that was bridged into Fluxer from being bridged straight back:
if (message.contains("[Discord]")) return;
Turn show_source off and that specific guard stops matching text that carries
no tag, leaving only the echo cache as protection.
Echo suppression#
Relaying between two platforms that both echo their own writes needs a record of what Viscord just sent. The bridge keeps one:
- A bounded LRU map capped at 512 entries, access-ordered so the oldest untouched fingerprints fall out first.
- Before sending a Discord message into Fluxer through the Fluxer webhook, the
bridge records
username + ":" + content. - When a Fluxer message arrives, the bridge looks up that fingerprint. If it is present and younger than 5000 ms, the message is an echo and is dropped without reaching Minecraft or bouncing back to Discord.
The cache is only consulted on the Fluxer inbound path, because that is where a webhook-authored echo reappears. Discord-side self-origination is handled separately by the ID and prefix checks in Multi-server.
Two properties follow from this design:
- Suppression is fingerprint-based, not ID-based, so it works even when the Fluxer gateway does not surface a webhook ID for a message.
- It is bounded in both size and time. A message that takes longer than five seconds to come back will not be recognised as an echo, and a channel busy enough to push 512 newer fingerprints through will evict older ones.
Combined with several Minecraft servers#
Tridirectional plus a shared channel is supported, and it is the busiest configuration Viscord runs. The layers stack: the bridge handles Discord ↔ Fluxer echoes, and the per-server prefix and trusted-ID logic handles Minecraft-server-to-server duplicates. Both sets of rules must be satisfied. See Multi-server.
Verifying a platform comes up#
Watch the log during startup. In order, a healthy Discord start logs:
[Viscord] Config file path: .../config/viscord/viscord.toml
[Discord] Using main channel for events: <id>
Connecting to Discord...
Connected as <botname#0000>
[Discord] Native application commands synchronized.
A healthy Fluxer start logs:
[Fluxer] Initialized. Channel: <id>, Events: <id>
[Fluxer Bot] Connecting to gateway...
[Fluxer Bot] Bot connected successfully.
If the Discord block stops at Connecting to Discord..., the token or the
network path is wrong. If the Fluxer block stops at Connecting to gateway...,
the token was rejected or the gateway refused the identify payload. Both cases
are covered in Troubleshooting.