chatStatus V3

  • Supports Caching using either Memcached or Redis Cache Engines.
    • Caching can be disabled - but to prevent the CCE and ECE API been overloaded - caching must be enabled in production.
  • Enhanced error handling.
    • CCE and ECE IDs only allowed in a configured range, e.g. 5000 - 5100 and 1000 - 1100
    • etc.
  • Enhanced debug output
  • Step by step Redhat Server Install & Configure Guide - here
  • Step by Step Ubuntu Server Install & Configure Guide - here

Note - this is also a legacy version - but is ideal for demos - as can be placed directly on the ECE Web server without any additional 3rd party software or configuration.

  • Business Hours - open / close your Web Chat via CCE Business Hours GUI
  • Web Chat can dynamically go 'busy' based on real time values retrieved via the ECE APIs and compared with min / max variables configured - these desired max/min values are set via Business Hours Name - in CCE.
    • Min Agent Availability
    • Max Queue Depth
    • Max Wait Time
  • Simple Proactive Chat - enable / disable & configurable timer

Business Hour Reasons can also be used to auto change the message for different closed reasons.

The chatStatus API returns 3 core variables which can be used on your webpage to change the status of your chat.

  • chatStatus - this variable is set to open, closed or busy based on real time data from the relevant APIs.
  • chatStatusReason (the value of the reason is retrieved via CCE BusinessHours API (or set to “out of service” - if unable to do so)
  • ChatTimer (the time in ms to wait before showing the proactive chat)
  • If the min Agent Count is set to '0' then the ECE Capacity API is not used.
  • if the Max Queue Depth AND the Max Wait Time are both set to '0' the ECE Live Session API is not used.
  • If all 3 of the ECE variables are set to '0' - then only the CCE Business Hours API is used to check if Chat is open or closed. Note this would mean that the Chat appears open even if the ECE System was out of service (since no ECE APIs are checked).

Sample JSON output when using the following example URL and where debugLevel is set to '3'. https://api.ubuntu100.lab1.mydomain.com/chatStatus.php?businessHoursId=5000&chatEntryId=1002

{
  "chatStatus": "open",
  "chatStatusReason": "",
  "chatTimer": 10000,
  "errorCode": 0,
  "errorString": "",
  "debugLevel": 3,
  "cacheEngine": "Redis",
  "cceBusinessHoursAPI": "From Source",
  "eceCapacityAPI": "From Source",
  "eceLiveSessionAPI": "From Source",
  "cceBusinessHoursObj": {
    "API": "cceBusinessHours",
    "Name": "Chat_10_1_10_60",
    "Status": "open",
    "runTimeStatusReason": "Week Day open reason",
    "ChatTimer": 10000,
    "MinAgentCount": 1,
    "MaxQueueDepth": 10,
    "MaxWaitTime": 60,
    "errorcode": 0,
    "errorstring": "",
    "httpcode": 200,
    "runTimeStatus": 3,
    "cache": false
  },
  "eceCapacityObj": {
    "API": "eceCapacity",
    "Status": "open",
    "BusyReason": "",
    "MinAgentCount": 1,
    "errorcode": 0,
    "errorstring": "",
    "httpcode": 200,
    "AgentCount": 5,
    "cache": false
  },
  "eceLiveSessionObj": {
    "API": "eceLiveSession",
    "Status": "open",
    "BusyReason": "",
    "errorcode": 0,
    "errorstring": "",
    "httpcode": 200,
    "MaxQueueDepth": 10,
    "MaxWaitTime": 60,
    "queueDepth": 0,
    "waitTime": 16,
    "cache": false
  }
}

http://ece-webserver.lab2.purplepi.ie/system/egain/chat/entrypoint/liveSessionStatus/1001

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<sessionStatus xmlns:ns5="http://jabber.org/protocol/httpbind" xmlns:ns2="http://bindings.egain.com/chat" xmlns:ns4="jabber:client" xmlns:ns3="urn:ietf:params:xml:ns:xmpp-stanzas">
    <ns2:waitTime>60.0</ns2:waitTime>
    <ns2:queueDepth>0</ns2:queueDepth>
    <ns2:altEngmtTime>0</ns2:altEngmtTime>
</sessionStatus>

http://ece-webserver.lab2.purplepi.ie/system/egain/chat/entrypoint/capacity/1001

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<availableSlots xmlns:ns5="http://jabber.org/protocol/httpbind" xmlns:ns2="http://bindings.egain.com/chat" xmlns:ns4="jabber:client" xmlns:ns3="urn:ietf:params:xml:ns:xmpp-stanzas">
    <ns2:count>3</ns2:count>
</availableSlots>

https://ucce-hds-a.lab2.purplepi.ie/unifiedconfig/config/businesshour/5001

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<businessHour>
	<department>
		<refURL>/unifiedconfig/config/department/5000</refURL>
		<name>Department1</name>
	</department>
	<refURL>/unifiedconfig/config/businesshour/5001</refURL>
	<changeStamp>86</changeStamp>
	<configuredStatus>
		<status>2</status>
		<statusReason>
			<refURL>/unifiedconfig/config/businesshourstatusreason/5006</refURL>
			<reasonText>Open Override</reasonText>
		</statusReason>
	</configuredStatus>
	<description>
    </description>
	<name>Sales.Chat_0_1_6_120</name>
	<runTimeStatus>3</runTimeStatus>
	<runTimeStatusReason>Open Override</runTimeStatusReason>
	<specialDaySchedules>
		<specialDaySchedule>
			<refURL>/unifiedconfig/config/businesshour/5001/specialdayschedule/5000</refURL>
			<changeStamp>0</changeStamp>
			<date>18-03-2019</date>
			<description>St Pat Bank Holiday</description>
			<status>0</status>
			<statusReason>
				<refURL>/unifiedconfig/config/businesshourstatusreason/5001</refURL>
				<reasonText>Week Day closed reason</reasonText>
			</statusReason>
		</specialDaySchedule>
	</specialDaySchedules>
	<timezone>
		<refURL>/unifiedconfig/config/timezone/v2/5038</refURL>
		<displayName>(UTC+00:00) Dublin, Edinburgh, Lisbon, London</displayName>
	</timezone>
	<type>1</type>
	<weekDaySchedules>
		<weekDaySchedule>
			<refURL>/unifiedconfig/config/businesshour/5001/weekdayschedule/5011</refURL>
			<changeStamp>0</changeStamp>
			<endTime>17:00</endTime>
			<startTime>09:00</startTime>
			<dayOfWeek>1</dayOfWeek>
		</weekDaySchedule>
		<weekDaySchedule>
			<refURL>/unifiedconfig/config/businesshour/5001/weekdayschedule/5012</refURL>
			<changeStamp>0</changeStamp>
			<endTime>17:00</endTime>
			<startTime>09:00</startTime>
			<dayOfWeek>2</dayOfWeek>
		</weekDaySchedule>
		<weekDaySchedule>
			<refURL>/unifiedconfig/config/businesshour/5001/weekdayschedule/5013</refURL>
			<changeStamp>0</changeStamp>
			<endTime>17:00</endTime>
			<startTime>09:00</startTime>
			<dayOfWeek>3</dayOfWeek>
		</weekDaySchedule>
		<weekDaySchedule>
			<refURL>/unifiedconfig/config/businesshour/5001/weekdayschedule/5014</refURL>
			<changeStamp>0</changeStamp>
			<endTime>17:00</endTime>
			<startTime>09:00</startTime>
			<dayOfWeek>4</dayOfWeek>
		</weekDaySchedule>
		<weekDaySchedule>
			<refURL>/unifiedconfig/config/businesshour/5001/weekdayschedule/5015</refURL>
			<changeStamp>0</changeStamp>
			<endTime>17:00</endTime>
			<startTime>09:00</startTime>
			<dayOfWeek>5</dayOfWeek>
		</weekDaySchedule>
	</weekDaySchedules>
</businessHour>

Note these test were done on a low end lab machine. 5000 Request made, with 500 concurrent Requests.

Caching Enabled - REDIS

  • HTTP - 2.5 seconds with zero failures
  • HTTPS - 15 seconds with 103 failures (failures seem to be HTTP response cut off mid way through response - so network / HTTPS /Server related and not related to the chatStatus API itself

Caching Disable

  • HTTP - 189 seconds with only 612 successful responses! The ECE API giving 500 error when overloaded (as expected).

Log Files

How to Configure php.info for above

extension_dir = "ext"
;; some other lines ...
extension=curl
;; and some more lines ...
extension=php-7.4.x_memcache.dll

This file includes a sample webpage which uses the chatStatus API and some simple custom JavaScript to change the webpage to open/closed/busy using AJAX updated every X seconds).

chatstatus.php
<?php
 
    /*
    ChatStatus API Version 3.6 - Memcached & Redis Caching Capable.
 
    Author: Gerry O'Rourke - Purplepi.ie
    Date: 16/12/2020
    Updated 29/04/2022
    Updated 22/03/2024 - Added some additional Logging & configured IPV4 DNS resolution only (disabling IPV6) in CURL Options
                       - Increased timeout values. If Primary DNS is down, it takes 5 seconds to attempt Backup.
                       - DNS is NOT enabled by default on Red Hat Enterprise - but is strongly recommended to be enabled!
    */
 
    /*
    MIT License
 
    Copyright (c) 2020 Gerard O'Rourke - purplepi.ie
 
    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:
 
    The above copyright notice and this permission notice shall be included in all
    copies or substantial portions of the Software.
 
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    SOFTWARE.
    */
 
// ******************************************************************************************** //
// **************************** Review and change Config BELOW this Line*********************** //
// ******************************************************************************************** //
 
//Variables CCE Server Details
$ccehostname = 'ucce-aw-server.example.com';
 
$cceuser = '[email protected]'; //create a dedicated PCCE User with read access to BusinessHours API (All departements) for this role.
$ccepass = 'IncorrectPa$$w0rd!'; 
 
//Variables ECE Server Details
$ecehostname = 'ece-webserver.example.com';
 
//HTTP (CURL) Timeout Values
$http_connect_timeout = 7; //HTTP Connection Time in Seconds - see CURLOPT_CONNECTTIMEOUT for more info.
$http_timeout = 10; //HTTP Maximum Time in Seconds for Request - see CURLOPT_TIMEOUT for more info.
$tls_cert_check = false;
 
// Default Values if unable to read 'Chat config' values from BusinessHours Name (if not set or malformed)
$defaultProactiveChatTimer = '0'; // value here in seconds, e.g. '10' for a 10 second delay for Proactive Chat. '0' to disable.
$defaultMinAgentCount = '0'; // zero values disables MinAgentCount Check
$defaultMaxQueueDepth = '0'; // zero values disables MaxQueueDepth Check
$defaultMaxWaitTime = '0'; // zero values disables MaxWaitTime Check
 
//debug level
$debuglevel = 3; // 0 = disabled, 1 = minimum (used on JS client code to output JSON to Console), 2 = some additional debug, 3 = All (including object details from the 3 APIs)
 
//Caching Config
$cache_engine = 2; // 0 = disabled (do NOT set to '0' in production, as it would overload the Web API Servers), 1 = Memcached, 2 = Redis
$cache_ttl = '29'; // set to 15 or higher for production (only used if caching engine is enabled) - depends on number of total number of different BusinessHours & Entry Points Used
$cacheEngine_host = 'localhost';
 
//Min and Max allowed for BusinessHoursId and ChatEntryIds
$minBusinessHoursId = 5000;
$maxBusinessHoursId = 5100;
$minChatEntryId = 1000;
$maxChatEntryId = 1100;
 
// ******************************************************************************************** //
// **************************** Review and change Config ABOVE this Line ********************** //
// ******************************************************************************************** //
 
header('Content-Type: application/json');
 
// Initiate variable values
$chatStatus = 'closed'; //Defaults value until overwritten
$chatStatusReason = ''; //Defaults value until overwritten
$chatTimer = 0; //Defaults value until overwritten
$errorcode = 0;
$errorstring = '';
$cceBusinessHoursAPI = '';
$eceCapacityAPI = '';
$eceLiveSessionAPI = '';
$cceBusinessHoursJSON = null;
$eceCapacityJSON = null;
$eceLiveSessionJSON = null;
 
//Cache Engine is set correctly - must be 0, 1 or 2
if (!is_int($cache_engine) || $cache_engine < 0 || $cache_engine > 2) {
    chatStatusError('Invalid Cache Engine Configuration.', 500);
}
 
//Get businessHoursId and chatEntryId variables
$_GET_lower = array_change_key_case($_GET, CASE_LOWER);
 
if (isset($_GET_lower['businesshoursid'])) {
    $myBusinessHoursId = htmlspecialchars($_GET_lower['businesshoursid']);
    if (!is_numeric($myBusinessHoursId)) {
        chatStatusError('businessHoursId not an integer.', 406);
    }
    if (
        $myBusinessHoursId < $minBusinessHoursId ||
        $myBusinessHoursId > $maxBusinessHoursId
    ) {
        chatStatusError('businessHoursId not in allowed range.', 406);
    }
} else {
    chatStatusError('businessHoursId not set.', 406);
}
 
if (isset($_GET_lower['chatentryid'])) {
    $myChatEntryId = htmlspecialchars($_GET_lower['chatentryid']);
    if (!is_numeric($myChatEntryId)) {
        chatStatusError('chatEntryID not an integer', 406);
    }
    if ($myChatEntryId < $minChatEntryId || $myChatEntryId > $maxChatEntryId) {
        chatStatusError('chatEntryId not in allowed range.', 406);
    }
} else {
    chatStatusError('chatEntryId not set.', 406);
}
 
// CCE & ECE URLs
$cceBusinessHoursUrl = 'https://' . $ccehostname . '/unifiedconfig/config/businesshour/' . $myBusinessHoursId;
 
//Used for testing when actually API Web Servers are unavailable - use a static XML file -
//copy file to local webserver and change cce hostname to be localhost
//$cceBusinessHoursUrl = 'http://' . $ccehostname . '/unifiedconfig/config/businesshour/5000.xml';
 
$eceCapacityUrl = 'https://' . $ecehostname . '/system/egain/chat/entrypoint/capacity/' . $myChatEntryId; 
$eceLiveSessionUrl = 'https://' . $ecehostname . '/system/egain/chat/entrypoint/liveSessionStatus/' . $myChatEntryId;
 
//Used for testing when ECE API Web Servers are unavailable - use a static XML file.
//$eceCapacityUrl = 'http://' . $ecehostname . '/system/egain/chat/entrypoint/capacity/1002.xml';
//$eceLiveSessionUrl = 'http://' . $ecehostname . '/system/egain/chat/entrypoint/liveSessionStatus/1002.xml';
 
// Memcache Variables (these are the 3 strings stored in the cache engine).
$cceBusinessHoursCacheVar = 'chatStatus_cceBusinessHoursId_' . $myBusinessHoursId; 
$eceCapacityCacheVar = 'chatStatus_eceCapacity_ChatEntryId_' . $myChatEntryId;
$eceLiveSessionCacheVar = 'chatStatus_eceLiveSession_ChatEntryId_' . $myChatEntryId;
 
// Connect to Memcached Server
if ($cache_engine == 1) {
    if (!class_exists('Memcache')) {
        chatStatusError('Memcache Module not installed.', 500);
    }
    $memcache = new Memcache();
    // @ symbol supresses output errors
    $memcache_status = @$memcache->connect($cacheEngine_host, 11211);
    if (!$memcache_status) {
        chatStatusError('Memcache Connection Error.', 500);
    }
    $cceBusinessHoursJSON = $memcache->get($cceBusinessHoursCacheVar);
    $eceCapacityJSON = $memcache->get($eceCapacityCacheVar);
    $eceLiveSessionJSON = $memcache->get($eceLiveSessionCacheVar);
}
 
// Connect to Redis Server
if ($cache_engine == 2) {
    if (!class_exists('Redis')) {
        chatStatusError('Redis Module not installed.', 500);
    }
    $redis = new Redis();
    try {
        $redis->connect($cacheEngine_host, 6379, 3);
    } catch (Exception $e) {
        chatStatusError('Redis Connection Error.', 500);
    }
    $cceBusinessHoursJSON = $redis->get($cceBusinessHoursCacheVar);
    $eceCapacityJSON = $redis->get($eceCapacityCacheVar);
    $eceLiveSessionJSON = $redis->get($eceLiveSessionCacheVar);
}
 
//Check if cceBusinessHours Data is in Cache
if ($cceBusinessHoursJSON != null) {
    // Cache available
    $cceBusinessHoursObj = json_decode($cceBusinessHoursJSON);
    $cceBusinessHoursObj->cache = true;
    $cceBusinessHoursJSON = json_encode($cceBusinessHoursObj);
    $cceBusinessHoursAPI = 'From Cache';
} else {
    // Cache miss
    $cceBusinessHoursObj = cceBusinessHoursAPI($cceBusinessHoursUrl);
    $cceBusinessHoursJSON = json_encode($cceBusinessHoursObj);
    $cceBusinessHoursObj->cache = false;
    $cceBusinessHoursAPI = 'From Source';
    if ($cache_engine == 1) {
        $memcache->set(
            $cceBusinessHoursCacheVar,
            $cceBusinessHoursJSON,
            0,
            $cache_ttl
        );
    }
    if ($cache_engine == 2) {
        $redis->setex(
            $cceBusinessHoursCacheVar,
            $cache_ttl,
            $cceBusinessHoursJSON
        );
    }
}
 
$chatStatus = $cceBusinessHoursObj->Status;
$chatStatusReason = $cceBusinessHoursObj->runTimeStatusReason;
$chatTimer = $cceBusinessHoursObj->ChatTimer;
 
if ($cceBusinessHoursObj->Status !== 'open') {
    $chatStatus = 'closed';
    $chatStatusReason = $cceBusinessHoursObj->runTimeStatusReason;
    $errorcode = $cceBusinessHoursObj->errorcode;
    $errorstring = $cceBusinessHoursObj->errorstring;
    chatStatusResponse();
}
 
// Check eceCapacityAPI
// Only check this ECE Capacity API if the min Agent Count is specificed as non zero.
if ($cceBusinessHoursObj->MinAgentCount > 0) {
    if ($eceCapacityJSON != null) {
        // Cache hit
        // Render from cache
        $eceCapacityObj = json_decode($eceCapacityJSON);
        $eceCapacityObj->cache = true;
        $eceCapacityAPI = 'From Cache';
        $eceCapacityJSON = json_encode($eceCapacityObj);
    } else {
        // Cache miss
        $eceCapacityObj = eceCapacityAPI(
            $eceCapacityUrl,
            $cceBusinessHoursObj->MinAgentCount
        );
        $eceCapacityObj->cache = false;
        $eceCapacityAPI = 'From Source';
        $eceCapacityJSON = json_encode($eceCapacityObj);
        if ($cache_engine == 1) {
            $memcache->set(
                $eceCapacityCacheVar,
                $eceCapacityJSON,
                0,
                $cache_ttl
            );
        }
        if ($cache_engine == 2) {
            $redis->setex($eceCapacityCacheVar, $cache_ttl, $eceCapacityJSON);
        }
    }
    if ($eceCapacityObj->Status !== 'open') {
        $chatStatus = $eceCapacityObj->Status;
        $chatStatusReason = $eceCapacityObj->BusyReason;
        $errorcode = $eceCapacityObj->errorcode;
        $errorstring = $eceCapacityObj->errorstring;
        chatStatusResponse();
    }
}
 
// Check eceLiveSessionAPI
// Only Check the ECE Live Session API if $cceBusinessHoursObj->MaxQueueDepth and $cceBusinessHoursObj->MaxWaitTime are both are NOT the integer '0'.
if (
    $cceBusinessHoursObj->MaxQueueDepth !== 0 ||
    $cceBusinessHoursObj->MaxWaitTime !== 0
) {
    if ($eceLiveSessionJSON != null) {
        // Cache hit
        // Render from cache
        $eceLiveSessionObj = json_decode($eceLiveSessionJSON);
        $eceLiveSessionObj->cache = true;
        $eceLiveSessionAPI = 'From Cache';
        $eceLiveSessionJSON = json_encode($eceLiveSessionObj);
    } else {
        // Cache miss
        $eceLiveSessionObj = eceLiveSessionAPI(
            $eceLiveSessionUrl,
            $cceBusinessHoursObj->MaxQueueDepth,
            $cceBusinessHoursObj->MaxWaitTime
        );
        $eceLiveSessionObj->cache = false;
        $eceLiveSessionAPI = 'From Source';
        $eceLiveSessionJSON = json_encode($eceLiveSessionObj);
        if ($cache_engine == 1) {
            $memcache->set(
                $eceLiveSessionCacheVar,
                $eceLiveSessionJSON,
                0,
                $cache_ttl
            );
        }
        if ($cache_engine == 2) {
            $redis->setex(
                $eceLiveSessionCacheVar,
                $cache_ttl,
                $eceLiveSessionJSON
            );
        }
    }
 
    $chatStatus = $eceLiveSessionObj->Status;
    $chatStatusReason = $eceLiveSessionObj->BusyReason;
    $errorcode = $eceLiveSessionObj->errorcode;
    $errorstring = $eceLiveSessionObj->errorstring;
}
 
chatStatusResponse();
 
/*chatStatusError*/
//This function should only be called if Memcached / Redis is down or invalid url parameters.
function chatStatusError($errormessage, $code)
{
    global $debuglevel;
 
    $chatStatus = 'closed';
    $chatStatusReason = 'out of service';
    $chatTimer = 0;
    $errorcode = $code;
    http_response_code($code); //SET HTTP Error Response Code
    $errorstring = $errormessage;
 
    $chatStatusObj = new \stdClass();
    $chatStatusObj->chatstatus = $chatStatus;
    $chatStatusObj->chatStatusReason = $chatStatusReason;
    $chatStatusObj->chatTimer = $chatTimer;
    $chatStatusObj->errorcode = $errorcode;
    $chatStatusObj->errorstring = $errorstring;
    $chatStatusObj->debuglevel = $debuglevel;
 
    $chatStatusJSON = json_encode($chatStatusObj);
    echo $chatStatusJSON;
    exit();
}
 
function chatStatusResponse()
{
    global $debuglevel,
        $chatStatus,
        $chatStatusReason,
        $chatTimer,
        $errorcode,
        $errorstring,
        $cache_engine;
    global $cceBusinessHoursAPI,
        $eceCapacityAPI,
        $eceLiveSessionAPI,
        $cceBusinessHoursObj,
        $eceCapacityObj,
        $eceLiveSessionObj;
 
    $chatStatusObj = new \stdClass();
    $chatStatusObj->chatStatus = $chatStatus;
    $chatStatusObj->chatStatusReason = $chatStatusReason;
 
    if ($chatStatus !== 'open') {
        $chatStatusObj->chatTimer = 0;
    } else {
        $chatStatusObj->chatTimer = $chatTimer;
    }
    $chatStatusObj->errorCode = $errorcode;
 
    if ($errorcode >= 400 or $errorcode < 600) {
        //http_response_code($errorcode); //Not setting HTTP as an error - defaults to 200 OK - use errorCode in Body to determine an errror
    }
 
    $chatStatusObj->errorString = $errorstring;
    $chatStatusObj->debugLevel = $debuglevel;
 
    if ($cache_engine == 0) {
        $chatStatusObj->cacheEngine = 'Disabled';
    }
    if ($cache_engine == 1) {
        $chatStatusObj->cacheEngine = 'Memcached';
    }
    if ($cache_engine == 2) {
        $chatStatusObj->cacheEngine = 'Redis';
    }
 
    if ($debuglevel > 1) {
        $chatStatusObj->cceBusinessHoursAPI = $cceBusinessHoursAPI;
        $chatStatusObj->eceCapacityAPI = $eceCapacityAPI;
        $chatStatusObj->eceLiveSessionAPI = $eceLiveSessionAPI;
    }
 
    if ($debuglevel > 2) {
        $chatStatusObj->cceBusinessHoursObj = $cceBusinessHoursObj;
        $chatStatusObj->eceCapacityObj = $eceCapacityObj;
        $chatStatusObj->eceLiveSessionObj = $eceLiveSessionObj;
    }
 
    $chatStatusJSON = json_encode($chatStatusObj);
    echo $chatStatusJSON;
    exit();
}
 
function cceBusinessHoursAPI($url)
{
    global $cceuser,
        $ccepass,
        $tls_cert_check,
        $http_connect_timeout,
        $http_timeout;
    global $defaultProactiveChatTimer,
        $defaultMinAgentCount,
        $defaultMaxQueueDepth,
        $defaultMaxWaitTime,
        $debuglevel;
 
    $cceBusinessHoursObj = new \stdClass();
    // Initial Values
    $cceBusinessHoursObj->API = 'cceBusinessHours';
    $cceBusinessHoursObj->Name = 'Unknown';
    $cceBusinessHoursObj->Status = 'closed';
    $cceBusinessHoursObj->runTimeStatusReason = 'out of service';
    $cceBusinessHoursObj->ChatTimer = $defaultProactiveChatTimer;
    $cceBusinessHoursObj->MinAgentCount = $defaultMinAgentCount;
    $cceBusinessHoursObj->MaxQueueDepth = $defaultMaxQueueDepth;
    $cceBusinessHoursObj->MaxWaitTime = $defaultMaxWaitTime;
    $cceBusinessHoursObj->errorcode = 0;
    $cceBusinessHoursObj->errorstring = '';
 
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $tls_cert_check);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $tls_cert_check);
    curl_setopt($ch, CURLOPT_USERPWD, "$cceuser:$ccepass");
    curl_setopt($ch, CURLOPT_HTTPGET, 1);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $http_connect_timeout); //connection timeout
    curl_setopt($ch, CURLOPT_TIMEOUT, $http_timeout); //timeout in seconds for request
    curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); //CURL to use IPV4 DNS lookup only (and not attempt both DNS for IPV4 and IPV6 records)
 
    $api_response = curl_exec($ch);
    //$info = curl_getinfo($ch);
    //print_r ($info);
    //exit();
 
    $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $cceBusinessHoursObj->httpcode = $httpcode;
    if ($httpcode !== 200) {
        $cceBusinessHoursObj->errorstring = 'CCE BusinessHours HTTP Error.';
 
        //Error Log
        error_log("CCE BusinessHours HTTP ERROR. HTTPURL: $url HTTPCode: $httpcode",0);
        error_log('Curl ErrorCode: ' . curl_errno($ch));
        error_log('Curl ErrorString: ' . curl_error($ch));
        if ($debuglevel > 2) {
            $info = curl_getinfo($ch);
            error_log(json_encode($info, JSON_UNESCAPED_SLASHES), 0);
        }
 
        if ($httpcode !== 0) {
            $cceBusinessHoursObj->errorcode = $httpcode;
            //http_response_code($httpcode);
        } else {
            $cceBusinessHoursObj->errorcode = 504;
            //http_response_code(500);
        }
        curl_close($ch);
        return $cceBusinessHoursObj;
    }
    curl_close($ch);
 
    //Supress Warnings if response cannot be parsed.
    $xml = @simplexml_load_string($api_response);
    if ($xml) {
        //continue
    } else {
        $cceBusinessHoursObj->errorcode = 500;
        $cceBusinessHoursObj->errorstring = 'Error parsing XML';
        return $cceBusinessHoursObj;
    }
 
    //print_r($xml);
 
    if (isset($xml->name)) {
        $bh_name = (string) $xml->name;
    } else {
        $cceBusinessHoursObj->errorcode = 500;
        $cceBusinessHoursObj->errorstring = 'Unable to retrieve Business Hour Name';
        return $cceBusinessHoursObj;
    }
    $cceBusinessHoursObj->Name = $bh_name;
 
    if (isset($xml->runTimeStatus)) {
        $bh_status = (int) $xml->runTimeStatus;
    } else {
        $cceBusinessHoursObj->errorcode = 500;
        $cceBusinessHoursObj->errorstring = 'Unable to retrieve runTimeStatus';
        return $cceBusinessHoursObj;
    }
    $cceBusinessHoursObj->runTimeStatus = $bh_status;
 
    if (isset($xml->runTimeStatusReason)) {
        $bh_statusReason = (string) $xml->runTimeStatusReason;
    } else {
        $cceBusinessHoursObj->errorcode = 500;
        $cceBusinessHoursObj->errorstring = 'Unable to retrieve runTimeStatusReason';
        return $cceBusinessHoursObj;
    }
    $cceBusinessHoursObj->runTimeStatusReason = $bh_statusReason;
 
    //Parse the Business name using underscore to find the configured Chat Values
    $myvar = explode('_', $bh_name);
    if (isset($myvar[1]) && is_numeric($myvar[1])) {
        $myProactiveChatTimer = $myvar[1];
    } else {
        $myProactiveChatTimer = $defaultProactiveChatTimer;
    }
    if (isset($myvar[2]) && is_numeric($myvar[2])) {
        $myMinAgentCount = $myvar[2];
    } else {
        $myMinAgentCount = $defaultMinAgentCount;
    }
    if (isset($myvar[3]) && is_numeric($myvar[3])) {
        $myMaxQueueDepth = $myvar[3];
    } else {
        $myMaxQueueDepth = $defaultMaxQueueDepth;
    }
    if (isset($myvar[4]) && is_numeric($myvar[4])) {
        $myMaxWaitTime = $myvar[4];
    } else {
        $myMaxWaitTime = $defaultMaxWaitTime;
    }
 
    $myProactiveChatTimer = $myProactiveChatTimer * 1000; //convert to ms
 
    if ($bh_status == 1 or $bh_status == 3) {
        $chatStatus = 'open';
    } else {
        $chatStatus = 'closed';
    }
 
    $cceBusinessHoursObj->Status = $chatStatus;
 
    // Note by converting to an integer if they variable value is invalid, the value will return zero - which is good - as its the disables the check.
    $cceBusinessHoursObj->ChatTimer = intval($myProactiveChatTimer);
    $cceBusinessHoursObj->MinAgentCount = intval($myMinAgentCount);
    $cceBusinessHoursObj->MaxQueueDepth = intval($myMaxQueueDepth);
    $cceBusinessHoursObj->MaxWaitTime = intval($myMaxWaitTime);
    $cceBusinessHoursObj->errorstring = '';
    return $cceBusinessHoursObj;
}
 
