Search code examples
javascriptphphtmlchatbotchatscript

Chatscript problem with creating pop-up chat window


I have created a bot using chatscript. The goal is to have it as helper in a website(the user will ask questions regarding the website and the bot will answer/maybe redirect him to the appropriate part of the website).

The bot responds fine using the provided .exe in the binaries folder and even when using the .php files in WEBINTERFACE\BETTER folder.The problem arises when trying to use it in a popup chat window,the said window is based on the one seen here (https://www.w3schools.com/howto/howto_js_popup_chat.asp).

When I try to run the page the bot is either unresponsive or produces the following error:

Fatal error: Uncaught TypeError: extract(): Argument #1 ($array) must be of type array, null given in C:\xampp\htdocs\chatscript\WEBINTERFACE\htmltest\ui2.php:38 Stack trace: #0 C:\xampp\htdocs\chatscript\WEBINTERFACE\htmltest\ui2.php(38): extract(NULL) #1 {main} thrown in C:\xampp\htdocs\chatscript\WEBINTERFACE\htmltest\ui2.php on line 38

the code of ui2.php(is the same as the original ui.php file of chat script) :

$host = "localhost";//  <<<<<<<<<<<<<<<<< YOUR CHATSCRIPT SERVER IP ADDRESS OR HOST-NAME GOES HERE
$port = 1024;          // <<<<<<< your port number if different from 1024
$bot  = "";       // <<<<<<< desired botname, or "" for default bot
//=========================

// Please do not change anything below this line.
$null = "\x00";
$postVars = filter_input_array(INPUT_POST, FILTER_SANITIZE_STRING);
extract($postVars);

if (isset($send))
{
    // open client connection to TCP server
    $userip = $_SERVER['REMOTE_ADDR']; // get actual ip address of user as his id. 
    // To check for proxies you can replace the line above with the code below, but it is easily faked so it's insecure:
    // $userip = isset($_SERVER['X_FORWARDED_FOR']) ? $_SERVER['X_FORWARDED_FOR'] : $_SERVER['REMOTE_ADDR'];
     
     
    $msg = $userip.$null.$bot.$null.$message.$null;

    // fifth parameter in fsockopen is timeout in seconds
    if(!$fp=fsockopen($host,$port,$errstr,$errno,300))
    {
        trigger_error('Error opening socket',E_USER_ERROR);
    }

    // write message to socket server
    $ret = '';
    fputs($fp,$msg);
    while (!feof($fp))
    {
        $ret .= fgets($fp, 512);
    }

    // close socket connection
    fclose($fp);
    exit($ret);}  

also my index2.php (where the chat window is located):

<!DOCTYPE HTML>
<html>
  <head>
    <title>
      CHATSCRIPT SERVER
    </title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <style type="text/css">
      body {font-family: Arial, Helvetica, sans-serif;}
* {box-sizing: border-box;}

/* Button used to open the chat form - fixed at the bottom of the page */
.open-button {
  background-color: #555;
  color: white;
  padding: 16px 20px;
  border: none;
  cursor: pointer;
  opacity: 0.8;
  position: fixed;
  bottom: 23px;
  right: 28px;
  width: 280px;
}

/* The popup chat - hidden by default */
.chat-popup {
  display: none;
  position: fixed;
  bottom: 0;
  right: 15px;
  border: 3px solid #f1f1f1;
  z-index: 9;
}

/* Add styles to the form container */
.form-container {
  max-width: 300px;
  padding: 10px;
  background-color: white;
}

/* Full-width textarea */
.form-container textarea {
  width: 100%;
  padding: 15px;
  margin: 5px 0 22px 0;
  border: none;
  background: #f1f1f1;
  resize: none;
  min-height: 30px;
}

#responseHolder {
        min-width: 10%;
        min-height: 10%;
        width: 80%;
        height: 300px;
        overflow: auto;
        margin: 10px auto;
        background-color: #f1f1f1;
      }



