Skip to content

Websocket Chat Discord Integration (Discord.js)

dane edited this page May 7, 2025 · 1 revision

This page will walk you through setting up your Discord.js bot to communicate bidirectionally with a WebSocket-based webchat on your website. Messages from the website chat will be posted to a specific Discord channel, and messages from that Discord channel will be sent to the webchat, including the sender's display name and top role color.

This guide assumes you have a working Discord.js bot project. If you don't, please refer to the official Discord.js documentation for setup instructions: https://discord.js.org/

Prerequisites

  • A working Discord.js bot project.

  • ws (WebSocket client library) installed in your project: npm install ws

  • A .env file with the following environment variables set:

    • ENABLE_WEBCHAT: Set to true to enable this webchat integration.
    • WEBCHAT_WSS_URL: The WebSocket Secure (wss://) URL of your webchat server.
    • WEBCHAT_CHANNEL_ID: The ID of the Discord channel to use for webchat messages.
    • WEBCHAT_MAX_RECONNECT_ATTEMPTS (optional, defaults to 5): Maximum number of immediate reconnection attempts if the WebSocket connection drops.
    • WEBCHAT_RECONNECT_TIMEOUT (optional, defaults to 30000ms): Time in milliseconds to wait before retrying connection after MAX_RECONNECT_ATTEMPTS have been exhausted.
    • WEBCHAT_RECONNECT_DELAY (optional, defaults to 5000ms): Time in milliseconds to wait between each immediate reconnection attempt.
  • The "Message Content Intent" enabled in your bot's settings on the Discord Developer Portal.

Implementation

We'll create a module to handle the WebSocket connection and message forwarding.

webSocketHandler.js (Module)

Create a file named webSocketHandler.js (or a similar name) in your project, for example, in a handlers or utils directory. Add the following code:

const WebSocket = require('ws');

function setupWebSocket(client) {
    const WEBCHAT_WSS_URL = process.env.WEBCHAT_WSS_URL;
    const MAX_RECONNECT_ATTEMPTS = parseInt(process.env.WEBCHAT_MAX_RECONNECT_ATTEMPTS, 10) || 5;
    const RECONNECT_TIMEOUT = parseInt(process.env.WEBCHAT_RECONNECT_TIMEOUT, 10) || 30000;
    const RECONNECT_DELAY = parseInt(process.env.WEBCHAT_RECONNECT_DELAY, 10) || 5000;
    const MESSAGE_TYPE = 'Discord';
    let ws;
    let reconnectAttempts = 0;

    if (process.env.ENABLE_WEBCHAT === 'true') {
        function connect() {
            ws = new WebSocket(WEBCHAT_WSS_URL);

            ws.on('open', () => {
                console.log('Webchat: Connected to Webchat WebSocket server');
                reconnectAttempts = 0;
            });

            ws.on('message', (message) => {
                const data = JSON.parse(message);
                if (data.type === 'message') {
                    const discordChannelId = process.env.WEBCHAT_CHANNEL_ID;
                    const discordChannel = client.channels.cache.get(discordChannelId);
                    if (discordChannel) {
                        if (data.data.message_type !== MESSAGE_TYPE) {
                            discordChannel.send(`**<${data.data.username}>:** ${data.data.content}`);
                        }
                    }
                }
            });

            ws.on('error', (error) => {
                console.error('Webchat: WebSocket error:', error);
            });

            ws.on('close', () => {
                if (reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
                    reconnectAttempts++;
                    console.log(`Webchat: WebSocket connection closed. Attempting to reconnect in ${RECONNECT_DELAY / 1000} seconds... (Attempt ${reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})`);
                    setTimeout(connect, RECONNECT_DELAY);
                } else {
                    console.log(`Webchat: Max reconnection attempts reached. Waiting for ${RECONNECT_TIMEOUT / 1000} seconds before retrying...`);
                    setTimeout(() => {
                        reconnectAttempts = 0;
                        connect();
                    }, RECONNECT_TIMEOUT);
                }
            });
        }

        connect();

        client.on('messageCreate', (message) => {
            if (message.channel.id === process.env.WEBCHAT_CHANNEL_ID && !message.author.bot) {
                const member = message.guild.members.cache.get(message.author.id);
                const topRoleColor = member.roles.highest.color;

                const data = {
                    messageId: message.id,
                    username: message.author.displayName,
                    content: message.content,
                    message_color: topRoleColor,
                    message_type: MESSAGE_TYPE
                };

                ws.send(JSON.stringify(data));
            }
        });
    }

}

module.exports = setupWebSocket;

Explanation

Environment Variables & Setup: The function first checks ENABLE_WEBCHAT. If not true, it exits. It retrieves necessary URLs, IDs, and reconnection parameters from process.env. MESSAGE_TYPE: A constant string ("Discord") used to identify messages originating from this Discord bot, preventing echo loops.

  • connect() Function: Establishes a connection to your WEBCHAT_WSS_URL.

  • ws.on('open'): Logs successful connection and resets reconnectAttempts.

  • ws.on('message'): Parses the incoming JSON message from the webchat server. If it's a message type and not originally from Discord (checked via data.data.message_type !== MESSAGE_TYPE), it forwards the message to the configured WEBCHAT_CHANNEL_ID on Discord.

  • ws.on('error'): Logs WebSocket connection errors.

  • ws.on('close'): Implements a robust reconnection strategy:

    • It attempts to reconnect MAX_RECONNECT_ATTEMPTS times with a RECONNECT_DELAY between each attempt.
    • If these attempts fail, it waits for a longer RECONNECT_TIMEOUT before resetting the attempt counter and trying the connection cycle again.
  • Initial connect() Call: Starts the WebSocket connection process when setupWebSocket is invoked.

  • client.on('messageCreate', ...): Listens for new messages on Discord:

    • Channel & Author Check: Ensures the message is in the designated WEBCHAT_CHANNEL_ID and not sent by a bot (to avoid loops with itself or other bots).
    • WebSocket State Check: Ensures the WebSocket connection is open (ws && ws.readyState === WebSocket.OPEN) before attempting to send.
    • User Details: Fetches the guild member to get their displayName (nickname in the server) and their roles.highest.hexColor for the top role color.
    • Payload Construction: Creates a JSON object containing the message ID, username, content, role color, and the MESSAGE_TYPE set to "Discord".
  • ws.send(): Sends the stringified JSON payload to the webchat WebSocket server.

Loading the WebSocket Handler

In your main bot file (e.g., index.js or bot.js), you need to require and call this setup function after your Discord client is ready.

// In your main bot file (e.g., index.js)
const { Client, GatewayIntentBits /*, ...other intents */ } = require('discord.js');
const dotenv = require('dotenv');
const setupWebSocket = require('./path/to/your/webSocketHandler'); // Adjust path as needed

dotenv.config();

const client = new Client({
  intents: [
    GatewayIntentBits.Guilds,
    GatewayIntentBits.GuildMessages,
    GatewayIntentBits.MessageContent, // Ensure this is enabled
    // ... other necessary intents
  ],
});

client.once('ready', () => {
  console.log(`Logged in as ${client.user.tag}!`);
  // Setup WebSocket connection after client is ready
  setupWebSocket(client);
});

// Your existing event loading logic

/*
const fs = require('node:fs');
const path = require('node:path');

const eventsPath = path.join(__dirname, 'events');
const eventFiles = fs
  .readdirSync(eventsPath)
  .filter((file) => file.endsWith('.js'));

for (const file of eventFiles) {
  const filePath = path.join(eventsPath, file);
  const event = require(filePath);
  if (event.once) {
    client.once(event.name, (...args) => event.execute(...args, client));
  } else {
    client.on(event.name, (...args) => event.execute(...args, client));
  }
}
*/

client.login(process.env.DISCORD_TOKEN);

Make sure to adjust the path './path/to/your/webSocketHandler' to correctly point to where you saved webSocketHandler.js.

How It Works

  • Initialization: When the bot starts and the client is ready, setupWebSocket(client) is called.

  • WebSocket Connection: The connect() function establishes a persistent WebSocket connection to your webchat server. It includes logic for automatic reconnection if the connection drops.

  • Website to Discord: When your webchat server sends a message over the WebSocket, the ws.on('message') event fires in the bot. The bot parses the message. If it's a new chat message (and not an echo of its own message), it sends it to the designated Discord channel (WEBCHAT_CHANNEL_ID).

  • Discord to Website: When a user sends a message in the WEBCHAT_CHANNEL_ID on Discord, the client.on('messageCreate') event fires. The bot checks if the message is from a human user in the correct channel. It gathers the sender's display name, message content, and their top role's color. This information is packaged into a JSON payload (marked with message_type: "Discord") and sent through the active WebSocket connection to your webchat server. Your webchat server will then need to process this payload and display the message accordingly.

Testing

  1. Configure .env: Ensure all the WEBCHAT_ environment variables are correctly set.
  2. Run your bot: Start your Discord bot. Check the console for "Webchat: Connected to Webchat WebSocket server".
  3. Send a message from your website's webchat: Verify the message appears in the specified Discord channel, formatted as : Message content.
  4. Send a message in the designated Discord channel: Verify the message appears on your website's webchat.

Confirm that the sender's Discord display name and their top role color are used in the webchat display (your website frontend will need to be set up to use this color).

Test Reconnection (Optional but Recommended):

  1. If possible, temporarily stop your webchat WebSocket server.
  2. Observe the bot's console logs. You should see messages about disconnection and reconnection attempts.
  3. Restart your webchat server. The bot should automatically reconnect.

Clone this wiki locally