// ECE Capacity API - Checking Agents availability
function eceCapacityAPI($url, $MinAgentCount)
{
    global $tls_cert_check, $http_connect_timeout, $http_timeout, $debuglevel;
 
    $eceCapacityObj = new \stdClass();
    // Initial Values
    $eceCapacityObj->API = 'eceCapacity';
    $eceCapacityObj->Status = 'closed';
    $eceCapacityObj->BusyReason = 'out of service';
    $eceCapacityObj->MinAgentCount = $MinAgentCount;
    $eceCapacityObj->errorcode = 0;
    $eceCapacityObj->errorstring = '';
 
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $tls_cert_check);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $tls_cert_check);
    curl_setopt($ch, CURLOPT_HTTPGET, 1);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $http_connect_timeout); //connection timeout
    curl_setopt($ch, CURLOPT_TIMEOUT, $http_timeout); //timeout in seconds for request
    curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); //CURL to use IPV4 DNS lookup only (and not attempt both DNS for IPV4 and IPV6 records)
    $api_response = curl_exec($ch);
    //$info = curl_getinfo($ch);
    $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
 
    $eceCapacityObj->httpcode = $httpcode;
    if ($httpcode !== 200) {
        $eceCapacityObj->errorstring = 'ECE Capacity HTTP Error.';
 
        //Error Log
        error_log(
            "ECE Capacity HTTP Error. HTTPURL: $url HTTPCode: $httpcode",
            0
        );
        error_log('Curl ErrorCode: ' . curl_errno($ch));
        error_log('Curl ErrorString: ' . curl_error($ch));
        if ($debuglevel > 2) {
            $info = curl_getinfo($ch);
            error_log(json_encode($info, JSON_UNESCAPED_SLASHES), 0);
        }
 
        if ($httpcode !== 0) {
            $eceCapacityObj->errorcode = $httpcode;
        } else {
            $eceCapacityObj->errorcode = 504;
        }
        curl_close($ch);
        return $eceCapacityObj;
    }
 
    curl_close($ch);
 
    $xml_data = str_replace('ns2:', '', $api_response);
 
    //Suppress errors if unable to parse XML using @
    $xml_capacity = @simplexml_load_string($xml_data);
 
    //Confirming we were able to parse the XML
    if ($xml_capacity) {
        //continue
    } else {
        $eceCapacityObj->errorstring = 'Erroring parsing XML';
        return $eceCapacityObj;
    }
 
    //      Check if 'count' is in XML and also if its numerical. Note - don't convert to interger before check as any string value would return 0 if converted to interger - which would pass numeric test.
 
    if (
        isset($xml_capacity->count) &&
        is_numeric((string) $xml_capacity->count)
    ) {
        $agentcount = (int) $xml_capacity->count;
    } else {
        $eceCapacityObj->errorstring = 'Invalid MinAgentCount';
        return $eceCapacityObj;
    }
 
    $eceCapacityObj->AgentCount = $agentcount;
 
    if ($agentcount >= $MinAgentCount) {
        $eceCapacityObj->Status = 'open';
        $eceCapacityObj->BusyReason = '';
    } else {
        $eceCapacityObj->Status = 'busy';
        $eceCapacityObj->BusyReason = 'MinAgentCount';
    }
    return $eceCapacityObj;
}
 
