Discord Bots with open-source tools
This guide outlines the architecture for a bi-directional bridge between a legacy MUD server and a Discord community. It focuses on using a middleware bot to handle Telnet-to-Discord translation, ensuring stability without requiring deep modifications to the MUD's core socket handling.

Establish the Middleware Architecture
Do not connect the MUD directly to Discord's API. Instead, create a middleware service that acts as a Telnet client to the MUD and a Bot client to Discord. This prevents MUD crashes if the Discord API experiences latency and allows for easier restarts of the bridge without dropping game connections.
const net = require('net');
const { Client, GatewayIntentBits } = require('discord.js');
const mudClient = new net.Socket();
const discordClient = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent] });
mudClient.connect(4000, '127.0.0.1', () => {
console.log('Connected to MUD');
});⚠ Common Pitfalls
- •Binding the bridge to the same port as the MUD.
- •Failing to handle 'ECONNRESET' when the MUD reboots for a hotfix.
Filter and Sanitize ANSI Escape Codes
MUDs transmit color via ANSI escape sequences (e.g., ^[[31m). Discord does not support these. Your bridge must strip or convert these sequences before sending strings to the Discord API to avoid raw control characters appearing in chat.
function stripAnsi(text) {
return text.replace(/[\u001b\u009b][[\]()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, '');
}⚠ Common Pitfalls
- •Incomplete regex patterns leaving trailing brackets.
- •Accidentally stripping legitimate brackets used in MUD syntax like [Group].
Implement Webhook for Player Identity
Using a standard Bot profile for all messages makes the chat hard to follow. Use Discord Webhooks to dynamically update the username and avatar of the sender to match the in-game player's name.
async function sendToDiscord(channel, playerName, message) {
const webhooks = await channel.fetchWebhooks();
const bridgeHook = webhooks.first();
await bridgeHook.send({
content: message,
username: playerName,
avatarURL: `https://api.dicebear.com/7.x/bottts/png?seed=${playerName}`
});
}⚠ Common Pitfalls
- •Creating a new webhook for every message (hits Discord rate limits).
- •Hardcoding webhook URLs in public repositories.
Configure Rate Limit Buffering
MUDs generate text faster than Discord's 5-messages-per-5-seconds limit. Implement a message queue that aggregates small lines into a single Discord message or delays transmission during high-traffic combat events.
let messageQueue = [];
setInterval(() => {
if (messageQueue.length > 0) {
const batch = messageQueue.splice(0, 5).join('\n');
discordChannel.send(batch);
}
}, 2000);⚠ Common Pitfalls
- •Infinite loops where Discord messages are relayed back to the MUD and then back to Discord.
- •Buffer overflows during 'spam' events if the queue isn't capped.
Secure the In-Game Bridge Account
The bridge connects to the MUD like a player. Create a specific 'Bridge' user in the MUD with restricted permissions. Ensure this user can only see specific channels (e.g., OOC, Gossip) and cannot be targeted by NPCs or players.
⚠ Common Pitfalls
- •Leaving the bridge account vulnerable to 'force' or 'teleport' commands.
- •Mapping staff-only channels to public Discord channels by mistake.
- •Allowing the bridge user to trigger game commands via Discord without strict role-based access control.
What you built
A successful Discord-MUD integration requires a robust middle layer that handles the disparate protocols of legacy Telnet and modern REST APIs. By implementing ANSI stripping, webhook identity mapping, and rate-limit buffering, you can extend your MUD's community reach without compromising game performance or security.