/* When the textarea gets focus, do something */
.form-container textarea:focus {
  background-color: #ddd;
  outline: none;
}

/* Set a style for the submit/send button */
.form-container .btn {
  background-color: #4CAF50;
  color: white;
  padding: 16px 20px;
  border: none;
  cursor: pointer;
  width: 100%;
  margin-bottom:10px;
  opacity: 0.8;
}

/* Add a red background color to the cancel button */
.form-container .cancel {
  background-color: red;
}

/* Add some hover effects to buttons */
.form-container .btn:hover, .open-button:hover {
  opacity: 1;
}

    </style>
  </head>
  <body>
  
 
   <button class="open-button" onclick="openForm()">Chat</button>
   <!--<div class="chat-popup" id="myForm" action="#">-->
   <input type="hidden" id="txtUser" name="user" size="20" value="You" />
   <div class="chat-popup" id="myForm2" action="#">
   <div id="responseHolder"></div>
    <form id="myForm" class="form-container" action="#">
      <label for="message"><b>Message</b></label>
      <textarea type="text" placeholder="Type message.." name="message"  id="txtMessage" required></textarea>
      <button type="submit" class="btn" name="send">Send</button>
      <button type="button" class="btn cancel" onclick="closeForm()">Close</button>
    </form>
   </div>





<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script type="text/javascript">
var botName = 'Testy McTesty';      // change this to your bot name

// declare timer variables
var alarm = null;
var callback = null;
var loopback = null;

$(function(){
    $('#myForm').submit(function(e){
    // this function overrides the form's submit() method, allowing us to use AJAX calls to communicate with the ChatScript server
    e.preventDefault();  // Prevent the default submit() method
    var name = $('#txtUser').val();
    if (name == '') {
        alert('Please provide your name.');
        document.getElementById('txtUser').focus();
    }
    var chatLog = $('#responseHolder').html();
    var youSaid = '<strong>' + name + ':</strong> ' + $('#txtMessage').val() + "<br>\n";
    update(youSaid);
    var data = $(this).serialize();
    sendMessage(data);
    $('#txtMessage').val('').focus();
    });

    // any user typing cancels loopback or callback for this round 
    $('#txtMessage').keypress(function(){
          window.clearInterval(loopback);
          window.clearTimeout(callback);
        });
});


function sendMessage(data){ //Sends inputs to the ChatScript server, and returns the response-  data - a JSON string of input information
$.ajax({
    url: 'ui2.php',
    dataType: 'text',
    data: data,
    type: 'post',
    success: function(response){
        processResponse(parseCommands(response));
    },
    error: function(xhr, status, error){
        alert('oops? Status = ' + status + ', error message = ' + error + "\nResponse = " + xhr.responseText);
    }
  });
}

