This is a shorter version of the setup. It is more straight forward with no testing process 
in the beginning. The testing isn't necessarily needed but may assist you in figuring out
where something is going wrong in the process.

1) add websocket_server.cpp, websocket.h, PyMUDDiscoServer.py, and PyMUDDiscoServer.log to your src folder of your MUD
    make sure to update any relevant path references like:
            result = std::system("python3 /path/to/your/src/PyMUDDiscoServer.py &");
            
2) in channels.cpp make sure to include
    #include <iostream>
    #include <string>
    #include "websocket.h"

3) Modify your functions to send to discord as you see fit, here is what I did:
in channels.cpp add to send_tochannel at the beginning:
   const char *he_she[] = { "it", "he", "she", "it" };
   const char *him_her[] = { "it", "him", "her", "it" };
   const char *his_her[] = { "its", "his", "her", "its" };
   
replace the if(social) section below bool emote = false; with
   bool emote = false;
   if( word[0] == ',' ) {
      emote = true;
      argument = argument.substr( 1, argument.length(  ) );
   }

   std::string discord_message; // Declare a variable to hold the message to send to Discord

   if( social ) 
   {
      // Manually expand tokens for the social case
      std::string expanded_message = socbuf_other;
         
      // Replace tokens in the expanded message manually
      size_t pos;
      while ((pos = expanded_message.find("$n")) != std::string::npos) {
         expanded_message.replace(pos, 2, ch->name);
      }
      while ((pos = expanded_message.find("$N")) != std::string::npos && victim) {
         expanded_message.replace(pos, 2, victim->name);
      }
      while ((pos = expanded_message.find("$e")) != std::string::npos) {
         expanded_message.replace(pos, 2, he_she[URANGE(0, ch->sex, SEX_MAX - 1)]);
      }
      while ((pos = expanded_message.find("$E")) != std::string::npos && victim) {
         expanded_message.replace(pos, 2, he_she[URANGE(0, victim->sex, SEX_MAX - 1)]);
      }
      while ((pos = expanded_message.find("$m")) != std::string::npos) {
         expanded_message.replace(pos, 2, him_her[URANGE(0, ch->sex, SEX_MAX - 1)]);
      }
      while ((pos = expanded_message.find("$M")) != std::string::npos && victim) {
         expanded_message.replace(pos, 2, him_her[URANGE(0, victim->sex, SEX_MAX - 1)]);
      }
      while ((pos = expanded_message.find("$s")) != std::string::npos) {
         expanded_message.replace(pos, 2, his_her[URANGE(0, ch->sex, SEX_MAX - 1)]);
      }
      while ((pos = expanded_message.find("$S")) != std::string::npos && victim) {
         expanded_message.replace(pos, 2, his_her[URANGE(0, victim->sex, SEX_MAX - 1)]);
      }

      // Construct the Discord message
      discord_message = "[" + capitalize(channel->name) + "] " + expanded_message;

      // Handle the social output for the player
      act_printf(AT_PLAIN, ch, nullptr, victim, TO_CHAR, "&W[&[%s]%s&W] &[%s]%s",
               channel->colorname.c_str(), capitalize(channel->name).c_str(),
               channel->colorname.c_str(), socbuf_char.c_str());
   }
   else if( emote ) {
      ch->printf( "&W[&[%s]%s&W] &[%s]%s %s\r\n",
                  channel->colorname.c_str(  ), capitalize( channel->name ).c_str(  ), 
                  channel->colorname.c_str(  ), ch->name, argument.c_str(  ) );
      // Construct the message for Discord (emote)
      discord_message = "[" + capitalize(channel->name) + "] " + ch->name + " " + argument;
   }
   else {
      if( ch->has_pcflag( PCFLAG_WIZINVIS ) ) {
         ch->printf( "&[%s](%d) You %s '%s'\r\n", channel->colorname.c_str(  ), 
                     (!ch->isnpc() ? ch->pcdata->wizinvis : ch->mobinvis), 
                     channel->name.c_str(  ), argument.c_str(  ) );
         // Construct the message for Discord (invisible player)
         discord_message = "[" + capitalize(channel->name) + "] " + "(Invis) " + ch->name + ": '" + argument + "'";
      } else {
         ch->printf( "&W[&[%s]%s&W] %s: &w'&[%s]%s&d'\r\n", 
                     channel->colorname.c_str(  ), capitalize(channel->name).c_str(  ), 
                     ch->name, channel->colorname.c_str(  ), argument.c_str(  ) );
         // Construct the message for Discord (regular message)
         discord_message = "[" + capitalize(channel->name) + "] " + ch->name + ": '" + argument + "'";
      }
   }

   // Check if the channel is one of those we want to send to Discord
   if (channel && !discord_message.empty()) {
      if (channel->name == "chat") {
         send_to_discord("CHAT|" + discord_message);  // Prefix the message with "CHAT"
      } else if (channel->name == "blah") {
         send_to_discord("BLAH|" + discord_message);  // Prefix the message with "INFO"
      }
   }
   
