DEV Community

Cover image for RASA - Socket.IO integration
Petr Janik
Petr Janik

Posted on

RASA - Socket.IO integration

We already saw how to integrate Rasa chatbot with Tiwlio and Google Chat. In this article, I will show you how to integrate the Rasa chatbot with another messaging channel – Socket.IO.

This will be the result:

Web chat with Rasa

What is Socket.IO?

Socket.IO is a library that enables real-time bi-directional communication between a client and a server. It uses the WebSocket protocol.

You can write a simple chat application to learn the basics of Socket.IO.

Socket.IO is composed of two parts:

  • A server that integrates with the Node.JS HTTP Server – socket.io.
  • A client library that loads on the browser side – socket.io-client.

Let's get started!

By adding the following configuration to credentials.yml in your Rasa chatbot project, you will enable the Socket.IO server.

# credentials.yml
socketio:
  user_message_evt: user_uttered
  bot_message_evt: bot_uttered
  session_persistence: false
Enter fullscreen mode Exit fullscreen mode

All that is left is a client.
Create a folder named webchat in the Rasa chatbot project.
Inside the webchat folder, create a new index.html file.
This file will contain the source code for the interactive web chat. You can view the complete code here.

Let's start with a markup.
At the top of the page, there will be a header.
All sent and received messages will be displayed in the middle. At the bottom, we will have a form with text input and a button to send messages.

<!DOCTYPE html>
<html>
  <head>
    <title>Socket.IO chat</title>
    <style>
      /* styles */
    </style>
  </head>
  <body>
    <header class="header">
      <p class="title">Chat with Rasa chatbot</p>
    </header>
    <div id="messages"></div>
    <form id="form">
      <input id="message-input" autocomplete="off" autofocus/>
      <button class="button">Send</button>
    </form>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

At the current stage, the page looks like this and doesn't do anything:
Page markup

To connect to the Socket.IO server provided by Rasa, we need Socket.IO client library. We'll include it as a script from CDN before the closing </body> tag in index.html.

<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.1.3/socket.io.js"
        integrity="sha512-PU5S6BA03fRv1Q5fpwXjg5nlRrgdoguZ74urFInkbABMCENyx5oP3hrDzYMMPh3qdLdknIvrGj3yqZ4JuU7Nag=="
        crossorigin="anonymous" referrerpolicy="no-referrer"></script>
Enter fullscreen mode Exit fullscreen mode

Below this <script> we have just added, add another <script> that will contain the application logic.

Connect to the Rasa Socket.IO server:

<script>
    // Connect to RASA server
    const socket = io("http://localhost:5005");
</script>
Enter fullscreen mode Exit fullscreen mode

The URL has a form of http://<host>:<port>, where the host and port are the appropriate values from your running Rasa server.

When the client connects, a connected event will be emitted. We can react to it and log a message into the console.

socket.on('connect', function () {
    console.log("Connected to Socket.io server");
});
Enter fullscreen mode Exit fullscreen mode

In a similar way, log any connection errors.

socket.on('connect_error', (error) => {
    // Write any connection errors to the console 
    console.error(error);
});
Enter fullscreen mode Exit fullscreen mode

When the user clicks the Send button, we obtain the input value and emit a user_uttered event, passing the input value as a value of a message field.
user_uttered is the event name for user messages we configured in credentials.yml.
We also append the message to the page and add corresponding classes for adding CSS later on.
Lastly, scroll to the bottom of the page.

const messages = document.getElementById('messages');
const form = document.getElementById('form');
const messageInput = document.getElementById('message-input');

form.addEventListener('submit', function (e) {
    e.preventDefault();
    const msg = messageInput.value;
    if (msg) {
        socket.emit('user_uttered', {
            "message": msg,
        });
        messageInput.value = '';

        appendMessage(msg, "sent");
    }
});

function appendMessage(msg, type) {
    const item = document.createElement('div');
    item.textContent = msg;
    item.classList.add("message");
    item.classList.add(`message_${type}`);
    messages.appendChild(item);
    scrollToBottom();
}

function scrollToBottom() {
    window.scrollTo(0, document.body.scrollHeight);
}
Enter fullscreen mode Exit fullscreen mode

Rasa server processes the message and sends a response in the bot_uttered event payload.
bot_uttered is the event name for bot messages we configured in credentials.yml.
The response can contain various fields. We will handle text, attachment and quick_replies.

socket.on('bot_uttered', function (response) {
    console.log("Bot uttered:", response);
    if (response.text) {
        appendMessage(response.text, "received");
    }
    if (response.attachment) {
        appendImage(response.attachment.payload.src, "received");
    }
    if (response.quick_replies) {
        appendQuickReplies(response.quick_replies);
    }
});
Enter fullscreen mode Exit fullscreen mode

If the response contains text, we want to display it in the chat as a message from the chatbot. This is done with the same method we used to append user's message but with a different class and therefore different CSS.

If the response contains attachment, we want to grab the attachment source and display it as an image sent by the chatbot.