function parseCommands(response){ // Response is data from CS server. This processes OOB commands sent from the CS server returning the remaining response w/o oob commands

    var len  = response.length;
    var i = -1;
    while (++i < len )
    {
        if (response.charAt(i) == ' ' || response.charAt(i) == '\t') continue; // starting whitespace
        if (response.charAt(i) == '[') break;   // we have an oob starter
        return response;                        // there is no oob data 
    }
    if ( i == len) return response; // no starter found
    var user = $('#txtUser').val();
     
    // walk string to find oob data and when ended return rest of string
    var start = 0;
    while (++i < len )
    {
        if (response.charAt(i) == ' ' || response.charAt(i) == ']') // separation
        {
            if (start != 0) // new oob chunk
            {
                var blob = response.slice(start,i);
                start = 0;

                var commandArr = blob.split('=');
                if (commandArr.length == 1) continue;   // failed to split left=right

                var command = commandArr[0]; // left side is command 
                var interval = (commandArr.length > 1) ? commandArr[1].trim() : -1; // right side is millisecond count
                if (interval == 0)  /* abort timeout item */
                {
                    switch (command){
                        case 'alarm':
                            window.clearTimeout(alarm);
                            alarm = null;
                            break;
                        case 'callback':
                            window.clearTimeout(callback);
                            callback = null;
                            break;
                        case 'loopback':
                            window.clearInterval(loopback);
                            loopback = null;
                            break;
                    }
                }
                else if (interval == -1) interval = -1; // do nothing
                else
                {
                    var timeoutmsg = {user: user, send: true, message: '[' + command + ' ]'}; // send naked command if timer goes off 
                    switch (command) {
                        case 'alarm':
                            alarm = setTimeout(function(){sendMessage(timeoutmsg );}, interval);
                            break;
                        case 'callback':
                            callback = setTimeout(function(){sendMessage(timeoutmsg );}, interval);
                            break;
                        case 'loopback':
                            loopback = setInterval(function(){sendMessage(timeoutmsg );}, interval);
                            break;
                    }
                }
            } // end new oob chunk
            if (response.charAt(i) == ']') return response.slice(i + 2); // return rest of string, skipping over space after ] 
        } // end if
        else if (start == 0) start = i; // begin new text blob
    } // end while
    return response;    // should never get here
 }
 
 
 
 function update(text){ // text is  HTML code to append to the 'chat log' div. This appends the input text to the response div
    var chatLog = $('#responseHolder').html();
    $('#responseHolder').html(chatLog + text);
    var rhd = $('#responseHolder');
    var h = rhd.get(0).scrollHeight;
    rhd.scrollTop(h);
}
 

function processResponse(response) { // given the final CS text, converts the parsed response from the CS server into HTML code for adding to the response holder div
    var botSaid = '<strong>' + botName + ':</strong> ' + response + "<br>\n";
    update(botSaid);
}

</script>


<script>
function openForm() {
  document.getElementById("myForm2").style.display = "block";
}

function closeForm() {
  document.getElementById("myForm2").style.display = "none";
}
</script>


</body>
</html>

and chatscripts original index.php:

<!DOCTYPE HTML>
<html>
  <head>
    <title>
      CHATSCRIPT SERVER
    </title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <style type="text/css">
      #responseHolder {
        min-width: 600px;
        min-height: 300px;
        width: 80%;
        height: 300px;
        overflow: auto;
        margin: 10px auto;
        background-color: pink;
      }

    </style>
  </head>
  <body>
    <div id="responseHolder"></div>
    <form id="frmChat" action="#">
    <p>
      Enter your message below:
    </p>
    <table>
      <tr>
        <td>Name:</td>
        <td>
          <input type="text" id="txtUser" name="user" size="20" value="" />
          <input type="hidden" name="send" />
        </td>
      </tr>
      <tr>
        <td>Message:</td>
        <td><input type="text" name="message" id="txtMessage" size="70" /></td>
      </tr>
      <tr>
        <td colspan="2"><input type="submit" name="send" value="Send Value" /></td>
      </tr>
    </table>
    </form>

<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script type="text/javascript">

var botName = 'Testy McTesty';      // change this to your bot name

// declare timer variables
var alarm = null;
var callback = null;
var loopback = null;

$(function(){
    $('#frmChat').submit(function(e){
    // this function overrides the form's submit() method, allowing us to use AJAX calls to communicate with the ChatScript server
    e.preventDefault();  // Prevent the default submit() method
    var name = $('#txtUser').val();
    if (name == '') {
        alert('Please provide your name.');
        document.getElementById('txtUser').focus();
    }
    var chatLog = $('#responseHolder').html();
    var youSaid = '<strong>' + name + ':</strong> ' + $('#txtMessage').val() + "<br>\n";
    update(youSaid);
    var data = $(this).serialize();
    sendMessage(data);
    $('#txtMessage').val('').focus();
    });

    // any user typing cancels loopback or callback for this round 
    $('#txtMessage').keypress(function(){
          window.clearInterval(loopback);
          window.clearTimeout(callback);
        });
});

