Restaurant Bot

Agnieszka Dudziak
Agnieszka Dudziak
8 min read
updated: Jan 18, 2024

Support your restaurant communication with a customizable chatbot template.

What can the template do?Link icon

  • Show your restaurant’s menu

  • Collect orders and summarize them

  • Manage table reservations and the booking process

  • Collect clients’ contact information

  • Pass orders automatically to your backend using webhooks

  • Answer frequent questions and show contact details

To start the order process, users must select the Restaurant Menu option from the menu. The interactive gallery shows a preview of the next steps with short descriptions. Users can decide if they want to start by ordering appetizers, first and main courses, or desserts.

When the order is complete, the chatbot shows the summary that must be confirmed. Here you can add the Question action to collect user data.

The template with the relevant code can be easily customized to your needs.

Features includedLink icon

To make the Restaurant Bot template work, we’ve used a few great ChatBot features.

PostbackLink icon

By default, when a button is clicked, the bot receives its title. Postback allows you to pass a hidden message when a user clicks the button.

For example, the user clicks +Order and the bot knows that Avocado paste was selected thanks to the Postback value assigned to the button.

EntitiesLink icon

The moment you import the template to your dashboard, all the necessary entities are added to your account.

The bot knows the restaurant’s menu thanks to the productName entity with our products added. This user entity helps your bot validate the user query and saves it to the custom attribute under the same name.

The collected attributes are passed to your backend in the webhook’s payload.

System entities such as Any, Number, and Email help you efficiently collect users’ data. For example, the Number entity validates responses saved to the custom attribute productQuantity.

Other entities available in the template are foodType and tableNumber.

FiltersLink icon

Another handy feature that can save you time is filters. Filters add rules to bot actions and responses that decide under what conditions they can be triggered. Instead of adding many interactions, you can have one that routes the chats based on users’ decisions.

WebhooksLink icon

Here is where the magic happens, and the order is handed to the backend. Follow the steps below to set up your webhook and replace the one in the template when you’re ready.

How the backend works?Link icon

To add a product to your order, display the current status, and start the process again, we’ve prepared a simple backend. See the full code and modify it according to your needs.

This example was written in JavaScript and requires Node.js, but you can rewrite this code to any other server-side language you want.
This example was written in JavaScript and requires Node.js, but you can rewrite this code to any other server-side language you want.

Technologies usedLink icon

  • Express.js - to simplify and clarify parsing the ongoing webhook request.

  • Firebase Functions - to deploy our code to a public HTTPS server.

  • Firebase Realtime Database - to store the order based on the user session.

We’ve split our backend functionality into three different parts:

  • Add product - to accept orders.

  • Order summary - to show the order summary.

  • Start again - to remove the order and start the order process again.

Let’s go through our webhook code.

In the opening part, you load all the required packages.
'use strict';

const TOKEN = '{{YOUR_TOKEN}}'; // put your authorization token here
const FIRESTORE_COLLECTION = 'COLLECTION_NAME'; // put your firestore collection name (https://firebase.google.com/docs/firestore)

const express = require('express');
const { Router } = require('express');
const router = new Router();
const app = express();
const functions = require('firebase-functions');
const firebase = require('firebase-admin');

firebase.initializeApp({
    credential: firebase.credential.applicationDefault()
});

// connect to firestore
const db = admin.firestore().collection(FIRESTORE_COLLECTION);

Then you will see the transformOrderToText function, which transforms the order into a text message.

Function for transform the order to a text message
// docs for the text fullfillment is described here: https://www.chatbot.com/docs/object-definitions
const transformOrderToText = (responses = []) => {
    let text = '';

    if (!responses.length) {
        return 'Your order is empty.';
    }

    responses.map(item => {
        text += `${item.productQuantity}x ${item.productName}\n`;
    });

    return text;
}

Ok! Now is the part to handle the ChatBot webhook verification request. In short, it’s a bilateral verification of the webhook.

GET for authorization request.
router
    .route('/')
    .get((req, res) => {
        if (req.query.token !== TOKEN) {
            return res.sendStatus(401);
        }

        return res.end(req.query.challenge);
    });

The next part is the most exciting - handling incoming requests from the ongoing chat.

Go with request to the proper route based on the action provided in the interaction.
router
    .route('/')
    .post((req, res, next) => {
        if (req.query.token !== TOKEN) {
            return res.sendStatus(401);
        }

        req.version = req.body.result ? 1 : 2;

        const action = req.version === 1 ?
            req.body.result.interaction.action : req.body.attributes.action;

        if (['add-product', 'order-summary', 'start-again'].includes(action)) {
            req.url = `/${action}`;
            return next();
        }

        res.json();
    });

For the readability of our code, we direct our incoming request to the appropriate part of the code. We used an action that can be defined in the interaction. Thanks to that, we split our bot functionalities into three dedicated actions: add-product, order-summary, and start-again.