4) Add the function broadcast_to_channel to channels.cpp - will need to modify to construct printf to your MUD
    /* Function to handle broadcasting to a channel like to_channel but not restricted to log channels */
    void broadcast_to_channel(mud_channel *channel, const std::string &nickname, const std::string &message) {
        if (!channel) {
            std::cerr << "Error: Null channel passed to broadcast_to_channel" << std::endl;
            return;
        }

        for (auto *d : dlist) {
            char_data *vch = d->original ? d->original : d->character;

            if (!vch || d->connected != CON_PLAYING)
                continue;

            if (vch->level >= channel->level && hasname(vch->pcdata->chan_listen, channel->name)) {
                // Use the vch->printf to format and send the message
                // All channels are the same format for now in AFKMud
                // Adds a * before the name to signify it is from Discord
                vch->printf(
                    "&W[&[%s]%s&W] *%s: &w'&[%s]%s&d'\r\n",
                    channel->colorname.c_str(),
                    capitalize(channel->name).c_str(),
                    nickname.c_str(),
                    channel->colorname.c_str(),
                    message.c_str()
                );
            }
        }
    }

5) in channels.h
    #include <string>
    #include <bitset>
    #include <list>

    using namespace std;

and add in channels.h
    void broadcast_to_channel(mud_channel *channel, const std::string &nickname, const std::string &message);
    
6) The area you put your start and shutdown websocket functions can be a bit sensitive, so if your MUD crashes
pay attention to where this is happening and try a different solution.

make sure to #include "websocket.h" in all locations you use start or shutdown web socket
in db.cpp add this near the bottom
   log_string( "Starting web socket");
   start_websocket_server();
   
in hotboot.cpp add this before log_string( "executing hotboot....");
   log_string( "stopping websocket server" );
   shutdown_websocket_server();
   
7) add entry into makefile for websocket.cpp

8) in startup file add this to initiate the python script and make sure to change the path
    # Kill any existing PyMUDDiscoServer.py process
    pkill -f PyMUDDiscoServer.py

    # Start the Python WebSocket listener
    echo "Starting Python WebSocket listener..."
    #Make sure you change this path
    python3 python3 /path/to/your/src/PyMUDDiscoServer.py &
    
9) Refer to the README_discord_bot for how to set up a discord bot that has permissions to
communicate with your websocket. I suggest putting the channel in slow mode so people
cannot spam the mud from discord. This should not prevent the bot from sending as many
messages as it needs as long as it has manage message permissions.

10) make/make clean and hotboot (might be worth a shutdown and restart)

Extra:
This function can be useful to strip color codes out of a string before you
send them to discord.

    // Strip out color codes from the string
    std::string strip_color_codes(const std::string& src) {
        std::string result;
        size_t i = 0;

        while (i < src.length()) {
            switch (src[i]) {
                case '&':  // Handle normal color codes
                case '{':  // Handle background color codes
                case '}':  // Handle blink/extended color codes
                    // Ensure we skip the current character and the next one
                    i += 2;
                    break;

                default:  // Regular character, add it to the result
                    result += src[i];
                    ++i;
                    break;
            }
        }

        return result;
    }
    
examples: 
1)

in my addchanges file i added something like:

    std::string changebuf = changes_table[maxChanges-1].change;
    to_channel(changebuf, "slayersilent", 1);
    
and added in channels.cpp to_channel (which is for log or info type channels)
you can check for multiple channels this way if you want to use a public
channel for one and a private channel that would only post to discord

    if (channel && (channel->name == "SLAYER" || channel->name == "slayersilent")) {
      std::string discord_message = "[SLAYER] " + argument;
      std::string cleaned_message = strip_color_codes(discord_message); // Strip color codes
      send_to_discord("SLAYER|" + cleaned_message);  // Prefix the message with "SLAYER"
   }
   
2)

One way to check for multiple channels
   if (channel && !discord_message.empty()) {
      if (channel->name == "chat") {
         send_to_discord("CHAT|" + discord_message);  // Prefix the message with "CHAT"
      } else if (channel->name == "blah") {
         send_to_discord("BLAH|" + discord_message);  // Prefix the message with "INFO"
      }
   }