function eceLiveSessionAPI($url, $MaxQueueDepth, $MaxWaitTime)
{
    global $tls_cert_check, $http_connect_timeout, $http_timeout, $debuglevel;
 
    $eceLiveSessionObj = new \stdClass();
    // Initial Values
    $eceLiveSessionObj->API = 'eceLiveSession';
    $eceLiveSessionObj->Status = 'closed';
    $eceLiveSessionObj->BusyReason = 'out of service';
    $eceLiveSessionObj->errorcode = 0;
    $eceLiveSessionObj->errorstring = '';
 
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $tls_cert_check);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $tls_cert_check);
    curl_setopt($ch, CURLOPT_HTTPGET, 1);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $http_connect_timeout); //connection timeout
    curl_setopt($ch, CURLOPT_TIMEOUT, $http_timeout); //timeout in seconds for request
    curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); //CURL to use IPV4 DNS lookup only (and not attempt both DNS for IPV4 and IPV6 records)
 
    $api_response = curl_exec($ch);
    $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    //$info = curl_getinfo($ch);
 
    $eceLiveSessionObj->httpcode = $httpcode;
    if ($httpcode !== 200) {
        $eceLiveSessionObj->errorstring = 'ECE LiveSession HTTP Error.';
 
        //Error Log
        error_log(
            "ECE LiveSession HTTP Error. HTTPURL: $url HTTPCode: $httpcode",
            0
        );
        error_log('Curl ErrorCode: ' . curl_errno($ch));
        error_log('Curl ErrorString: ' . curl_error($ch));
        if ($debuglevel > 2) {
            $info = curl_getinfo($ch);
            error_log(json_encode($info, JSON_UNESCAPED_SLASHES), 0);
        }
 
        if ($httpcode !== 0) {
            $eceLiveSessionObj->errorcode = $httpcode;
        } else {
            $eceLiveSessionObj->errorcode = 504;
        }
        curl_close($ch);
        return $eceLiveSessionObj;
    }
 
    curl_close($ch);
 
    $xml_data = str_replace('ns2:', '', $api_response);
 
    //Supress Errors if unable to parse  XML
    $xml_liveSessionStatus = @simplexml_load_string($xml_data);
 
    if ($xml_liveSessionStatus) {
        //continue
    } else {
        $eceLiveSessionObj->errorstring = 'Error parsing XML';
        return $eceLiveSessionObj;
    }
 
    if (
        isset($xml_liveSessionStatus->queueDepth) &&
        is_numeric((string) $xml_liveSessionStatus->queueDepth)
    ) {
        $queueDepth = (int) $xml_liveSessionStatus->queueDepth;
    } else {
        $eceLiveSessionObj->errorstring = 'Unable to read queueDepth';
        return $eceLiveSessionObj;
    }
 
    if (
        isset($xml_liveSessionStatus->waitTime) &&
        is_numeric((string) $xml_liveSessionStatus->waitTime)
    ) {
        $waitTime = (int) $xml_liveSessionStatus->waitTime;
    } else {
        $eceLiveSessionObj->errorstring = 'Unable to read waitTime';
        return $eceLiveSessionObj;
    }
 
    $eceLiveSessionObj->MaxQueueDepth = $MaxQueueDepth;
    $eceLiveSessionObj->MaxWaitTime = $MaxWaitTime;
    $eceLiveSessionObj->queueDepth = $queueDepth;
    $eceLiveSessionObj->waitTime = $waitTime;
 
    // Go Busy if Queue Depth is greater than Max Queue Depth - unless Max Queue Depth is set to 0 (i.e. Max Queue Depth if 0 disables the check).
    if ($MaxQueueDepth != 0 && $queueDepth > $MaxQueueDepth) {
        $eceLiveSessionObj->Status = 'busy';
        $eceLiveSessionObj->BusyReason = 'MaxQueueDepth';
        $eceLiveSessionObj->errorstring = '';
    } else {
        if ($MaxWaitTime != 0 && $waitTime > $MaxWaitTime && $queueDepth != 0) {
            $eceLiveSessionObj->Status = 'busy';
            $eceLiveSessionObj->BusyReason = 'MaxWaitTime';
            $eceLiveSessionObj->errorstring = '';
        } else {
            $eceLiveSessionObj->Status = 'open';
            $eceLiveSessionObj->BusyReason = '';
            $eceLiveSessionObj->errorstring = '';
        }
    }
    return $eceLiveSessionObj;
}
?>