function appendImage(src, type) {
    const item = document.createElement('div');
    item.classList.add("message");
    item.classList.add(`message_${type}`);
    const img = document.createElement('img');
    img.src = src;
    img.onload = scrollToBottom;
    item.appendChild(img);
    messages.appendChild(item);
}
Enter fullscreen mode Exit fullscreen mode

If the response contains quick_replies, we want to display them as buttons that the user can click to send a message without a need for typing.

We attach a click event listener to each button to:

  • remove the quick replies,
  • append the title of the quick reply the user clicked on as a message from the user,
  • send the underlying quick reply payload.
function appendQuickReplies(quickReplies) {
    const quickRepliesNode = document.createElement('div');
    quickRepliesNode.classList.add("quick-replies");
    quickReplies.forEach(quickReply => {
        const quickReplyDiv = document.createElement('button');
        quickReplyDiv.innerHTML = quickReply.title;
        quickReplyDiv.classList.add("button");
        quickReplyDiv.addEventListener("click", () => {
            messages.removeChild(quickRepliesNode);
            appendMessage(quickReply.title, "sent");
            socket.emit('user_uttered', {
                "message": quickReply.payload,
            });
        })
        quickRepliesNode.appendChild(quickReplyDiv);
    })
    messages.appendChild(quickRepliesNode);
    scrollToBottom();
}
Enter fullscreen mode Exit fullscreen mode

Styles

Finally, to make the page nicer, let's add some CSS.
First, add a CSS reset. Then add the rest of the styles.

/** CSS RESET        **/
/** ...              **/
/** END OF CSS RESET **/

body {
    --white-color: #f3f4fb;
    --black-color: black;
    --blue-color: #5a18ee;
    --light-purple-color: #7f7afc;
    --light-violet-color: #8c54f4;
    --darker-violet-color: #713dc3;
    --dark-violet-color: #5d2db0;
    font-family: Roboto, sans-serif;
    background-color: var(--blue-color);
}

#form {
    padding: 0.25rem;
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    display: flex;
    height: 3rem;
    box-sizing: border-box;
    background-color: var(--black-color);
}

#message-input {
    flex-grow: 1;
    padding: 0 1rem;
    border-radius: 2rem;
    margin: 0.25rem;
    background-color: var(--white-color);
}

#message-input:focus {
    outline: none;
}

.button {
    background: var(--light-violet-color);
    border: none;
    padding: 0 1rem;
    margin: 0.25rem;
    border-radius: 3px;
    outline: none;
    color: var(--white-color);
    font: inherit;
}

.button:hover {
    background: var(--darker-violet-color);
}

.button:active {
    background: var(--dark-violet-color);
}

#messages {
    display: flex;
    flex-direction: column;
    padding: 10px 10px 60px 10px;
}

.message {
    padding: 0.5rem 1rem;
    border-radius: 10px;
    word-wrap: break-word;
    max-width: calc(100% - 40px - 32px);
    margin-bottom: 10px;
}

.message_received {
    background: var(--white-color);
    color: var(--black-color);
    border-bottom-left-radius: 0;
    align-self: flex-start;
}

.message_sent {
    color: var(--white-color);
    background: var(--light-purple-color);
    border-bottom-right-radius: 0;
    align-self: flex-end;
}

.header {
    background-color: var(--black-color);
    color: var(--white-color);
    text-align: center;
    padding: 12px;
}

.title {
    font-size: 23px;
}

.quick-replies {
    display: flex;
    align-self: flex-end;
    height: 2.5rem;
    box-sizing: border-box;
}
Enter fullscreen mode Exit fullscreen mode

Run the application

Now let's start the application from the webchat folder by running npx http-server ..

Because the rasa server will be running on http://localhost:5005 and our chat application is running on http://localhost:8080, we need to enable CORS for the rasa server.

  1. Run the rasa server with enabled cors for all origins: rasa run --cors "*". Alternatively, enable only a specific host and port: rasa run --cors "http://127.0.0.1:8080".
  2. In a new terminal window, run the actions server: rasa run actions.
  3. Open http://127.0.0.1:8080 and chat with the chatbot!

Web chat with Rasa

The web chat we created does not support all Rasa functionality and it could be improved in many ways. If you want to add Rasa chat to your webpage, consider rasa-webchat which provides more features.

To learn more about Socket.IO channel connector, visit the Rasa documentation.

Repository for this tutorial:

You can checkout the state of the repository at the end of this tutorial by running:

git clone --branch 22-socketio-integration git@github.com:petr7555/rasa-dev-tutorial.git
Enter fullscreen mode Exit fullscreen mode

Discussion (21)

Collapse
socr102 profile image
Elina

Great
I am very interesting in your every post
Have you ever deploied the Rasa Chatbot into the facebook
I have already the runned the Chat Bot in the sercer using this:

rasa run -m models --enable-api --cors "*" --credentials credentials.yml --endpoints endpoints.yml

rasa run actions

and then I have tried to set up the webhook in the developers.facebook.com

But I can't set up webhook

Do you know how to set up webhook without using the ngrok?

thank you
best
Elina

Collapse
petr7555 profile image
Petr Janik Author • Edited

Hi Elina.
I am glad to hear you like my article.

