chatStatus V3
ChatStatus V3
New Features
-
- 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
How to install Apache, PHP & Redis on a Ubuntu or Redhat Server
Legacy Versions
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.
Features
- 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)
Notes on Setting config
- 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
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 } }
APIs used to achieve this functionality
ECE - liveSessionStatus - Display chat option based on queue depth and wait time
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>
ECE - Agent Capacity
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>
CCE - Business Hours
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>
Apache Benchmark Tests
Note these test were done on a low end lab machine. 5000 Request made, with 500 concurrent Requests.
Results Summary
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
PHP Libraries
- Memcache
- Redis
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
Download
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 API Code
- 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; } ?>
Swagger YAML file
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"}
PHP Code to act as a health probe on the Chat API Server
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(); } ?>