https://app.swaggerhub.com/apis/gerardorourke/chatstatus/3.4

chatstatus.yml
openapi: 3.0.1
info:
  title: chatstatus.php
  description: chatstatus PHP API - can be used to enable a Cisco ECE WebChat to be opened, closed or busy based on opening hours (using Cisco CCE Business Hours and Agent Availability, Queue Depth and Wait Time - using the Cisco ECE APIs) 
  version: '3.4'
servers:
  - url: https://chatapi.example.com
paths:
  /api/chatstatus.php:
    get:
      description: chatstatus API
      parameters:
        - name: businessHoursId
          in: query
          schema:
            type: integer
          example: '5000'
        - name: chatEntryId
          in: query
          schema:
            type: integer
          example: '1001'
      responses:
        '200':
          description: Auto generated using Swagger Inspector
          content:
            application/json:
              schema:
                type: object
                properties:
                  chatStatus:
                    type: string
                  errorString:
                    type: string
                  errorCode:
                    type: integer
                  chatStatusReason:
                    type: string
                  cacheEngine:
                    type: string
                  chatTimer:
                    type: integer
                  debugLevel:
                    type: integer
              examples:
                'open': # 'open' example
                  value: >-
                    {"chatStatus":"open","chatStatusReason":"","chatTimer":10000,"errorCode":0,"errorString":"","debugLevel":0,"cacheEngine":"Redis"}
                'busy - agent availability': # 'busy' example - chatStatusReason of 'MinAgentCount'
                  value: >-
                    {"chatStatus":"busy","chatStatusReason":"MinAgentCount","chatTimer":0,"errorCode":0,"errorString":"","debugLevel":0,"cacheEngine":"Redis"}
                'closed - standard - reason: "week day closed"': 
                  value: >-
                    {"chatStatus":"closed","chatStatusReason":"Week Day closed reason","chatTimer":0,"errorCode":0,"errorString":"","debugLevel":0,"cacheEngine":"Redis"}
                'closed - reason: "Holiday"':
                  value: >-
                    {"chatStatus":"closed","chatStatusReason":"Holiday","chatTimer":0,"errorCode":0,"errorString":"","debugLevel":0,"cacheEngine":"Redis"}
                'debugLevel "1" example - busy': # same output as debug 0 - but browser should log output to the console
                  value: >-
                    {"chatStatus":"busy","chatStatusReason":"MinAgentCount","chatTimer":0,"errorCode":0,"errorString":"","debugLevel":1,"cacheEngine":"Redis"}
                'debugLevel "2" example - busy': # same output as debug 0 - but browser should log output to the console
                  value: >-
                    {"chatStatus":"busy","chatStatusReason":"MinAgentCount","chatTimer":0,"errorCode":0,"errorString":"","debugLevel":2,"cacheEngine":"Redis","cceBusinessHoursAPI":"From Source","eceCapacityAPI":"From Source","eceLiveSessionAPI":""}                 
                'debugLevel "3" example - busy': # same output as debug 0 - but browser should log output to the console
                  value: >-   
                    {"chatStatus":"busy","chatStatusReason":"MinAgentCount","chatTimer":0,"errorCode":0,"errorString":"","debugLevel":3,"cacheEngine":"Redis","cceBusinessHoursAPI":"From Source","eceCapacityAPI":"From Source","eceLiveSessionAPI":"","cceBusinessHoursObj":{"API":"cceBusinessHours","Name":"eirSupportChat_10_1_5_60","Status":"open","runTimeStatusReason":"Week Day open reason","ChatTimer":10000,"MinAgentCount":1,"MaxQueueDepth":5,"MaxWaitTime":60,"errorcode":0,"errorstring":"","httpcode":200,"runTimeStatus":1,"cache":false},"eceCapacityObj":{"API":"eceCapacity","Status":"busy","BusyReason":"MinAgentCount","MinAgentCount":1,"errorcode":0,"errorstring":"","httpcode":200,"AgentCount":0,"cache":false},"eceLiveSessionObj":null}
                'debugLevel "3" example - open': # same output as debug 0 - but browser should log output to the console
                  value: >-   
                    {"chatStatus":"open","chatStatusReason":"","chatTimer":10000,"errorCode":0,"errorString":"","debugLevel":3,"cacheEngine":"Redis","cceBusinessHoursAPI":"From Cache","eceCapacityAPI":"From Cache","eceLiveSessionAPI":"From Cache","cceBusinessHoursObj":{"API":"cceBusinessHours","Name":"eirSupportChat_10_1_5_60","Status":"open","runTimeStatusReason":"Week Day open reason","ChatTimer":10000,"MinAgentCount":1,"MaxQueueDepth":5,"MaxWaitTime":60,"errorcode":0,"errorstring":"","httpcode":200,"runTimeStatus":1,"cache":true},"eceCapacityObj":{"API":"eceCapacity","Status":"open","BusyReason":"","MinAgentCount":1,"errorcode":0,"errorstring":"","httpcode":200,"AgentCount":1,"cache":true},"eceLiveSessionObj":{"API":"eceLiveSession","Status":"open","BusyReason":"","errorcode":0,"errorstring":"","httpcode":200,"MaxQueueDepth":5,"MaxWaitTime":60,"queueDepth":0,"waitTime":6,"cache":true}}
        '404':
          description: client error - businessHoursId does not exist
          content:
            application/json:
              schema:
                type: object
                properties:
                  debuglevel:
                    type: integer
                  chatStatusReason:
                    type: string
                  chatstatus:
                    type: string
                  chatTimer:
                    type: string
                  errorcode:
                    type: integer
                  errorstring:
                    type: string
              examples:
                'invalid businessHoursId':
                  value: >-
                    {"chatStatus":"closed","chatStatusReason":"out of service","chatTimer":0,"errorCode":404,"errorString":"CCE BusinessHours HTTP Error.","debugLevel":0,"cacheEngine":"Redis"}                   

        '406':
          description: client error - missing or invalid parameters
          content:
            application/json:
              schema:
                type: object
                properties:
                  debuglevel:
                    type: integer
                  chatStatusReason:
                    type: string
                  chatstatus:
                    type: string
                  chatTimer:
                    type: string
                  errorcode:
                    type: integer
                  errorstring:
                    type: string
              examples:
                'businessHoursId Not set':
                  value: >-
                    {"chatstatus":"closed","chatStatusReason":"out of
                    service","chatTimer":"0","errorcode":406,"errorstring":"businessHoursId
                    not set.","debuglevel":0}                    
                'chatEntryId Not set':
                  value: >-
                    {"chatstatus":"closed","chatStatusReason":"out of service","chatTimer":"0","errorcode":406,"errorstring":"chatEntryId not set.","debuglevel":0}
                    
        '500':
          description: server error
          content:
            application/json:
              schema:
                type: object
                properties:
                  debuglevel:
                    type: integer
                  chatStatusReason:
                    type: string
                  chatstatus:
                    type: string
                  chatTimer:
                    type: string
                  errorcode:
                    type: integer
                  errorstring:
                    type: string
              examples:
                'Redis - Out of Service':
                  value: >-
                    {"chatstatus":"closed","chatStatusReason":"out of service","chatTimer":0,"errorcode":500,"errorstring":"Redis Connection Error.","debuglevel":0}
                'ECE - WebServer Out of Service':
                  value: >-
                    {"chatStatus":"closed","chatStatusReason":"out of service","chatTimer":0,"errorCode":500,"errorString":"ECE Capacity HTTP Error.","debugLevel":0,"cacheEngine":"Redis"}                
        '503':
          description: server error
          content:
            application/json:
              schema:
                type: object
                properties:
                  debuglevel:
                    type: integer
                  chatStatusReason:
                    type: string
                  chatstatus:
                    type: string
                  chatTimer:
                    type: string
                  errorcode:
                    type: integer
                  errorstring:
                    type: string
              examples:
                'CCE Business Hours API Server - Out of Service':
                  value: >-
                    {"chatStatus":"closed","chatStatusReason":"out of service","chatTimer":0,"errorCode":503,"errorString":"CCE BusinessHours HTTP Error.","debugLevel":0,"cacheEngine":"Redis"}

This will fail if there is an issue with Apache or Redis

probe.php
<?php
echo '<!DOCTYPE html><html><head><title>Chat API Probe</title></head><body><h1>Chat API Server Health Probe</h1>';
 
if (!class_exists('Redis')){
    http_response_code(500); //SET HTTP Response Code
    echo '<p>500 Error - Redis Module not installed</p></body></html>';
    exit();
}
 
try{
    $redis = new Redis();
    $redis->connect('127.0.0.1', 6379);
    //check whether server is running or not
    echo "<p>In Service: ".$redis->ping()."</p></body></html>";
}catch (Exception $e) {
    http_response_code(500); //SET HTTP Response Code
    //echo '<p>500 Error - Redis' . $e . '</p></body></html>';
    echo '<p>500 Error - Redis</p></body></html>';
    exit();
}
?>
  • vendors/cisco/uc/ece/chatstatusv3.txt
  • Last modified: 2024/11/05 14:17
  • by gerardorourke