It’s Alive! A Chatbot Behind the Scenes

Cody the Chatbot

Chatbots are a thing nowadays and most probably they are here to stay, at least for some time. Like with any interactive toy, creating a lively and engaging bot is always a complicated issue, both from the UX and the development point of view. The challenges of designing and writing a coherent script for Cody the Chatbot have been covered by its creators, The Rectangles, here and here. This article will focus on the development part, which has been no less demanding than the creative aspect.

Chop-Chop decided to design and release their own chatbot featuring Cody the mascot both as a tool for the visitors and potential customers and as a creative and technical challenge. Although Cody the Chatbot is a scripted tool, not an AI-driven chatbot that spontaneously reacts to what the visitors say, creating an attractive and meaningful user experience was nonetheless a complex undertaking.

When we started working on the chatbot, we researched the available engines, but none of them really met our needs. Thus, we decided to create our own engine and a simple API from scratch using Laravel, ES6, React.js, Elixir and Gulp.

Moreover, we used the following libraries:

At first glance, coding a bot may seem fairly easy — from the end users’ point of view it all boils down to making the bot react to the answers selected by the visitor. There is more to it, however, than meets the eye — the whole magic is happening in the backend. What were the challenges, then, that we faced and how did we handle them? There was a couple of main issues that we had to address while bringing Cody to life:

1. Editing the script

The whole chatbot’s script is divided into meaningful pieces of conversation called nodes. Each node has a unique ID so that the users can be redirected to a particular node at any point of the conversation.

User dashboard

One of the requirements was storing the conversations’ transcripts. We could either store in the database the whole conversation or only the IDs of the nodes. We went for the second option, but we had to make it so that when a node is edited, its clone is made for the nodes’ values saved in the transcripts from the past not to change. As a result, the database stores all script versions so we can preview any script or conversation from the past.

And of course, when the script s edited in the backend, the changes are not immediately visible to the users. They are saved as draft on the go and you can preview and test your new nodes until you are happy with them. Then you click “Publish” button and voilà — the new script has just been launched.

2. Parsing the messages

There is more to Cody the Chatbot than a simple “click and reaction” script. Cody is able to diversify his messages in a couple of ways:

  • selecting a random response from a predefined set
  • interpolating the variables — for example, if a user has revealed their name, Cody is able to refer to them by it
  • checking the conditions — if a user has talked to Cody before, they will receive different answers than a new guest. Or, for example, if a visitor reveals that they are a designer, they will be redirected to different nodes than e.g. a web developer.
Conditional reponses

To handle these features, we needed a backend parser to interpolate the variables and check the conditions. Generally speaking, the parser looks like this:

foreach ($lines as &$line) {
$line = $this->checkConditions($line);
$line = $this->interpolateVariables($line);

When checking a condition, we can either check whether a given variable ({?variable}) exists (e.g. if a user has revealed their name) or compare a variable and a value: {?variable=value}

Here’s how we check the conditions:

preg_match_all(‘/{\?(.+?)}/’, $text, $matches, PREG_SET_ORDER);
$parts = preg_split(‘/{\?(.+?)}/’, $text);
foreach ($matches as $index => $match) {
$condition = $match[1];
if (1 === preg_match(‘/=/’, $condition)) {
$conditionParts = preg_split(‘/=/’, $condition);
$value = $this->getVariable($conditionParts[0]);
if ($conditionParts[1] === $value) {
return $parts[$index + 1];
} else {
$value = $this->getVariable($match[1]);
if ($value !== null && $value !== ‘false’ && $value !== ‘empty’) {
return $parts[$index + 1];
return $parts[0];

And this is how to interpolate the variables:

preg_match_all(‘/{\$(.+?)}/’, $text, $matches, PREG_SET_ORDER);
foreach ($matches as $match) {
$value = $this->getVariable($match[1]);
$text = preg_replace(
‘/{\$’ . $match[1] . ‘}/’,
return $text;

3. Good user experience

Although Cody is not an AI-powered tool that learns and dynamically reacts to what the users say, we nevertheless wanted to give the visitors a smooth and lifelike experience. We implemented facial expressions, variable writing speed and cookie support so that Cody remembers the user and their session progress.

The status of the conversation is stored in the browser session, so that the data is kept server-side. With any API request the engine checks the last node the user reached and selects the next node on this basis. Here’s what it looks like in the code (note that in the snippet below we omitted the code that checks if the items pulled from the database are not null and that we replaced the real life queries to the database with nonexistent functions like getAction):

$action = $this->getUserAction($request);
$condition = [ ‘type’ => $action[‘type’] ];
if (‘button’ === $action[‘type’]) {
$condition[‘value’] = $action[‘value’];
$chosenAction = getAction($condition);
if ($chosenAction->isEmpty()) {
return false;
$nextNode = getNextNode($chosenAction);
return $nextNode->last();

Moreover, each node can link (redirect) to another node, so that it’s possible to combine nodes and send them together. So when we already know which node should go next, we have to check whether it has any redirects. We do it recursively:

$currentNode = [ $node ];
// Check if Node has redirects.
$redirect = $node->getRedirects();
if ($redirect->isEmpty()) {
return $currentNode;
// Follow the redirect.
$targetNode = findNode($redirect->target);
return array_merge(
    // This is a recursive call.

4. Marketing usability

Since the chatbot is basically a marketing tool, we had to think of clever ways of storing and managing the conversations transcripts. We wanted to know which answers are selected more frequently than others and which links suggested by Cody the Bot are clicked by the users. At first we considered using Google Analytics to store the data, but having taken into account its restrictions and the possibility of GA meddling with the results we decided against it. Eventually we decided to store the data in a database on our server and to add the option to export the numeric results as CSV.

5. User-friendly dashboard

This was perhaps the most important lesson that we learned in the chatbot development process. Coding the dashboard is one thing, but making it usable for the client is a whole different story. It has turned out that what is obvious for technical guys may not be clear at all for end users. Think, for example, errors handling: there’s nothing easier for a developer to open the console and check what’s going on, but an end user needs a neat error message, preferably with a suggestion how to solve the issue in question. The same applies, for instance, to adding new nodes and creating/editing a script: end users prefer clear visual interfaces where they can see the connections and dependencies between the elements.

6. Reusable engine

The whole engine behind Cody has been designed and coded to make it easily reusable in other chatbot projects. All that needs to happen for the engine to serve a new purpose is importing new visual assets and setting up the dialogue scenario. The engine is already there and can be adjusted to any specific needs and ideas.

All in all, creating a coherent and usable chatbot experience has been challenging both for the design and the development team. We all had to cooperate closely (with the marketing department chiming in from time to time) to make Cody just as we imagined — and it can be safely said that it was worth the effort. Don’t take my word for it: Meet Cody!