I was able to deploy the Rasa chatbot to Facebook by following the instructions in the Rasa documentation.

In the first terminal, run: rasa run.
In the second terminal: rasa run actions.
In the third terminal: ngrok http 5005, where 5005 is the port where the rasa server is running (first terminal).

The ngrok output will contain something like: Forwarding https://48cc-185-219-166-190.ngrok.io -> http://localhost:5005.
Copy the https address and add a Webhook Callback URL https://48cc-185-219-166-190.ngrok.io/webhooks/facebook/webhook. The 48cc-185-219-166-190.ngrok.io part will be different for you.

Lastly, do not forget to add messages and messaging_postback subscriptions as shown in the screenshot below.
Facebook Webhooks setup

I hope this helps! If you encounter any issues, let me know.

Collapse
socr102 profile image
Elina

Thank you for your help
I solved it by using the ngrok
I tried the X.X.X.X/webhooks/facebook/webhook as webhook callback url
But in the facbook,Ip address is not recognized

This is why we use the ngrok
Your help was avaliable
ngrok http 5005
Using the ngrok, we can convert localhost:/5005 into the another address
And by using this address, we can get this problem
Thank you for your attention and knidly and friendly help
I hope you are doing well
Also I will wait for your next and next and next post ....
💖💋

Thread Thread
petr7555 profile image
Petr Janik Author

You're welcome. I am glad I could help.

Thread Thread
socr102 profile image
Elina

hello friend
I have a question about the Rasa Bot.
I want to use an IFrame when deploying my Rasa Bot to Facebook.
when click on the URL. Go to a new Chrome tab.
How to solve it?
I am asking for help individually
thank you
best

Thread Thread
petr7555 profile image
Petr Janik Author • Edited

Hi Elina,
I am sorry, I do not understand what your use case is.
Are you trying to embed Facebook page in an iframe or do you want the chatbot to send an iframe instead of a text?

Thread Thread
socr102 profile image
Elina

In my facebook messenger, there are things like this:

dev-to-uploads.s3.amazonaws.com/up...

when I click it:
dev-to-uploads.s3.amazonaws.com/up...

I want make the multiwebform as ifrme in the facebook messenger

that's all

thank you
best

Thread Thread
petr7555 profile image
Petr Janik Author • Edited

You can use Messenger Webview.
It can be opened either by clicking on a button (see URL button) or by clicking on a template (see Generic Template).
The Webview can be either compact, tall or full. This has effect only on mobile. On desktop, the website will always open in a new tab.

Example:

responses:
  utter_greet:
    - text: ""
      elements:
        - title: Welcome!
          image_url: https://avatars.githubusercontent.com/u/21214473?s=200&v=4
          subtitle: Rasa is an open source machine learning framework.
          default_action:
            type: web_url
            url: https://github.com/RasaHQ/rasa
            webview_height_ratio: tall
          buttons:
            - title: Compact
              type: web_url
              url: https://github.com/RasaHQ/rasa
              webview_height_ratio: compact           
            - title: Tall
              type: web_url
              url: https://github.com/RasaHQ/rasa
              webview_height_ratio: tall            
            - title: Full
              type: web_url
              url: https://github.com/RasaHQ/rasa
              webview_height_ratio: full
Enter fullscreen mode Exit fullscreen mode

Which results in:
Facebook template

After clicking on Tall button on mobile:
Tall Webview

I wish you a lot of luck with your project, Elina! 🍀

Thread Thread
socr102 profile image
Elina

Thank you for your help
and you mean that it appears in the new tab in case of desktop?

Thread Thread
petr7555 profile image
Petr Janik Author

Yes, in a new tab.

Thread Thread
socr102 profile image
Elina

But I saw the iframe in the desktop when I used the facebook messenger

Thread Thread
petr7555 profile image
Petr Janik Author

I could only find websites mentioning that you can embed Facebook Messenger into your website, not vice versa – add website as iframe into Facebook Messenger.

Thread Thread
socr102 profile image
Elina
Thread Thread
petr7555 profile image
Petr Janik Author

I have tried clicking the button in a Messenger application (i.e. facebook.com/messages/), but it still opens in a new tab.

Unfortunately, I do not how to achive something similar to the last picture you sent.

Thread Thread
socr102 profile image
Elina

I solved it
thank you for your kindness
I think we can work in rasa project
what do you think about this
thank you
best

Thread Thread
petr7555 profile image
Petr Janik Author

That's great! Would you mind sharing a bit about how you solved it?

Thread Thread
socr102 profile image
Elina

Could you send me your mail ?
I can contact using that
thank you

Thread Thread
petr7555 profile image
Petr Janik Author

I think writing it here in the comments could benefit others who are reading this thread. Is it fine with you to send your solution publicly?

Thread Thread
socr102 profile image
Elina

I want to know about you a lot

ok I will publish it as soon as my project is finished

It needs some time to pulish the method

Collapse
emmajadew profile image
Emma Jade Wightman

incredible work <3

Collapse
petr7555 profile image
Petr Janik Author • Edited

Thank you, Emma 😊