When our backend receives a request, we check if the action we defined earlier in the interaction is the one we expect - if so, we direct our request to the appropriate part of our code.

  1. Add product

Add the product to the order.
router
    .route('/add-product')
    .post((req, res, next) => {
        const sessionParameters = req.version === 1 ?
            req.body.result.sessionParameters : req.body.attributes;

        // get attributes collected in the ongoing chat
        const productName = sessionParameters.productName;
        const productQuantity = Number(sessionParameters.productQuantity);

        // make a product object based on the collected attributes
        if (productName && productQuantity) {
            req.product = { productName, productQuantity };

            // go to the next part of request
            return next();
        }

        // return empty response
        return res.json();
    })
    .post(async (req, res, next) => {
        let order = [];

        // find sessionId
        const sessionId = req.version === 1 ? req.body.sessionId : req.body.chatId;
        const product = req.product;

        // find a document in the firestore db
        const doc = db.doc(sessionId);
        const products = await doc.get();
        const data = { products: [] };

        if (products.data()) {
            data.products = products.data().products;
        }

        // find product in data from db
        const findProductIndex = data.products.findIndex(item => item.productName === product.productName);

        if (findProductIndex > -1) {
            data.products[findProductIndex].productQuantity += product.productQuantity;
        } else {
            data.products.push(product);
        }

        // update document
        await doc.set(data);
        order = data.products;

        if (order.length) {
            req.order = order;
            return next();
        }

        return res.json();
    })
    .post((req, res) => {
        let responses = [];

        if (req.version == 2) {
            responses = [
                {
                    type: 'text',
                    message: 'Product has been added successfully. Your order summary:'
                },
                {
                    type: 'text',
                    message: transformOrderToText(req.order)
                }
            ];
        } else {
            responses = [
                {
                    type: 'text',
                    elements: ['Product has been added successfully. Your order summary:']
                },
                {
                    type: 'text',
                    elements: [transformOrderToText(req.order)]
                }
            ];
        }

        // return responses back to the ChatBot
        res.json({ responses });
    });

This part is used to add a product to an order, which will be stored in the Firebase Realtime Database.

At the start, you save attributes collected in the chatbot to the productName and productQuantity variables. If you collect them, you create an object that stores a single product of the order.

After that, you open the transaction to your database and store the order using sessionID, which you receive with the incoming request.

By using the sessionID sent with the request, you ensure that when the webhook is used in the same chat again, the sessionID stays the same and the chat continues.
By using the sessionID sent with the request, you ensure that when the webhook is used in the same chat again, the sessionID stays the same and the chat continues.

If you manage to open the transaction to the database, the system checks if the order already contains anything:

  • if not, you need to create an empty order and add the first product;

  • if so, you’ve already ordered something, and you need to either increment the product quantity or add the product separately.

Now, you need to return the current status of the order and save it back to the database.
Return an array of responses back to the bot. Check what other responses you can return from the webhook.

  1. Order summary

Return order back to the ChatBot.
router
    .route('/order-summary')
    .post(async (req, res) => {
        const sessionId = req.version === 1 ? req.body.sessionId : req.body.chatId;
        const doc = db.doc(sessionId);
        const products = await doc.get();

        // get order
        let order = products.data().products || [];
        let responses = [];

        if (req.version == 2) {
            responses = [
                {
                    type: 'text',
                    message: 'Your order summary:'
                },
                {
                    type: 'text',
                    message: transformOrderToText(order)
                }
            ];
        } else {
            responses = [
                {
                    type: 'text',
                    elements: ['Your order summary:']
                },
                {
                    type: 'text',
                    elements: [transformOrderToText(order)]
                }
            ];
        }

        res.json({ responses });
    });

This action is similar to the end part of our previous function. At the start, you open the transaction to the database, collect the order, and then return the right answer to the bot.

  1. Start again

Clear the order.
router
    .route('/start-again')
    .post(async (req, res) => {
        // get the sessionId
        const sessionId = req.version === 1 ? req.body.sessionId : req.body.chatId;

        try {
            db.doc(sessionId).delete();
        } catch (e) {
            next(e);
        }

        res.json();
    });

Here you only open the connection to your database, return an empty array to save it to your database, and clear the current order.

The final part of the code is to run the application.
app.use(router);

exports.restaurantBot = functions.https.onRequest((req, res) => {
    if (!req.path) {
        req.url = `/${req.url}`;
    }

    return app(req, res);
});
The backend is fully working on our side right after you import this template into your account. You can continue using it and customize the menu with your needs, or use the code we shared to set up your backend.
The backend is fully working on our side right after you import this template into your account. You can continue using it and customize the menu with your needs, or use the code we shared to set up your backend.

Import templateLink icon

Start using the Restaurant Bot template now to automate taking orders and making reservations.

Read more:

Was this article helpful?

Got it!

Thanks for your feedback.

Thank you!

We’re happy to help.

Start a free ChatBot trial
and build your first chatbot today!

Free 14-day trial No credit card required

Discover our text| products