function sendMessage(data){ //Sends inputs to the ChatScript server, and returns the response-  data - a JSON string of input information
$.ajax({
    url: 'ui.php',
    dataType: 'text',
    data: data,
    type: 'post',
    success: function(response){
        processResponse(parseCommands(response));
    },
    error: function(xhr, status, error){
        alert('oops? Status = ' + status + ', error message = ' + error + "\nResponse = " + xhr.responseText);
    }
  });
}

function parseCommands(response){ // Response is data from CS server. This processes OOB commands sent from the CS server returning the remaining response w/o oob commands

    var len  = response.length;
    var i = -1;
    while (++i < len )
    {
        if (response.charAt(i) == ' ' || response.charAt(i) == '\t') continue; // starting whitespace
        if (response.charAt(i) == '[') break;   // we have an oob starter
        return response;                        // there is no oob data 
    }
    if ( i == len) return response; // no starter found
    var user = $('#txtUser').val();
     
    // walk string to find oob data and when ended return rest of string
    var start = 0;
    while (++i < len )
    {
        if (response.charAt(i) == ' ' || response.charAt(i) == ']') // separation
        {
            if (start != 0) // new oob chunk
            {
                var blob = response.slice(start,i);
                start = 0;

                var commandArr = blob.split('=');
                if (commandArr.length == 1) continue;   // failed to split left=right

                var command = commandArr[0]; // left side is command 
                var interval = (commandArr.length > 1) ? commandArr[1].trim() : -1; // right side is millisecond count
                if (interval == 0)  /* abort timeout item */
                {
                    switch (command){
                        case 'alarm':
                            window.clearTimeout(alarm);
                            alarm = null;
                            break;
                        case 'callback':
                            window.clearTimeout(callback);
                            callback = null;
                            break;
                        case 'loopback':
                            window.clearInterval(loopback);
                            loopback = null;
                            break;
                    }
                }
                else if (interval == -1) interval = -1; // do nothing
                else
                {
                    var timeoutmsg = {user: user, send: true, message: '[' + command + ' ]'}; // send naked command if timer goes off 
                    switch (command) {
                        case 'alarm':
                            alarm = setTimeout(function(){sendMessage(timeoutmsg );}, interval);
                            break;
                        case 'callback':
                            callback = setTimeout(function(){sendMessage(timeoutmsg );}, interval);
                            break;
                        case 'loopback':
                            loopback = setInterval(function(){sendMessage(timeoutmsg );}, interval);
                            break;
                    }
                }
            } // end new oob chunk
            if (response.charAt(i) == ']') return response.slice(i + 2); // return rest of string, skipping over space after ] 
        } // end if
        else if (start == 0) start = i; // begin new text blob
    } // end while
    return response;    // should never get here
 }
 
function update(text){ // text is  HTML code to append to the 'chat log' div. This appends the input text to the response div
    var chatLog = $('#responseHolder').html();
    $('#responseHolder').html(chatLog + text);
    var rhd = $('#responseHolder');
    var h = rhd.get(0).scrollHeight;
    rhd.scrollTop(h);
}

function processResponse(response) { // given the final CS text, converts the parsed response from the CS server into HTML code for adding to the response holder div
    var botSaid = '<strong>' + botName + ':</strong> ' + response + "<br>\n";
    update(botSaid);
}

</script>
</body>
</html>


and yeah the web page is empty besides the chat window(which itself is not edited much) because I want to make the bot work in the web page before anything else (I use XAMPP for the connection).


Solution

  • If the bot corresponds correctly using the BETTER ui, then try to analyze the $postVars (with a vardump) sent to ui.php firstly. Then follow the same procedure with using it inside the pop up.

    The error tells you that's the ui.php expect an array from your index2.php but it receives nothing to extract.

    Because the data are sent through AJAX maybe there is a conflict with the original JS code and your pop up JS code.