2016-11-21 22:32:22 +00:00
/*
2010-08-13 13:48:37 +00:00
* FreeSWITCH Modular Media Switching Software Library / Soft - Switch Application
2016-02-07 22:36:47 +00:00
* Copyright ( C ) 2005 - 2016 , Anthony Minessale II < anthm @ freeswitch . org >
2010-08-13 13:48:37 +00:00
*
* Version : MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 ( the " License " ) ; you may not use this file except in compliance with
* the License . You may obtain a copy of the License at
* http : //www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an " AS IS " basis ,
* WITHOUT WARRANTY OF ANY KIND , either express or implied . See the License
* for the specific language governing rights and limitations under the
* License .
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft - Switch Application
*
* The Initial Developer of the Original Code is
* Anthony Minessale II < anthm @ freeswitch . org >
* Portions created by the Initial Developer are Copyright ( C )
* the Initial Developer . All Rights Reserved .
*
* Contributor ( s ) :
2016-11-21 22:32:22 +00:00
*
2010-08-13 13:48:37 +00:00
* Marc Olivier Chouinard < mochouinard @ moctel . com >
2013-10-04 19:21:27 +00:00
* Emmanuel Schmidbauer < e . schmidbauer @ gmail . com >
2015-05-03 23:25:42 +00:00
* Í talo Rossi < italorossib @ gmail . com >
2019-08-14 14:28:37 +00:00
* Fernando Santos < fernando @ nextbilling . com . br >
2010-08-13 13:48:37 +00:00
*
* mod_callcenter . c - - Call Center Module
*
*/
# include <switch.h>
# define CALLCENTER_EVENT "callcenter::info"
# define CC_AGENT_TYPE_CALLBACK "Callback"
2010-08-26 01:40:33 +00:00
# define CC_AGENT_TYPE_UUID_STANDBY "uuid-standby"
2010-08-14 16:22:11 +00:00
# define CC_SQLITE_DB_NAME "callcenter"
2017-08-10 05:36:58 +00:00
# define CC_APP_KEY "mod_callcenter"
2010-08-13 13:48:37 +00:00
2011-04-28 05:29:28 +00:00
2010-08-13 13:48:37 +00:00
/* Prototypes */
SWITCH_MODULE_SHUTDOWN_FUNCTION ( mod_callcenter_shutdown ) ;
SWITCH_MODULE_RUNTIME_FUNCTION ( mod_callcenter_runtime ) ;
SWITCH_MODULE_LOAD_FUNCTION ( mod_callcenter_load ) ;
2016-11-21 22:32:22 +00:00
/* SWITCH_MODULE_DEFINITION(name, load, shutdown, runtime)
2010-08-13 13:48:37 +00:00
* Defines a switch_loadable_module_function_table_t and a static const char [ ] modname
*/
SWITCH_MODULE_DEFINITION ( mod_callcenter , mod_callcenter_load , mod_callcenter_shutdown , NULL ) ;
2017-07-18 16:25:22 +00:00
static switch_status_t load_agent ( const char * agent_name , switch_event_t * params , switch_xml_t x_agents_cfg ) ;
static switch_status_t load_tiers ( switch_bool_t load_all , const char * queue_name , const char * agent_name , switch_event_t * params , switch_xml_t x_tiers_cfg ) ;
2010-08-13 13:48:37 +00:00
static const char * global_cf = " callcenter.conf " ;
2010-09-08 04:07:19 +00:00
struct cc_status_table {
const char * name ;
int status ;
} ;
struct cc_state_table {
const char * name ;
int state ;
} ;
2010-08-13 13:48:37 +00:00
typedef enum {
CC_STATUS_SUCCESS ,
CC_STATUS_FALSE ,
CC_STATUS_AGENT_NOT_FOUND ,
CC_STATUS_QUEUE_NOT_FOUND ,
CC_STATUS_AGENT_ALREADY_EXIST ,
CC_STATUS_AGENT_INVALID_TYPE ,
CC_STATUS_AGENT_INVALID_STATUS ,
CC_STATUS_AGENT_INVALID_STATE ,
CC_STATUS_TIER_ALREADY_EXIST ,
CC_STATUS_TIER_NOT_FOUND ,
CC_STATUS_TIER_INVALID_STATE ,
CC_STATUS_INVALID_KEY
} cc_status_t ;
typedef enum {
CC_TIER_STATE_UNKNOWN = 0 ,
CC_TIER_STATE_NO_ANSWER = 1 ,
CC_TIER_STATE_READY = 2 ,
CC_TIER_STATE_OFFERING = 3 ,
CC_TIER_STATE_ACTIVE_INBOUND = 4 ,
CC_TIER_STATE_STANDBY = 5
} cc_tier_state_t ;
2010-09-08 04:07:19 +00:00
static struct cc_state_table STATE_CHART [ ] = {
2010-08-13 13:48:37 +00:00
{ " Unknown " , CC_TIER_STATE_UNKNOWN } ,
{ " No Answer " , CC_TIER_STATE_NO_ANSWER } ,
{ " Ready " , CC_TIER_STATE_READY } ,
{ " Offering " , CC_TIER_STATE_OFFERING } ,
{ " Active Inbound " , CC_TIER_STATE_ACTIVE_INBOUND } ,
{ " Standby " , CC_TIER_STATE_STANDBY } ,
{ NULL , 0 }
} ;
typedef enum {
CC_AGENT_STATUS_UNKNOWN = 0 ,
CC_AGENT_STATUS_LOGGED_OUT = 1 ,
CC_AGENT_STATUS_AVAILABLE = 2 ,
CC_AGENT_STATUS_AVAILABLE_ON_DEMAND = 3 ,
2017-07-18 16:09:39 +00:00
CC_AGENT_STATUS_ON_BREAK = 4
2010-08-13 13:48:37 +00:00
} cc_agent_status_t ;
2010-09-08 04:07:19 +00:00
static struct cc_status_table AGENT_STATUS_CHART [ ] = {
2010-08-13 13:48:37 +00:00
{ " Unknown " , CC_AGENT_STATUS_UNKNOWN } ,
{ " Logged Out " , CC_AGENT_STATUS_LOGGED_OUT } ,
{ " Available " , CC_AGENT_STATUS_AVAILABLE } ,
{ " Available (On Demand) " , CC_AGENT_STATUS_AVAILABLE_ON_DEMAND } ,
{ " On Break " , CC_AGENT_STATUS_ON_BREAK } ,
{ NULL , 0 }
} ;
typedef enum {
CC_AGENT_STATE_UNKNOWN = 0 ,
CC_AGENT_STATE_WAITING = 1 ,
CC_AGENT_STATE_RECEIVING = 2 ,
CC_AGENT_STATE_IN_A_QUEUE_CALL = 3 ,
2015-05-03 04:10:02 +00:00
CC_AGENT_STATE_IDLE = 4 ,
CC_AGENT_STATE_RESERVED = 5
2010-08-13 13:48:37 +00:00
} cc_agent_state_t ;
2010-09-08 04:07:19 +00:00
static struct cc_state_table AGENT_STATE_CHART [ ] = {
2010-08-13 13:48:37 +00:00
{ " Unknown " , CC_AGENT_STATE_UNKNOWN } ,
{ " Waiting " , CC_AGENT_STATE_WAITING } ,
{ " Receiving " , CC_AGENT_STATE_RECEIVING } ,
{ " In a queue call " , CC_AGENT_STATE_IN_A_QUEUE_CALL } ,
{ " Idle " , CC_AGENT_STATE_IDLE } ,
2015-05-03 04:10:02 +00:00
{ " Reserved " , CC_AGENT_STATE_RESERVED } ,
2010-08-13 13:48:37 +00:00
{ NULL , 0 }
} ;
typedef enum {
CC_MEMBER_STATE_UNKNOWN = 0 ,
CC_MEMBER_STATE_WAITING = 1 ,
CC_MEMBER_STATE_TRYING = 2 ,
CC_MEMBER_STATE_ANSWERED = 3 ,
2010-08-26 10:19:58 +00:00
CC_MEMBER_STATE_ABANDONED = 4
2010-08-13 13:48:37 +00:00
} cc_member_state_t ;
2010-09-08 04:07:19 +00:00
static struct cc_state_table MEMBER_STATE_CHART [ ] = {
2010-08-13 13:48:37 +00:00
{ " Unknown " , CC_MEMBER_STATE_UNKNOWN } ,
{ " Waiting " , CC_MEMBER_STATE_WAITING } ,
{ " Trying " , CC_MEMBER_STATE_TRYING } ,
{ " Answered " , CC_MEMBER_STATE_ANSWERED } ,
2010-08-26 10:19:58 +00:00
{ " Abandoned " , CC_MEMBER_STATE_ABANDONED } ,
2010-08-13 13:48:37 +00:00
{ NULL , 0 }
} ;
2010-09-08 04:07:19 +00:00
struct cc_member_cancel_reason_table {
const char * name ;
int reason ;
} ;
typedef enum {
CC_MEMBER_CANCEL_REASON_NONE ,
CC_MEMBER_CANCEL_REASON_TIMEOUT ,
2011-03-14 16:31:56 +00:00
CC_MEMBER_CANCEL_REASON_NO_AGENT_TIMEOUT ,
2016-10-05 02:07:42 +00:00
CC_MEMBER_CANCEL_REASON_BREAK_OUT ,
CC_MEMBER_CANCEL_REASON_EXIT_WITH_KEY
2010-09-08 04:07:19 +00:00
} cc_member_cancel_reason_t ;
static struct cc_member_cancel_reason_table MEMBER_CANCEL_REASON_CHART [ ] = {
{ " NONE " , CC_MEMBER_CANCEL_REASON_NONE } ,
{ " TIMEOUT " , CC_MEMBER_CANCEL_REASON_TIMEOUT } ,
{ " NO_AGENT_TIMEOUT " , CC_MEMBER_CANCEL_REASON_NO_AGENT_TIMEOUT } ,
2011-03-14 16:31:56 +00:00
{ " BREAK_OUT " , CC_MEMBER_CANCEL_REASON_BREAK_OUT } ,
2016-10-05 02:07:42 +00:00
{ " EXIT_WITH_KEY " , CC_MEMBER_CANCEL_REASON_EXIT_WITH_KEY } ,
2010-09-08 04:07:19 +00:00
{ NULL , 0 }
} ;
2017-08-10 05:36:58 +00:00
typedef enum {
2017-09-13 20:39:43 +00:00
CC_APP_AGENT_CONNECTING = ( 1 < < 0 ) ,
CC_APP_SHOULD_BREAK = ( 1 < < 1 )
2017-08-10 05:36:58 +00:00
} cc_app_flag_t ;
2010-08-13 13:48:37 +00:00
static char members_sql [ ] =
" CREATE TABLE members ( \n "
2010-08-26 10:19:58 +00:00
" queue VARCHAR(255), \n "
2019-08-14 14:28:37 +00:00
" instance_id VARCHAR(255), \n "
2010-08-26 10:19:58 +00:00
" uuid VARCHAR(255) NOT NULL DEFAULT '', \n "
2011-03-31 09:17:04 +00:00
" session_uuid VARCHAR(255) NOT NULL DEFAULT '', \n "
2011-03-31 09:22:50 +00:00
" cid_number VARCHAR(255), \n "
" cid_name VARCHAR(255), \n "
2010-08-26 10:19:58 +00:00
" system_epoch INTEGER NOT NULL DEFAULT 0, \n "
" joined_epoch INTEGER NOT NULL DEFAULT 0, \n "
2010-08-27 02:49:02 +00:00
" rejoined_epoch INTEGER NOT NULL DEFAULT 0, \n "
2010-08-26 10:19:58 +00:00
" bridge_epoch INTEGER NOT NULL DEFAULT 0, \n "
" abandoned_epoch INTEGER NOT NULL DEFAULT 0, \n "
" base_score INTEGER NOT NULL DEFAULT 0, \n "
" skill_score INTEGER NOT NULL DEFAULT 0, \n "
" serving_agent VARCHAR(255), \n "
" serving_system VARCHAR(255), \n "
" state VARCHAR(255) \n " " ); \n " ;
2016-11-21 22:32:22 +00:00
/* Member State
2010-08-13 13:48:37 +00:00
Waiting
Answered
*/
static char agents_sql [ ] =
" CREATE TABLE agents ( \n "
" name VARCHAR(255), \n "
2019-08-14 14:28:37 +00:00
" instance_id VARCHAR(255), \n "
2010-08-13 13:48:37 +00:00
" uuid VARCHAR(255), \n "
2010-08-26 01:40:33 +00:00
" type VARCHAR(255), \n " /* Callback , Dial in...*/
2017-06-28 19:43:16 +00:00
" contact VARCHAR(1024), \n "
2010-08-13 13:48:37 +00:00
" status VARCHAR(255), \n "
/*User Personal Status
Available
On Break
Logged Out
*/
" state VARCHAR(255), \n "
/* User Personal State
Waiting
Receiving
In a queue call
*/
" max_no_answer INTEGER NOT NULL DEFAULT 0, \n "
" wrap_up_time INTEGER NOT NULL DEFAULT 0, \n "
2010-08-20 01:43:07 +00:00
" reject_delay_time INTEGER NOT NULL DEFAULT 0, \n "
" busy_delay_time INTEGER NOT NULL DEFAULT 0, \n "
2011-03-12 16:58:35 +00:00
" no_answer_delay_time INTEGER NOT NULL DEFAULT 0, \n "
2010-08-13 13:48:37 +00:00
" last_bridge_start INTEGER NOT NULL DEFAULT 0, \n "
" last_bridge_end INTEGER NOT NULL DEFAULT 0, \n "
2016-11-21 22:32:22 +00:00
" last_offered_call INTEGER NOT NULL DEFAULT 0, \n "
2010-08-13 13:48:37 +00:00
" last_status_change INTEGER NOT NULL DEFAULT 0, \n "
" no_answer_count INTEGER NOT NULL DEFAULT 0, \n "
" calls_answered INTEGER NOT NULL DEFAULT 0, \n "
2010-08-20 01:43:07 +00:00
" talk_time INTEGER NOT NULL DEFAULT 0, \n "
2016-10-05 02:07:42 +00:00
" ready_time INTEGER NOT NULL DEFAULT 0, \n "
2017-07-18 16:09:39 +00:00
" external_calls_count INTEGER NOT NULL DEFAULT 0 \n "
2010-08-13 13:48:37 +00:00
" ); \n " ;
static char tiers_sql [ ] =
" CREATE TABLE tiers ( \n "
" queue VARCHAR(255), \n "
" agent VARCHAR(255), \n "
" state VARCHAR(255), \n "
/*
2016-11-21 22:32:22 +00:00
Agent State :
2010-08-13 13:48:37 +00:00
Ready
Active inbound
Wrap - up inbound
Standby
No Answer
Offering
*/
" level INTEGER NOT NULL DEFAULT 1, \n "
" position INTEGER NOT NULL DEFAULT 1 \n " " ); \n " ;
2010-08-26 10:19:58 +00:00
static switch_xml_config_int_options_t config_int_0_86400 = { SWITCH_TRUE , 0 , SWITCH_TRUE , 86400 } ;
2010-08-13 13:48:37 +00:00
2011-04-23 21:01:35 +00:00
/* TODO This is temporary until we either move it to the core, or use it differently in the module */
switch_time_t local_epoch_time_now ( switch_time_t * t )
{
switch_time_t now = switch_micro_time_now ( ) / 1000000 ; /* APR_USEC_PER_SEC */
if ( t ) {
* t = now ;
}
return now ;
}
2010-08-13 13:48:37 +00:00
const char * cc_tier_state2str ( cc_tier_state_t state )
{
uint8_t x ;
const char * str = " Unknown " ;
2010-09-08 04:07:19 +00:00
for ( x = 0 ; x < ( sizeof ( STATE_CHART ) / sizeof ( struct cc_state_table ) ) - 1 ; x + + ) {
2010-08-13 13:48:37 +00:00
if ( STATE_CHART [ x ] . state = = state ) {
str = STATE_CHART [ x ] . name ;
break ;
}
}
return str ;
}
cc_tier_state_t cc_tier_str2state ( const char * str )
{
uint8_t x ;
cc_tier_state_t state = CC_TIER_STATE_UNKNOWN ;
2010-09-08 04:07:19 +00:00
for ( x = 0 ; x < ( sizeof ( STATE_CHART ) / sizeof ( struct cc_state_table ) ) - 1 & & STATE_CHART [ x ] . name ; x + + ) {
2010-08-13 13:48:37 +00:00
if ( ! strcasecmp ( STATE_CHART [ x ] . name , str ) ) {
state = STATE_CHART [ x ] . state ;
break ;
}
}
return state ;
}
2010-09-08 04:07:19 +00:00
const char * cc_member_cancel_reason2str ( cc_member_cancel_reason_t reason )
{
uint8_t x ;
const char * str = " NONE " ;
for ( x = 0 ; x < ( sizeof ( MEMBER_CANCEL_REASON_CHART ) / sizeof ( struct cc_member_cancel_reason_table ) ) - 1 ; x + + ) {
if ( MEMBER_CANCEL_REASON_CHART [ x ] . reason = = reason ) {
str = MEMBER_CANCEL_REASON_CHART [ x ] . name ;
break ;
}
}
return str ;
}
cc_member_cancel_reason_t cc_member_cancel_str2reason ( const char * str )
{
uint8_t x ;
cc_member_cancel_reason_t reason = CC_MEMBER_CANCEL_REASON_NONE ;
for ( x = 0 ; x < ( sizeof ( MEMBER_CANCEL_REASON_CHART ) / sizeof ( struct cc_member_cancel_reason_table ) ) - 1 & & MEMBER_CANCEL_REASON_CHART [ x ] . name ; x + + ) {
if ( ! strcasecmp ( MEMBER_CANCEL_REASON_CHART [ x ] . name , str ) ) {
reason = MEMBER_CANCEL_REASON_CHART [ x ] . reason ;
break ;
}
}
return reason ;
}
2010-08-13 13:48:37 +00:00
const char * cc_agent_status2str ( cc_agent_status_t status )
{
uint8_t x ;
const char * str = " Unknown " ;
2010-09-08 04:07:19 +00:00
for ( x = 0 ; x < ( sizeof ( AGENT_STATUS_CHART ) / sizeof ( struct cc_status_table ) ) - 1 ; x + + ) {
2010-08-13 13:48:37 +00:00
if ( AGENT_STATUS_CHART [ x ] . status = = status ) {
str = AGENT_STATUS_CHART [ x ] . name ;
break ;
}
}
return str ;
}
cc_agent_status_t cc_agent_str2status ( const char * str )
{
uint8_t x ;
cc_agent_status_t status = CC_AGENT_STATUS_UNKNOWN ;
2010-09-08 04:07:19 +00:00
for ( x = 0 ; x < ( sizeof ( AGENT_STATUS_CHART ) / sizeof ( struct cc_status_table ) ) - 1 & & AGENT_STATUS_CHART [ x ] . name ; x + + ) {
2010-08-13 13:48:37 +00:00
if ( ! strcasecmp ( AGENT_STATUS_CHART [ x ] . name , str ) ) {
status = AGENT_STATUS_CHART [ x ] . status ;
break ;
}
}
return status ;
}
const char * cc_agent_state2str ( cc_agent_state_t state )
{
uint8_t x ;
const char * str = " Unknown " ;
2010-09-08 04:07:19 +00:00
for ( x = 0 ; x < ( sizeof ( AGENT_STATE_CHART ) / sizeof ( struct cc_state_table ) ) - 1 ; x + + ) {
2010-08-13 13:48:37 +00:00
if ( AGENT_STATE_CHART [ x ] . state = = state ) {
str = AGENT_STATE_CHART [ x ] . name ;
break ;
}
}
return str ;
}
cc_agent_state_t cc_agent_str2state ( const char * str )
{
uint8_t x ;
cc_agent_state_t state = CC_AGENT_STATE_UNKNOWN ;
2010-09-08 04:07:19 +00:00
for ( x = 0 ; x < ( sizeof ( AGENT_STATE_CHART ) / sizeof ( struct cc_state_table ) ) - 1 & & AGENT_STATE_CHART [ x ] . name ; x + + ) {
2010-08-13 13:48:37 +00:00
if ( ! strcasecmp ( AGENT_STATE_CHART [ x ] . name , str ) ) {
state = AGENT_STATE_CHART [ x ] . state ;
break ;
}
}
return state ;
}
const char * cc_member_state2str ( cc_member_state_t state )
{
uint8_t x ;
const char * str = " Unknown " ;
2010-09-08 04:07:19 +00:00
for ( x = 0 ; x < ( sizeof ( MEMBER_STATE_CHART ) / sizeof ( struct cc_state_table ) ) - 1 ; x + + ) {
2010-08-13 13:48:37 +00:00
if ( MEMBER_STATE_CHART [ x ] . state = = state ) {
str = MEMBER_STATE_CHART [ x ] . name ;
break ;
}
}
return str ;
}
cc_member_state_t cc_member_str2state ( const char * str )
{
uint8_t x ;
cc_member_state_t state = CC_MEMBER_STATE_UNKNOWN ;
2010-09-08 04:07:19 +00:00
for ( x = 0 ; x < ( sizeof ( MEMBER_STATE_CHART ) / sizeof ( struct cc_state_table ) ) - 1 & & MEMBER_STATE_CHART [ x ] . name ; x + + ) {
2010-08-13 13:48:37 +00:00
if ( ! strcasecmp ( MEMBER_STATE_CHART [ x ] . name , str ) ) {
state = MEMBER_STATE_CHART [ x ] . state ;
break ;
}
}
return state ;
}
typedef enum {
PFLAG_DESTROY = 1 < < 0
} cc_flags_t ;
static struct {
2017-07-18 14:34:07 +00:00
switch_hash_t * queue_hash ;
2017-07-18 16:25:22 +00:00
int debug ;
2010-08-14 16:22:11 +00:00
char * odbc_dsn ;
2010-10-02 03:01:03 +00:00
char * dbname ;
2017-07-12 16:01:06 +00:00
char * cc_instance_id ;
2015-05-03 04:10:02 +00:00
switch_bool_t reserve_agents ;
2016-06-09 19:53:21 +00:00
switch_bool_t truncate_tiers ;
switch_bool_t truncate_agents ;
2018-01-24 19:46:39 +00:00
switch_bool_t global_database_lock ;
2017-07-18 16:25:22 +00:00
int32_t threads ;
int32_t running ;
2010-08-13 13:48:37 +00:00
switch_mutex_t * mutex ;
switch_memory_pool_t * pool ;
2017-07-18 16:25:22 +00:00
switch_event_node_t * node ;
2019-04-15 14:15:43 +00:00
int agent_originate_timeout ;
2017-07-18 16:25:22 +00:00
} globals ;
2010-08-13 13:48:37 +00:00
# define CC_QUEUE_CONFIGITEM_COUNT 100
struct cc_queue {
char * name ;
2017-07-18 16:25:22 +00:00
2010-08-13 13:48:37 +00:00
char * strategy ;
char * moh ;
2014-02-24 13:38:31 +00:00
char * announce ;
uint32_t announce_freq ;
2010-08-13 13:48:37 +00:00
char * record_template ;
char * time_base_score ;
2016-04-20 08:15:34 +00:00
uint32_t ring_progressively_delay ;
2010-09-08 04:07:19 +00:00
2010-08-26 01:40:33 +00:00
switch_bool_t tier_rules_apply ;
uint32_t tier_rule_wait_second ;
switch_bool_t tier_rule_wait_multiply_level ;
switch_bool_t tier_rule_no_agent_no_wait ;
2010-09-08 04:07:19 +00:00
2010-08-26 10:19:58 +00:00
uint32_t discard_abandoned_after ;
switch_bool_t abandoned_resume_allowed ;
2010-08-26 01:40:33 +00:00
2010-09-08 04:07:19 +00:00
uint32_t max_wait_time ;
uint32_t max_wait_time_with_no_agent ;
2010-10-30 02:41:14 +00:00
uint32_t max_wait_time_with_no_agent_time_reached ;
2016-10-05 02:07:42 +00:00
char * agent_no_answer_status ;
2016-02-07 22:36:47 +00:00
uint32_t calls_answered ;
uint32_t calls_abandoned ;
2010-09-08 04:07:19 +00:00
2010-08-13 13:48:37 +00:00
switch_mutex_t * mutex ;
switch_thread_rwlock_t * rwlock ;
switch_memory_pool_t * pool ;
uint32_t flags ;
2010-09-08 04:07:19 +00:00
switch_time_t last_agent_exist ;
switch_time_t last_agent_exist_check ;
2016-10-05 02:07:42 +00:00
switch_bool_t skip_agents_with_external_calls ;
2010-08-13 13:48:37 +00:00
switch_xml_config_item_t config [ CC_QUEUE_CONFIGITEM_COUNT ] ;
switch_xml_config_string_options_t config_str_pool ;
} ;
typedef struct cc_queue cc_queue_t ;
2017-07-18 16:25:22 +00:00
static void cc_send_presence ( const char * queue_name ) ;
2016-10-05 02:07:42 +00:00
2017-07-18 16:25:22 +00:00
static void free_queue ( cc_queue_t * queue )
2010-08-13 13:48:37 +00:00
{
2017-07-18 16:25:22 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Destroying Profile %s \n " , queue - > name ) ;
2010-08-13 13:48:37 +00:00
switch_core_destroy_memory_pool ( & queue - > pool ) ;
}
static void queue_rwunlock ( cc_queue_t * queue )
{
2017-07-18 16:25:22 +00:00
switch_thread_rwlock_unlock ( queue - > rwlock ) ;
if ( switch_test_flag ( queue , PFLAG_DESTROY ) ) {
if ( switch_thread_rwlock_trywrlock ( queue - > rwlock ) = = SWITCH_STATUS_SUCCESS ) {
switch_thread_rwlock_unlock ( queue - > rwlock ) ;
free_queue ( queue ) ;
2017-07-18 14:34:07 +00:00
}
}
}
2017-07-18 16:25:22 +00:00
static void destroy_queue ( const char * queue_name )
2010-08-13 23:15:24 +00:00
{
cc_queue_t * queue = NULL ;
2017-07-18 16:25:22 +00:00
switch_mutex_lock ( globals . mutex ) ;
if ( ( queue = switch_core_hash_find ( globals . queue_hash , queue_name ) ) ) {
switch_core_hash_delete ( globals . queue_hash , queue_name ) ;
2010-08-13 23:15:24 +00:00
}
2017-07-18 16:25:22 +00:00
switch_mutex_unlock ( globals . mutex ) ;
2010-08-13 23:15:24 +00:00
if ( ! queue ) {
2017-07-18 16:25:22 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " [%s] Invalid queue \n " , queue_name ) ;
return ;
2010-08-13 23:15:24 +00:00
}
2014-05-16 21:03:59 +00:00
if ( switch_thread_rwlock_trywrlock ( queue - > rwlock ) ! = SWITCH_STATUS_SUCCESS ) {
/* Lock failed, set the destroy flag so it'll be destroyed whenever its not in use anymore */
switch_set_flag ( queue , PFLAG_DESTROY ) ;
2017-07-18 16:25:22 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " [%s] queue is in use, memory will be freed whenever its no longer in use \n " ,
queue - > name ) ;
return ;
2010-08-13 23:15:24 +00:00
}
2017-07-18 16:25:22 +00:00
free_queue ( queue ) ;
2010-08-13 23:15:24 +00:00
}
2017-07-18 16:25:22 +00:00
switch_cache_db_handle_t * cc_get_db_handle ( void )
2010-08-13 13:48:37 +00:00
{
switch_cache_db_handle_t * dbh = NULL ;
2012-10-10 01:20:32 +00:00
char * dsn ;
2016-11-21 22:32:22 +00:00
2017-07-18 16:25:22 +00:00
if ( ! zstr ( globals . odbc_dsn ) ) {
dsn = globals . odbc_dsn ;
2010-08-13 13:48:37 +00:00
} else {
2017-07-18 16:25:22 +00:00
dsn = globals . dbname ;
2012-10-10 01:20:32 +00:00
}
if ( switch_cache_db_get_db_handle_dsn ( & dbh , dsn ) ! = SWITCH_STATUS_SUCCESS ) {
dbh = NULL ;
2010-08-13 13:48:37 +00:00
}
2016-11-21 22:32:22 +00:00
2012-10-10 01:20:32 +00:00
return dbh ;
2010-08-13 13:48:37 +00:00
}
/*!
2016-11-21 22:32:22 +00:00
* \ brief Sets the queue ' s configuration instructions
2010-08-13 13:48:37 +00:00
*/
cc_queue_t * queue_set_config ( cc_queue_t * queue )
{
int i = 0 ;
queue - > config_str_pool . pool = queue - > pool ;
/*
2016-11-21 22:32:22 +00:00
SWITCH _CONFIG_SET_ITEM ( item , " key " , type , flags ,
2010-08-13 13:48:37 +00:00
pointer , default , options , help_syntax , help_description )
*/
2010-08-13 23:15:24 +00:00
SWITCH_CONFIG_SET_ITEM ( queue - > config [ i + + ] , " strategy " , SWITCH_CONFIG_STRING , 0 , & queue - > strategy , " longest-idle-agent " , & queue - > config_str_pool , NULL , NULL ) ;
2010-08-13 13:48:37 +00:00
SWITCH_CONFIG_SET_ITEM ( queue - > config [ i + + ] , " moh-sound " , SWITCH_CONFIG_STRING , 0 , & queue - > moh , NULL , & queue - > config_str_pool , NULL , NULL ) ;
2014-02-24 13:38:31 +00:00
SWITCH_CONFIG_SET_ITEM ( queue - > config [ i + + ] , " announce-sound " , SWITCH_CONFIG_STRING , 0 , & queue - > announce , NULL , & queue - > config_str_pool , NULL , NULL ) ;
SWITCH_CONFIG_SET_ITEM ( queue - > config [ i + + ] , " announce-frequency " , SWITCH_CONFIG_INT , 0 , & queue - > announce_freq , 0 , & config_int_0_86400 , NULL , NULL ) ;
2010-08-13 13:48:37 +00:00
SWITCH_CONFIG_SET_ITEM ( queue - > config [ i + + ] , " record-template " , SWITCH_CONFIG_STRING , 0 , & queue - > record_template , NULL , & queue - > config_str_pool , NULL , NULL ) ;
SWITCH_CONFIG_SET_ITEM ( queue - > config [ i + + ] , " time-base-score " , SWITCH_CONFIG_STRING , 0 , & queue - > time_base_score , " queue " , & queue - > config_str_pool , NULL , NULL ) ;
2016-04-20 08:15:34 +00:00
SWITCH_CONFIG_SET_ITEM ( queue - > config [ i + + ] , " ring-progressively-delay " , SWITCH_CONFIG_INT , 0 , & queue - > ring_progressively_delay , NULL , & config_int_0_86400 , NULL , NULL ) ;
2010-09-08 04:07:19 +00:00
2010-08-26 01:40:33 +00:00
SWITCH_CONFIG_SET_ITEM ( queue - > config [ i + + ] , " tier-rules-apply " , SWITCH_CONFIG_BOOL , 0 , & queue - > tier_rules_apply , SWITCH_FALSE , NULL , NULL , NULL ) ;
2010-08-26 10:19:58 +00:00
SWITCH_CONFIG_SET_ITEM ( queue - > config [ i + + ] , " tier-rule-wait-second " , SWITCH_CONFIG_INT , 0 , & queue - > tier_rule_wait_second , 0 , & config_int_0_86400 , NULL , NULL ) ;
2010-08-26 01:40:33 +00:00
SWITCH_CONFIG_SET_ITEM ( queue - > config [ i + + ] , " tier-rule-wait-multiply-level " , SWITCH_CONFIG_BOOL , 0 , & queue - > tier_rule_wait_multiply_level , SWITCH_FALSE , NULL , NULL , NULL ) ;
SWITCH_CONFIG_SET_ITEM ( queue - > config [ i + + ] , " tier-rule-no-agent-no-wait " , SWITCH_CONFIG_BOOL , 0 , & queue - > tier_rule_no_agent_no_wait , SWITCH_TRUE , NULL , NULL , NULL ) ;
2010-08-26 10:19:58 +00:00
SWITCH_CONFIG_SET_ITEM ( queue - > config [ i + + ] , " discard-abandoned-after " , SWITCH_CONFIG_INT , 0 , & queue - > discard_abandoned_after , 60 , & config_int_0_86400 , NULL , NULL ) ;
SWITCH_CONFIG_SET_ITEM ( queue - > config [ i + + ] , " abandoned-resume-allowed " , SWITCH_CONFIG_BOOL , 0 , & queue - > abandoned_resume_allowed , SWITCH_FALSE , NULL , NULL , NULL ) ;
2010-08-13 13:48:37 +00:00
2010-09-08 04:07:19 +00:00
SWITCH_CONFIG_SET_ITEM ( queue - > config [ i + + ] , " max-wait-time " , SWITCH_CONFIG_INT , 0 , & queue - > max_wait_time , 0 , & config_int_0_86400 , NULL , NULL ) ;
SWITCH_CONFIG_SET_ITEM ( queue - > config [ i + + ] , " max-wait-time-with-no-agent " , SWITCH_CONFIG_INT , 0 , & queue - > max_wait_time_with_no_agent , 0 , & config_int_0_86400 , NULL , NULL ) ;
2013-01-27 20:06:43 +00:00
SWITCH_CONFIG_SET_ITEM ( queue - > config [ i + + ] , " max-wait-time-with-no-agent-time-reached " , SWITCH_CONFIG_INT , 0 , & queue - > max_wait_time_with_no_agent_time_reached , 5 , & config_int_0_86400 , NULL , NULL ) ;
2010-09-08 04:07:19 +00:00
2016-10-05 02:07:42 +00:00
SWITCH_CONFIG_SET_ITEM ( queue - > config [ i + + ] , " agent-no-answer-status " , SWITCH_CONFIG_STRING , 0 , & queue - > agent_no_answer_status , cc_agent_status2str ( CC_AGENT_STATUS_ON_BREAK ) , & queue - > config_str_pool , NULL , NULL ) ;
SWITCH_CONFIG_SET_ITEM ( queue - > config [ i + + ] , " skip-agents-with-external-calls " , SWITCH_CONFIG_BOOL , 0 , & queue - > skip_agents_with_external_calls , SWITCH_TRUE , NULL , NULL , NULL ) ;
2010-08-13 13:48:37 +00:00
switch_assert ( i < CC_QUEUE_CONFIGITEM_COUNT ) ;
return queue ;
}
2017-07-18 16:25:22 +00:00
static int cc_execute_sql_affected_rows ( char * sql ) {
2015-05-03 04:10:02 +00:00
switch_cache_db_handle_t * dbh = NULL ;
2016-01-31 01:35:32 +00:00
int res = 0 ;
2017-07-18 16:25:22 +00:00
if ( ! ( dbh = cc_get_db_handle ( ) ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Error Opening DB \n " ) ;
2015-05-03 04:10:02 +00:00
return - 1 ;
}
switch_cache_db_execute_sql ( dbh , sql , NULL ) ;
2016-01-31 01:35:32 +00:00
res = switch_cache_db_affected_rows ( dbh ) ;
switch_cache_db_release_db_handle ( & dbh ) ;
return res ;
2015-05-03 04:10:02 +00:00
}
2010-08-13 13:48:37 +00:00
2017-07-18 16:25:22 +00:00
char * cc_execute_sql2str ( cc_queue_t * queue , switch_mutex_t * mutex , char * sql , char * resbuf , size_t len )
2010-08-13 13:48:37 +00:00
{
char * ret = NULL ;
switch_cache_db_handle_t * dbh = NULL ;
2018-01-24 19:46:39 +00:00
if ( globals . global_database_lock ) {
if ( mutex ) {
switch_mutex_lock ( mutex ) ;
} else {
switch_mutex_lock ( globals . mutex ) ;
}
2010-08-13 13:48:37 +00:00
}
2017-07-18 16:25:22 +00:00
if ( ! ( dbh = cc_get_db_handle ( ) ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Error Opening DB \n " ) ;
2013-07-06 17:52:21 +00:00
goto end ;
}
2010-08-13 13:48:37 +00:00
ret = switch_cache_db_execute_sql2str ( dbh , sql , resbuf , len , NULL ) ;
2013-07-06 17:52:21 +00:00
end :
switch_cache_db_release_db_handle ( & dbh ) ;
2018-01-24 19:46:39 +00:00
if ( globals . global_database_lock ) {
if ( mutex ) {
switch_mutex_unlock ( mutex ) ;
} else {
switch_mutex_unlock ( globals . mutex ) ;
}
2010-08-13 13:48:37 +00:00
}
return ret ;
}
2017-07-18 16:25:22 +00:00
static switch_status_t cc_execute_sql ( cc_queue_t * queue , char * sql , switch_mutex_t * mutex )
2010-08-13 13:48:37 +00:00
{
switch_cache_db_handle_t * dbh = NULL ;
switch_status_t status = SWITCH_STATUS_FALSE ;
2018-01-24 19:46:39 +00:00
if ( globals . global_database_lock ) {
if ( mutex ) {
switch_mutex_lock ( mutex ) ;
} else {
switch_mutex_lock ( globals . mutex ) ;
}
2010-08-13 13:48:37 +00:00
}
2017-07-18 16:25:22 +00:00
if ( ! ( dbh = cc_get_db_handle ( ) ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Error Opening DB \n " ) ;
2010-08-13 13:48:37 +00:00
goto end ;
}
status = switch_cache_db_execute_sql ( dbh , sql , NULL ) ;
end :
switch_cache_db_release_db_handle ( & dbh ) ;
2018-01-24 19:46:39 +00:00
if ( globals . global_database_lock ) {
if ( mutex ) {
switch_mutex_unlock ( mutex ) ;
} else {
switch_mutex_unlock ( globals . mutex ) ;
}
2010-08-13 13:48:37 +00:00
}
return status ;
}
2017-07-18 16:25:22 +00:00
static switch_bool_t cc_execute_sql_callback ( cc_queue_t * queue , switch_mutex_t * mutex , char * sql , switch_core_db_callback_func_t callback , void * pdata )
2010-08-13 13:48:37 +00:00
{
switch_bool_t ret = SWITCH_FALSE ;
char * errmsg = NULL ;
switch_cache_db_handle_t * dbh = NULL ;
2018-01-24 19:46:39 +00:00
if ( globals . global_database_lock ) {
if ( mutex ) {
switch_mutex_lock ( mutex ) ;
} else {
switch_mutex_lock ( globals . mutex ) ;
}
2010-08-13 13:48:37 +00:00
}
2017-07-18 16:25:22 +00:00
if ( ! ( dbh = cc_get_db_handle ( ) ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Error Opening DB \n " ) ;
2010-08-13 13:48:37 +00:00
goto end ;
}
switch_cache_db_execute_sql_callback ( dbh , sql , callback , pdata , & errmsg ) ;
if ( errmsg ) {
2017-07-18 16:25:22 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " SQL ERR: [%s] %s \n " , sql , errmsg ) ;
2010-08-13 13:48:37 +00:00
free ( errmsg ) ;
}
end :
switch_cache_db_release_db_handle ( & dbh ) ;
2018-01-24 19:46:39 +00:00
if ( globals . global_database_lock ) {
if ( mutex ) {
switch_mutex_unlock ( mutex ) ;
} else {
switch_mutex_unlock ( globals . mutex ) ;
}
2010-08-13 13:48:37 +00:00
}
return ret ;
}
2017-07-18 16:25:22 +00:00
static cc_queue_t * load_queue ( const char * queue_name , switch_bool_t request_agents , switch_bool_t request_tiers , switch_xml_t x_queues_cfg )
2010-08-13 13:48:37 +00:00
{
cc_queue_t * queue = NULL ;
2016-10-05 02:07:42 +00:00
switch_xml_t x_queues , x_queue , cfg , x_agents , x_agent , x_tiers ;
switch_xml_t xml = NULL ;
2010-08-13 13:48:37 +00:00
switch_event_t * event = NULL ;
2014-01-14 14:34:20 +00:00
switch_event_t * params = NULL ;
2010-08-13 13:48:37 +00:00
2014-01-14 14:34:20 +00:00
switch_event_create ( & params , SWITCH_EVENT_REQUEST_PARAMS ) ;
switch_assert ( params ) ;
switch_event_add_header_string ( params , SWITCH_STACK_BOTTOM , " CC-Queue " , queue_name ) ;
2016-10-05 02:07:42 +00:00
if ( x_queues_cfg ) {
x_queues = x_queues_cfg ;
} else {
if ( ! ( xml = switch_xml_open_cfg ( global_cf , & cfg , params ) ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Open of %s failed \n " , global_cf ) ;
goto end ;
}
2017-07-18 16:25:22 +00:00
if ( ! ( x_queues = switch_xml_child ( cfg , " queues " ) ) ) {
2016-10-05 02:07:42 +00:00
goto end ;
}
2010-08-13 13:48:37 +00:00
}
if ( ( x_queue = switch_xml_find_child ( x_queues , " queue " , " name " , queue_name ) ) ) {
switch_memory_pool_t * pool ;
int count ;
if ( switch_core_new_memory_pool ( & pool ) ! = SWITCH_STATUS_SUCCESS ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_CRIT , " Pool Failure \n " ) ;
goto end ;
}
if ( ! ( queue = switch_core_alloc ( pool , sizeof ( cc_queue_t ) ) ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_CRIT , " Alloc Failure \n " ) ;
switch_core_destroy_memory_pool ( & pool ) ;
goto end ;
}
queue - > pool = pool ;
queue_set_config ( queue ) ;
/* Add the params to the event structure */
2014-01-22 21:28:53 +00:00
count = ( int ) switch_event_import_xml ( switch_xml_child ( x_queue , " param " ) , " name " , " value " , & event ) ;
2010-08-13 13:48:37 +00:00
if ( switch_xml_config_parse_event ( event , count , SWITCH_FALSE , queue - > config ) ! = SWITCH_STATUS_SUCCESS ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Failed to process configuration \n " ) ;
switch_core_destroy_memory_pool ( & pool ) ;
goto end ;
}
switch_thread_rwlock_create ( & queue - > rwlock , pool ) ;
queue - > name = switch_core_strdup ( pool , queue_name ) ;
2010-09-08 04:07:19 +00:00
queue - > last_agent_exist = 0 ;
queue - > last_agent_exist_check = 0 ;
2016-02-07 22:36:47 +00:00
queue - > calls_answered = 0 ;
queue - > calls_abandoned = 0 ;
2010-09-08 04:07:19 +00:00
2016-10-05 02:07:42 +00:00
if ( cc_agent_str2status ( queue - > agent_no_answer_status ) = = CC_AGENT_STATUS_UNKNOWN ) {
2017-07-18 16:25:22 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_WARNING , " Queue %s has invalid agent-no-answer-status, setting to %s " , queue - > name , cc_agent_status2str ( CC_AGENT_STATUS_ON_BREAK ) ) ;
2016-10-05 02:07:42 +00:00
queue - > agent_no_answer_status = switch_core_strdup ( pool , cc_agent_status2str ( CC_AGENT_STATUS_ON_BREAK ) ) ;
}
2010-08-13 13:48:37 +00:00
switch_mutex_init ( & queue - > mutex , SWITCH_MUTEX_NESTED , queue - > pool ) ;
2017-07-18 16:25:22 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " Added queue %s \n " , queue - > name ) ;
switch_core_hash_insert ( globals . queue_hash , queue - > name , queue ) ;
2010-08-13 13:48:37 +00:00
}
2016-05-14 01:21:04 +00:00
/* Importing from XML config Agents */
2017-07-18 16:25:22 +00:00
if ( queue & & request_agents & & ( x_agents = switch_xml_child ( cfg , " agents " ) ) ) {
2016-05-14 01:21:04 +00:00
for ( x_agent = switch_xml_child ( x_agents , " agent " ) ; x_agent ; x_agent = x_agent - > next ) {
const char * agent = switch_xml_attr ( x_agent , " name " ) ;
if ( agent ) {
2017-07-18 16:25:22 +00:00
load_agent ( agent , params , x_agents ) ;
2016-05-14 01:21:04 +00:00
}
}
}
/* Importing from XML config Agent Tiers */
2017-07-18 16:25:22 +00:00
if ( queue & & request_tiers & & ( x_tiers = switch_xml_child ( cfg , " tiers " ) ) ) {
load_tiers ( SWITCH_TRUE , queue_name , NULL , params , x_tiers ) ;
2016-05-14 01:21:04 +00:00
}
2010-08-13 13:48:37 +00:00
end :
if ( xml ) {
switch_xml_free ( xml ) ;
}
if ( event ) {
switch_event_destroy ( & event ) ;
}
2014-01-14 14:34:20 +00:00
if ( params ) {
switch_event_destroy ( & params ) ;
}
2010-08-13 13:48:37 +00:00
return queue ;
}
2017-07-18 16:25:22 +00:00
static cc_queue_t * get_queue ( const char * queue_name )
2017-07-18 14:34:07 +00:00
{
cc_queue_t * queue = NULL ;
2017-07-18 16:25:22 +00:00
switch_mutex_lock ( globals . mutex ) ;
if ( ! ( queue = switch_core_hash_find ( globals . queue_hash , queue_name ) ) ) {
queue = load_queue ( queue_name , SWITCH_FALSE , SWITCH_FALSE , NULL ) ;
2010-08-13 13:48:37 +00:00
}
if ( queue ) {
2017-07-18 16:25:22 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG10 , " [%s] rwlock \n " , queue - > name ) ;
2010-08-13 13:48:37 +00:00
switch_thread_rwlock_rdlock ( queue - > rwlock ) ;
}
2017-07-18 16:25:22 +00:00
switch_mutex_unlock ( globals . mutex ) ;
2010-08-13 13:48:37 +00:00
return queue ;
}
struct call_helper {
const char * member_uuid ;
2011-03-31 09:17:04 +00:00
const char * member_session_uuid ;
2010-09-08 04:07:19 +00:00
const char * queue_name ;
2010-08-13 13:48:37 +00:00
const char * queue_strategy ;
const char * member_joined_epoch ;
2011-03-31 09:22:50 +00:00
const char * member_cid_name ;
const char * member_cid_number ;
2010-08-13 13:48:37 +00:00
const char * agent_name ;
const char * agent_system ;
const char * agent_status ;
2010-08-26 01:40:33 +00:00
const char * agent_type ;
const char * agent_uuid ;
2010-08-13 13:48:37 +00:00
const char * originate_string ;
const char * record_template ;
int no_answer_count ;
int max_no_answer ;
2010-08-20 01:43:07 +00:00
int reject_delay_time ;
int busy_delay_time ;
2011-03-12 16:58:35 +00:00
int no_answer_delay_time ;
2016-10-05 02:07:42 +00:00
cc_agent_status_t agent_no_answer_status ;
2010-08-20 01:43:07 +00:00
2010-08-13 13:48:37 +00:00
switch_memory_pool_t * pool ;
} ;
2017-07-18 16:25:22 +00:00
int cc_queue_count ( const char * queue )
2010-08-13 13:48:37 +00:00
{
char * sql ;
int count = 0 ;
char res [ 256 ] = " 0 " ;
const char * event_name = " Single-Queue " ;
switch_event_t * event ;
if ( ! switch_strlen_zero ( queue ) ) {
if ( queue [ 0 ] = = ' * ' ) {
event_name = " All-Queues " ;
sql = switch_mprintf ( " SELECT count(*) FROM members WHERE state = '%q' OR state = '%q' " ,
cc_member_state2str ( CC_MEMBER_STATE_WAITING ) , cc_member_state2str ( CC_MEMBER_STATE_TRYING ) ) ;
} else {
sql = switch_mprintf ( " SELECT count(*) FROM members WHERE queue = '%q' AND (state = '%q' OR state = '%q') " ,
queue , cc_member_state2str ( CC_MEMBER_STATE_WAITING ) , cc_member_state2str ( CC_MEMBER_STATE_TRYING ) ) ;
}
2017-07-18 16:25:22 +00:00
cc_execute_sql2str ( NULL , NULL , sql , res , sizeof ( res ) ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
count = atoi ( res ) ;
if ( switch_event_create_subclass ( & event , SWITCH_EVENT_CUSTOM , CALLCENTER_EVENT ) = = SWITCH_STATUS_SUCCESS ) {
2010-09-08 04:07:19 +00:00
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Queue " , queue ) ;
2010-08-13 13:48:37 +00:00
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Action " , " members-count " ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Count " , res ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Selection " , event_name ) ;
switch_event_fire ( & event ) ;
}
2016-11-21 22:32:22 +00:00
}
2010-08-13 13:48:37 +00:00
return count ;
}
2017-07-18 16:25:22 +00:00
cc_status_t cc_agent_add ( const char * agent , const char * type )
2010-08-13 13:48:37 +00:00
{
2013-12-23 12:34:34 +00:00
switch_event_t * event ;
2010-08-13 13:48:37 +00:00
cc_status_t result = CC_STATUS_SUCCESS ;
char * sql ;
2010-08-26 01:40:33 +00:00
if ( ! strcasecmp ( type , CC_AGENT_TYPE_CALLBACK ) | | ! strcasecmp ( type , CC_AGENT_TYPE_UUID_STANDBY ) ) {
2010-08-13 13:48:37 +00:00
char res [ 256 ] = " " ;
/* Check to see if agent already exist */
sql = switch_mprintf ( " SELECT count(*) FROM agents WHERE name = '%q' " , agent ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql2str ( NULL , NULL , sql , res , sizeof ( res ) ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
if ( atoi ( res ) ! = 0 ) {
result = CC_STATUS_AGENT_ALREADY_EXIST ;
goto done ;
}
/* Add Agent */
2016-11-21 22:32:22 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Adding Agent %s with type %s with default status %s \n " ,
2010-08-13 13:48:37 +00:00
agent , type , cc_agent_status2str ( CC_AGENT_STATUS_LOGGED_OUT ) ) ;
2019-08-14 14:28:37 +00:00
sql = switch_mprintf ( " INSERT INTO agents (name, instance_id, type, status, state) VALUES('%q', 'single_box', '%q', '%q', '%q'); " ,
2010-08-13 13:48:37 +00:00
agent , type , cc_agent_status2str ( CC_AGENT_STATUS_LOGGED_OUT ) , cc_agent_state2str ( CC_AGENT_STATE_WAITING ) ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
2016-11-21 22:32:22 +00:00
2013-12-23 12:34:34 +00:00
if ( switch_event_create_subclass ( & event , SWITCH_EVENT_CUSTOM , CALLCENTER_EVENT ) = = SWITCH_STATUS_SUCCESS ) {
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Agent " , agent ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Agent-Type " , type ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Action " , " agent-add " ) ;
switch_event_fire ( & event ) ;
}
2010-08-13 13:48:37 +00:00
} else {
result = CC_STATUS_AGENT_INVALID_TYPE ;
goto done ;
}
2016-11-21 22:32:22 +00:00
done :
2010-08-13 13:48:37 +00:00
return result ;
}
2017-07-18 16:25:22 +00:00
cc_status_t cc_agent_del ( const char * agent )
2010-08-13 13:48:37 +00:00
{
cc_status_t result = CC_STATUS_SUCCESS ;
char * sql ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Deleted Agent %s \n " , agent ) ;
sql = switch_mprintf ( " DELETE FROM agents WHERE name = '%q'; "
" DELETE FROM tiers WHERE agent = '%q'; " ,
agent , agent ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
return result ;
}
2017-07-18 16:25:22 +00:00
cc_status_t cc_agent_get ( const char * key , const char * agent , char * ret_result , size_t ret_result_size )
2010-08-13 13:48:37 +00:00
{
cc_status_t result = CC_STATUS_SUCCESS ;
char * sql ;
switch_event_t * event ;
char res [ 256 ] ;
2011-08-16 23:39:06 +00:00
/* Check to see if agent already exists */
2010-08-13 13:48:37 +00:00
sql = switch_mprintf ( " SELECT count(*) FROM agents WHERE name = '%q' " , agent ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql2str ( NULL , NULL , sql , res , sizeof ( res ) ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
if ( atoi ( res ) = = 0 ) {
result = CC_STATUS_AGENT_NOT_FOUND ;
goto done ;
}
2016-11-21 22:32:22 +00:00
if ( ! strcasecmp ( key , " status " ) | | ! strcasecmp ( key , " state " ) | | ! strcasecmp ( key , " uuid " ) ) {
2011-08-16 23:39:06 +00:00
/* Check to see if agent already exists */
2010-08-13 13:48:37 +00:00
sql = switch_mprintf ( " SELECT %q FROM agents WHERE name = '%q' " , key , agent ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql2str ( NULL , NULL , sql , res , sizeof ( res ) ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
switch_snprintf ( ret_result , ret_result_size , " %s " , res ) ;
result = CC_STATUS_SUCCESS ;
if ( switch_event_create_subclass ( & event , SWITCH_EVENT_CUSTOM , CALLCENTER_EVENT ) = = SWITCH_STATUS_SUCCESS ) {
2011-08-16 23:39:06 +00:00
char tmpname [ 256 ] ;
if ( ! strcasecmp ( key , " uuid " ) ) {
2016-11-21 22:32:22 +00:00
switch_snprintf ( tmpname , sizeof ( tmpname ) , " CC-Agent-UUID " ) ;
2011-08-16 23:39:06 +00:00
} else {
switch_snprintf ( tmpname , sizeof ( tmpname ) , " CC-Agent-%c%s " , ( char ) switch_toupper ( key [ 0 ] ) , key + 1 ) ;
}
2010-09-08 04:07:19 +00:00
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Agent " , agent ) ;
2011-08-16 23:39:06 +00:00
switch_event_add_header ( event , SWITCH_STACK_BOTTOM , " CC-Action " , " agent-%s-get " , key ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , tmpname , res ) ;
2010-08-13 13:48:37 +00:00
switch_event_fire ( & event ) ;
}
} else {
result = CC_STATUS_INVALID_KEY ;
goto done ;
}
2016-11-21 22:32:22 +00:00
done :
2010-08-13 13:48:37 +00:00
if ( result = = CC_STATUS_SUCCESS ) {
2010-10-30 02:41:14 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Get Info Agent %s %s = %s \n " , agent , key , res ) ;
2010-08-13 13:48:37 +00:00
}
return result ;
}
2017-07-18 16:25:22 +00:00
cc_status_t cc_agent_update ( const char * key , const char * value , const char * agent )
2010-08-13 13:48:37 +00:00
{
cc_status_t result = CC_STATUS_SUCCESS ;
char * sql ;
char res [ 256 ] ;
switch_event_t * event ;
/* Check to see if agent already exist */
sql = switch_mprintf ( " SELECT count(*) FROM agents WHERE name = '%q' " , agent ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql2str ( NULL , NULL , sql , res , sizeof ( res ) ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
if ( atoi ( res ) = = 0 ) {
result = CC_STATUS_AGENT_NOT_FOUND ;
goto done ;
}
if ( ! strcasecmp ( key , " status " ) ) {
if ( cc_agent_str2status ( value ) ! = CC_AGENT_STATUS_UNKNOWN ) {
/* Reset values on available only */
if ( cc_agent_str2status ( value ) = = CC_AGENT_STATUS_AVAILABLE ) {
2011-05-08 18:10:01 +00:00
sql = switch_mprintf ( " UPDATE agents SET status = '%q', last_status_change = '% " SWITCH_TIME_T_FMT " ', talk_time = 0, calls_answered = 0, no_answer_count = 0 "
2010-08-13 13:48:37 +00:00
" WHERE name = '%q' AND NOT status = '%q' " ,
2011-04-23 21:01:35 +00:00
value , local_epoch_time_now ( NULL ) ,
2010-08-13 13:48:37 +00:00
agent , value ) ;
} else {
2011-05-08 18:10:01 +00:00
sql = switch_mprintf ( " UPDATE agents SET status = '%q', last_status_change = '% " SWITCH_TIME_T_FMT " ' WHERE name = '%q' " ,
2011-04-23 21:01:35 +00:00
value , local_epoch_time_now ( NULL ) , agent ) ;
2010-08-13 13:48:37 +00:00
}
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
/* Used to stop any active callback */
if ( cc_agent_str2status ( value ) ! = CC_AGENT_STATUS_AVAILABLE ) {
sql = switch_mprintf ( " SELECT uuid FROM members WHERE serving_agent = '%q' AND serving_system = 'single_box' AND NOT state = 'Answered' " , agent ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql2str ( NULL , NULL , sql , res , sizeof ( res ) ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
if ( ! switch_strlen_zero ( res ) ) {
2011-04-15 00:23:12 +00:00
switch_core_session_hupall_matching_var ( " cc_member_pre_answer_uuid " , res , SWITCH_CAUSE_ORIGINATOR_CANCEL ) ;
2010-08-13 13:48:37 +00:00
}
}
result = CC_STATUS_SUCCESS ;
if ( switch_event_create_subclass ( & event , SWITCH_EVENT_CUSTOM , CALLCENTER_EVENT ) = = SWITCH_STATUS_SUCCESS ) {
2010-09-08 04:07:19 +00:00
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Agent " , agent ) ;
2010-08-13 13:48:37 +00:00
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Action " , " agent-status-change " ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Agent-Status " , value ) ;
switch_event_fire ( & event ) ;
}
} else {
result = CC_STATUS_AGENT_INVALID_STATUS ;
goto done ;
}
} else if ( ! strcasecmp ( key , " state " ) ) {
if ( cc_agent_str2state ( value ) ! = CC_AGENT_STATE_UNKNOWN ) {
if ( cc_agent_str2state ( value ) ! = CC_AGENT_STATE_RECEIVING ) {
sql = switch_mprintf ( " UPDATE agents SET state = '%q' WHERE name = '%q' " , value , agent ) ;
} else {
2011-05-08 18:10:01 +00:00
sql = switch_mprintf ( " UPDATE agents SET state = '%q', last_offered_call = '% " SWITCH_TIME_T_FMT " ' WHERE name = '%q' " ,
2011-04-23 21:01:35 +00:00
value , local_epoch_time_now ( NULL ) , agent ) ;
2010-08-13 13:48:37 +00:00
}
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
result = CC_STATUS_SUCCESS ;
if ( switch_event_create_subclass ( & event , SWITCH_EVENT_CUSTOM , CALLCENTER_EVENT ) = = SWITCH_STATUS_SUCCESS ) {
2010-09-08 04:07:19 +00:00
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Agent " , agent ) ;
2010-08-13 13:48:37 +00:00
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Action " , " agent-state-change " ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Agent-State " , value ) ;
switch_event_fire ( & event ) ;
}
} else {
result = CC_STATUS_AGENT_INVALID_STATE ;
goto done ;
}
2010-08-26 01:40:33 +00:00
} else if ( ! strcasecmp ( key , " uuid " ) ) {
2019-08-14 14:28:37 +00:00
sql = switch_mprintf ( " UPDATE agents SET uuid = '%q', instance_id = 'single_box' WHERE name = '%q' " , value , agent ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2010-08-26 01:40:33 +00:00
switch_safe_free ( sql ) ;
2010-08-13 13:48:37 +00:00
2010-08-26 01:40:33 +00:00
result = CC_STATUS_SUCCESS ;
2010-08-13 13:48:37 +00:00
} else if ( ! strcasecmp ( key , " contact " ) ) {
2019-08-14 14:28:37 +00:00
sql = switch_mprintf ( " UPDATE agents SET contact = '%q', instance_id = 'single_box' WHERE name = '%q' " , value , agent ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
result = CC_STATUS_SUCCESS ;
2013-12-23 12:34:34 +00:00
if ( switch_event_create_subclass ( & event , SWITCH_EVENT_CUSTOM , CALLCENTER_EVENT ) = = SWITCH_STATUS_SUCCESS ) {
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Agent " , agent ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Action " , " agent-contact-change " ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Agent-Contact " , value ) ;
switch_event_fire ( & event ) ;
}
2010-08-20 01:43:07 +00:00
} else if ( ! strcasecmp ( key , " ready_time " ) ) {
2019-08-14 14:28:37 +00:00
sql = switch_mprintf ( " UPDATE agents SET ready_time = '%ld', instance_id = 'single_box' WHERE name = '%q' " , atol ( value ) , agent ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2010-08-20 01:43:07 +00:00
switch_safe_free ( sql ) ;
result = CC_STATUS_SUCCESS ;
} else if ( ! strcasecmp ( key , " busy_delay_time " ) ) {
2019-08-14 14:28:37 +00:00
sql = switch_mprintf ( " UPDATE agents SET busy_delay_time = '%ld', instance_id = 'single_box' WHERE name = '%q' " , atol ( value ) , agent ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2010-08-20 01:43:07 +00:00
switch_safe_free ( sql ) ;
result = CC_STATUS_SUCCESS ;
} else if ( ! strcasecmp ( key , " reject_delay_time " ) ) {
2019-08-14 14:28:37 +00:00
sql = switch_mprintf ( " UPDATE agents SET reject_delay_time = '%ld', instance_id = 'single_box' WHERE name = '%q' " , atol ( value ) , agent ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2010-08-20 01:43:07 +00:00
switch_safe_free ( sql ) ;
2010-08-13 13:48:37 +00:00
2011-03-12 16:58:35 +00:00
result = CC_STATUS_SUCCESS ;
} else if ( ! strcasecmp ( key , " no_answer_delay_time " ) ) {
2019-08-14 14:28:37 +00:00
sql = switch_mprintf ( " UPDATE agents SET no_answer_delay_time = '%ld', instance_id = 'single_box' WHERE name = '%q' " , atol ( value ) , agent ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2011-03-12 16:58:35 +00:00
switch_safe_free ( sql ) ;
2010-08-20 01:43:07 +00:00
result = CC_STATUS_SUCCESS ;
2010-08-13 13:48:37 +00:00
} else if ( ! strcasecmp ( key , " type " ) ) {
2010-08-26 01:40:33 +00:00
if ( strcasecmp ( value , CC_AGENT_TYPE_CALLBACK ) & & strcasecmp ( value , CC_AGENT_TYPE_UUID_STANDBY ) ) {
2010-08-13 13:48:37 +00:00
result = CC_STATUS_AGENT_INVALID_TYPE ;
goto done ;
}
sql = switch_mprintf ( " UPDATE agents SET type = '%q' WHERE name = '%q' " , value , agent ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
result = CC_STATUS_SUCCESS ;
} else if ( ! strcasecmp ( key , " max_no_answer " ) ) {
2019-08-14 14:28:37 +00:00
sql = switch_mprintf ( " UPDATE agents SET max_no_answer = '%d', instance_id = 'single_box' WHERE name = '%q' " , atoi ( value ) , agent ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
result = CC_STATUS_SUCCESS ;
} else if ( ! strcasecmp ( key , " wrap_up_time " ) ) {
2019-08-14 14:28:37 +00:00
sql = switch_mprintf ( " UPDATE agents SET wrap_up_time = '%d', instance_id = 'single_box' WHERE name = '%q' " , atoi ( value ) , agent ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
result = CC_STATUS_SUCCESS ;
2015-05-03 04:10:02 +00:00
} else if ( ! strcasecmp ( key , " state_if_waiting " ) ) {
if ( cc_agent_str2state ( value ) = = CC_AGENT_STATE_UNKNOWN ) {
result = CC_STATUS_AGENT_INVALID_STATE ;
goto done ;
} else {
sql = switch_mprintf ( " UPDATE agents SET state = '%q' WHERE name = '%q' AND state = '%q' AND status IN ('%q', '%q') " ,
value , agent ,
cc_agent_state2str ( CC_AGENT_STATE_WAITING ) ,
cc_agent_status2str ( CC_AGENT_STATUS_AVAILABLE ) ,
cc_agent_status2str ( CC_AGENT_STATUS_AVAILABLE_ON_DEMAND ) ) ;
2017-07-18 16:25:22 +00:00
if ( cc_execute_sql_affected_rows ( sql ) > 0 ) {
2015-05-03 04:10:02 +00:00
result = CC_STATUS_SUCCESS ;
if ( switch_event_create_subclass ( & event , SWITCH_EVENT_CUSTOM , CALLCENTER_EVENT ) = = SWITCH_STATUS_SUCCESS ) {
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Agent " , agent ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Action " , " agent-state-change " ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Agent-State " , value ) ;
switch_event_fire ( & event ) ;
}
} else {
result = CC_STATUS_AGENT_NOT_FOUND ;
}
switch_safe_free ( sql ) ;
}
2010-08-13 13:48:37 +00:00
} else {
result = CC_STATUS_INVALID_KEY ;
goto done ;
}
done :
if ( result = = CC_STATUS_SUCCESS ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Updated Agent %s set %s = %s \n " , agent , key , value ) ;
}
return result ;
}
2017-07-18 16:25:22 +00:00
cc_status_t cc_tier_add ( const char * queue_name , const char * agent , const char * state , int level , int position )
2010-08-13 13:48:37 +00:00
{
cc_status_t result = CC_STATUS_SUCCESS ;
char * sql ;
cc_queue_t * queue = NULL ;
2017-07-18 16:25:22 +00:00
if ( ! ( queue = get_queue ( queue_name ) ) ) {
2010-08-13 13:48:37 +00:00
result = CC_STATUS_QUEUE_NOT_FOUND ;
goto done ;
} else {
queue_rwunlock ( queue ) ;
}
if ( cc_tier_str2state ( state ) ! = CC_TIER_STATE_UNKNOWN ) {
char res [ 256 ] = " " ;
/* Check to see if agent already exist */
sql = switch_mprintf ( " SELECT count(*) FROM agents WHERE name = '%q' " , agent ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql2str ( NULL , NULL , sql , res , sizeof ( res ) ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
if ( atoi ( res ) = = 0 ) {
result = CC_STATUS_AGENT_NOT_FOUND ;
goto done ;
}
/* Check to see if tier already exist */
sql = switch_mprintf ( " SELECT count(*) FROM tiers WHERE agent = '%q' AND queue = '%q' " , agent , queue_name ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql2str ( NULL , NULL , sql , res , sizeof ( res ) ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
if ( atoi ( res ) ! = 0 ) {
result = CC_STATUS_TIER_ALREADY_EXIST ;
goto done ;
}
/* Add Agent in tier */
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Adding Tier on Queue %s for Agent %s, level %d, position %d \n " , queue_name , agent , level , position ) ;
sql = switch_mprintf ( " INSERT INTO tiers (queue, agent, state, level, position) VALUES('%q', '%q', '%q', '%d', '%d'); " ,
queue_name , agent , state , level , position ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
result = CC_STATUS_SUCCESS ;
} else {
result = CC_STATUS_TIER_INVALID_STATE ;
goto done ;
}
2016-11-21 22:32:22 +00:00
done :
2010-08-13 13:48:37 +00:00
return result ;
}
2017-07-18 16:25:22 +00:00
cc_status_t cc_tier_update ( const char * key , const char * value , const char * queue_name , const char * agent )
2010-08-13 13:48:37 +00:00
{
cc_status_t result = CC_STATUS_SUCCESS ;
char * sql ;
char res [ 256 ] ;
cc_queue_t * queue = NULL ;
/* Check to see if tier already exist */
sql = switch_mprintf ( " SELECT count(*) FROM tiers WHERE agent = '%q' AND queue = '%q' " , agent , queue_name ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql2str ( NULL , NULL , sql , res , sizeof ( res ) ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
if ( atoi ( res ) = = 0 ) {
result = CC_STATUS_TIER_NOT_FOUND ;
goto done ;
}
/* Check to see if agent already exist */
sql = switch_mprintf ( " SELECT count(*) FROM agents WHERE name = '%q' " , agent ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql2str ( NULL , NULL , sql , res , sizeof ( res ) ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
if ( atoi ( res ) = = 0 ) {
result = CC_STATUS_AGENT_NOT_FOUND ;
goto done ;
}
2017-07-18 16:25:22 +00:00
if ( ! ( queue = get_queue ( queue_name ) ) ) {
2010-08-13 13:48:37 +00:00
result = CC_STATUS_QUEUE_NOT_FOUND ;
goto done ;
} else {
queue_rwunlock ( queue ) ;
}
if ( ! strcasecmp ( key , " state " ) ) {
if ( cc_tier_str2state ( value ) ! = CC_TIER_STATE_UNKNOWN ) {
sql = switch_mprintf ( " UPDATE tiers SET state = '%q' WHERE queue = '%q' AND agent = '%q' " , value , queue_name , agent ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
result = CC_STATUS_SUCCESS ;
} else {
result = CC_STATUS_TIER_INVALID_STATE ;
goto done ;
}
} else if ( ! strcasecmp ( key , " level " ) ) {
sql = switch_mprintf ( " UPDATE tiers SET level = '%d' WHERE queue = '%q' AND agent = '%q' " , atoi ( value ) , queue_name , agent ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
result = CC_STATUS_SUCCESS ;
} else if ( ! strcasecmp ( key , " position " ) ) {
sql = switch_mprintf ( " UPDATE tiers SET position = '%d' WHERE queue = '%q' AND agent = '%q' " , atoi ( value ) , queue_name , agent ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
result = CC_STATUS_SUCCESS ;
} else {
result = CC_STATUS_INVALID_KEY ;
goto done ;
2016-11-21 22:32:22 +00:00
}
2010-08-13 13:48:37 +00:00
done :
if ( result = = CC_STATUS_SUCCESS ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Updated tier: Agent %s in Queue %s set %s = %s \n " , agent , queue_name , key , value ) ;
}
return result ;
}
2017-07-18 16:25:22 +00:00
cc_status_t cc_tier_del ( const char * queue_name , const char * agent )
2010-08-13 13:48:37 +00:00
{
cc_status_t result = CC_STATUS_SUCCESS ;
char * sql ;
2010-09-08 04:07:19 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Deleted tier Agent %s in Queue %s \n " , agent , queue_name ) ;
sql = switch_mprintf ( " DELETE FROM tiers WHERE queue = '%q' AND agent = '%q'; " , queue_name , agent ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
result = CC_STATUS_SUCCESS ;
return result ;
}
2017-07-18 16:25:22 +00:00
static switch_status_t load_agent ( const char * agent_name , switch_event_t * params , switch_xml_t x_agents_cfg )
2010-08-13 13:48:37 +00:00
{
2016-10-05 02:07:42 +00:00
switch_xml_t x_agents , x_agent , cfg ;
switch_xml_t xml = NULL ;
if ( x_agents_cfg ) {
x_agents = x_agents_cfg ;
} else {
if ( ! ( xml = switch_xml_open_cfg ( global_cf , & cfg , params ) ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Open of %s failed \n " , global_cf ) ;
2017-07-18 16:25:22 +00:00
return SWITCH_STATUS_FALSE ;
2017-07-18 14:34:07 +00:00
}
2017-07-18 16:25:22 +00:00
if ( ! ( x_agents = switch_xml_child ( cfg , " agents " ) ) ) {
2016-10-05 02:07:42 +00:00
goto end ;
}
2010-08-13 13:48:37 +00:00
}
if ( ( x_agent = switch_xml_find_child ( x_agents , " agent " , " name " , agent_name ) ) ) {
const char * type = switch_xml_attr ( x_agent , " type " ) ;
2016-11-21 22:32:22 +00:00
const char * contact = switch_xml_attr ( x_agent , " contact " ) ;
2010-08-13 13:48:37 +00:00
const char * status = switch_xml_attr ( x_agent , " status " ) ;
const char * max_no_answer = switch_xml_attr ( x_agent , " max-no-answer " ) ;
const char * wrap_up_time = switch_xml_attr ( x_agent , " wrap-up-time " ) ;
2010-08-20 01:43:07 +00:00
const char * reject_delay_time = switch_xml_attr ( x_agent , " reject-delay-time " ) ;
const char * busy_delay_time = switch_xml_attr ( x_agent , " busy-delay-time " ) ;
2011-03-12 16:58:35 +00:00
const char * no_answer_delay_time = switch_xml_attr ( x_agent , " no-answer-delay-time " ) ;
2010-08-13 13:48:37 +00:00
if ( type ) {
2017-07-18 16:25:22 +00:00
cc_status_t res = cc_agent_add ( agent_name , type ) ;
2010-08-13 13:48:37 +00:00
if ( res = = CC_STATUS_SUCCESS | | res = = CC_STATUS_AGENT_ALREADY_EXIST ) {
if ( contact ) {
2017-07-18 16:25:22 +00:00
cc_agent_update ( " contact " , contact , agent_name ) ;
2010-08-13 13:48:37 +00:00
}
if ( status ) {
2017-07-18 16:25:22 +00:00
cc_agent_update ( " status " , status , agent_name ) ;
2010-08-13 13:48:37 +00:00
}
if ( wrap_up_time ) {
2017-07-18 16:25:22 +00:00
cc_agent_update ( " wrap_up_time " , wrap_up_time , agent_name ) ;
2010-08-13 13:48:37 +00:00
}
if ( max_no_answer ) {
2017-07-18 16:25:22 +00:00
cc_agent_update ( " max_no_answer " , max_no_answer , agent_name ) ;
2010-08-13 13:48:37 +00:00
}
2010-08-20 01:43:07 +00:00
if ( reject_delay_time ) {
2017-07-18 16:25:22 +00:00
cc_agent_update ( " reject_delay_time " , reject_delay_time , agent_name ) ;
2010-08-20 01:43:07 +00:00
}
if ( busy_delay_time ) {
2017-07-18 16:25:22 +00:00
cc_agent_update ( " busy_delay_time " , busy_delay_time , agent_name ) ;
2010-08-20 01:43:07 +00:00
}
2011-03-12 16:58:35 +00:00
if ( no_answer_delay_time ) {
2017-07-18 16:25:22 +00:00
cc_agent_update ( " no_answer_delay_time " , no_answer_delay_time , agent_name ) ;
2011-03-12 16:58:35 +00:00
}
2010-08-13 13:48:37 +00:00
if ( type & & res = = CC_STATUS_AGENT_ALREADY_EXIST ) {
2017-07-18 16:25:22 +00:00
cc_agent_update ( " type " , type , agent_name ) ;
2010-08-13 13:48:37 +00:00
}
}
}
}
end :
if ( xml ) {
switch_xml_free ( xml ) ;
}
return SWITCH_STATUS_SUCCESS ;
}
2017-07-18 16:25:22 +00:00
static switch_status_t load_tier ( const char * queue , const char * agent , const char * level , const char * position )
2013-11-28 06:28:00 +00:00
{
/* Hack to check if an tier already exist */
2017-07-18 16:25:22 +00:00
if ( cc_tier_update ( " unknown " , " unknown " , queue , agent ) = = CC_STATUS_TIER_NOT_FOUND ) {
2015-05-04 19:51:02 +00:00
if ( ! zstr ( level ) & & ! zstr ( position ) ) {
2017-07-18 16:25:22 +00:00
cc_tier_add ( queue , agent , cc_tier_state2str ( CC_TIER_STATE_READY ) , atoi ( level ) , atoi ( position ) ) ;
2015-05-04 19:51:02 +00:00
} else if ( ! zstr ( level ) & & zstr ( position ) ) {
2017-07-18 16:25:22 +00:00
cc_tier_add ( queue , agent , cc_tier_state2str ( CC_TIER_STATE_READY ) , atoi ( level ) , 1 ) ;
2015-05-04 19:51:02 +00:00
} else if ( zstr ( level ) & & ! zstr ( position ) ) {
2017-07-18 16:25:22 +00:00
cc_tier_add ( queue , agent , cc_tier_state2str ( CC_TIER_STATE_READY ) , 1 , atoi ( position ) ) ;
2013-11-28 06:28:00 +00:00
} else {
/* default to level 1 and position 1 within the level */
2017-07-18 16:25:22 +00:00
cc_tier_add ( queue , agent , cc_tier_state2str ( CC_TIER_STATE_READY ) , 1 , 1 ) ;
2013-11-28 06:28:00 +00:00
}
} else {
2015-05-04 19:51:02 +00:00
if ( ! zstr ( level ) ) {
2017-07-18 16:25:22 +00:00
cc_tier_update ( " level " , level , queue , agent ) ;
2015-05-04 19:51:02 +00:00
} else {
2017-07-18 16:25:22 +00:00
cc_tier_update ( " level " , " 1 " , queue , agent ) ;
2013-11-28 06:28:00 +00:00
}
2015-05-04 19:51:02 +00:00
if ( ! zstr ( position ) ) {
2017-07-18 16:25:22 +00:00
cc_tier_update ( " position " , position , queue , agent ) ;
2015-05-04 19:51:02 +00:00
} else {
2017-07-18 16:25:22 +00:00
cc_tier_update ( " position " , " 1 " , queue , agent ) ;
2013-11-28 06:28:00 +00:00
}
}
return SWITCH_STATUS_SUCCESS ;
}
2017-07-18 16:25:22 +00:00
static switch_status_t load_tiers ( switch_bool_t load_all , const char * queue_name , const char * agent_name , switch_event_t * params , switch_xml_t x_tiers_cfg )
2013-11-28 06:28:00 +00:00
{
2016-10-05 02:07:42 +00:00
switch_xml_t x_tiers , x_tier , cfg ;
switch_xml_t xml = NULL ;
2013-11-28 06:28:00 +00:00
switch_status_t result = SWITCH_STATUS_FALSE ;
2016-10-05 02:07:42 +00:00
if ( x_tiers_cfg ) {
x_tiers = x_tiers_cfg ;
} else {
if ( ! ( xml = switch_xml_open_cfg ( global_cf , & cfg , params ) ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Open of %s failed \n " , global_cf ) ;
2017-07-18 16:25:22 +00:00
return SWITCH_STATUS_FALSE ;
2017-07-18 14:34:07 +00:00
}
2017-07-18 16:25:22 +00:00
if ( ! ( x_tiers = switch_xml_child ( cfg , " tiers " ) ) ) {
2016-10-05 02:07:42 +00:00
goto end ;
}
2013-11-28 06:28:00 +00:00
}
/* Importing from XML config Agent Tiers */
for ( x_tier = switch_xml_child ( x_tiers , " tier " ) ; x_tier ; x_tier = x_tier - > next ) {
const char * agent = switch_xml_attr ( x_tier , " agent " ) ;
const char * queue = switch_xml_attr ( x_tier , " queue " ) ;
const char * level = switch_xml_attr ( x_tier , " level " ) ;
const char * position = switch_xml_attr ( x_tier , " position " ) ;
if ( load_all = = SWITCH_TRUE ) {
2017-07-18 16:25:22 +00:00
result = load_tier ( queue , agent , level , position ) ;
2013-11-28 06:28:00 +00:00
} else if ( ! zstr ( agent_name ) & & ! zstr ( queue_name ) & & ! strcasecmp ( agent , agent_name ) & & ! strcasecmp ( queue , queue_name ) ) {
2017-07-18 16:25:22 +00:00
result = load_tier ( queue , agent , level , position ) ;
2013-11-28 06:28:00 +00:00
} else if ( zstr ( agent_name ) & & ! strcasecmp ( queue , queue_name ) ) {
2017-07-18 16:25:22 +00:00
result = load_tier ( queue , agent , level , position ) ;
2013-11-28 06:28:00 +00:00
} else if ( zstr ( queue_name ) & & ! strcasecmp ( agent , agent_name ) ) {
2017-07-18 16:25:22 +00:00
result = load_tier ( queue , agent , level , position ) ;
2013-11-28 06:28:00 +00:00
}
}
end :
if ( xml ) {
switch_xml_free ( xml ) ;
}
return result ;
}
2019-08-20 14:39:01 +00:00
struct sqlite_column_rename_callback_data {
const char * table ;
switch_cache_db_handle_t * dbh ;
} ;
typedef struct sqlite_column_rename_callback_data sqlite_column_rename_callback_data_t ;
static int sqlite_column_rename_callback ( void * pArg , const char * errmsg )
{
sqlite_column_rename_callback_data_t * callback_data = ( sqlite_column_rename_callback_data_t * ) pArg ;
char tmptable [ 4096 ] ;
char * sql = NULL ;
if ( ! strcasecmp ( " agents " , callback_data - > table ) ) {
if ( NULL ! = ( sql = strstr ( agents_sql , " TABLE agents ( " ) ) ) {
sql + = 14 ;
sprintf ( tmptable , " CREATE TABLE agents_tmp (%s " , sql ) ;
switch_cache_db_execute_sql_callback ( callback_data - > dbh , tmptable , NULL , NULL , NULL ) ;
switch_cache_db_execute_sql_callback ( callback_data - > dbh , " INSERT INTO agents_tmp SELECT * FROM agents; " , NULL , NULL , NULL ) ;
switch_cache_db_execute_sql_callback ( callback_data - > dbh , " drop table agents " , NULL , NULL , NULL ) ;
switch_cache_db_execute_sql_callback ( callback_data - > dbh , agents_sql , NULL , NULL , NULL ) ;
switch_cache_db_execute_sql_callback ( callback_data - > dbh , " INSERT INTO agents SELECT * FROM agents_tmp; " , NULL , NULL , NULL ) ;
switch_cache_db_execute_sql_callback ( callback_data - > dbh , " drop table agents_tmp " , NULL , NULL , NULL ) ;
}
}
else if ( ! strcasecmp ( " members " , callback_data - > table ) ) {
if ( NULL ! = ( sql = strstr ( members_sql , " TABLE members ( " ) ) ) {
sql + = 15 ;
sprintf ( tmptable , " CREATE TABLE members_tmp (%s " , sql ) ;
switch_cache_db_execute_sql_callback ( callback_data - > dbh , tmptable , NULL , NULL , NULL ) ;
switch_cache_db_execute_sql_callback ( callback_data - > dbh , " INSERT INTO members_tmp SELECT * FROM members; " , NULL , NULL , NULL ) ;
switch_cache_db_execute_sql_callback ( callback_data - > dbh , " drop table members " , NULL , NULL , NULL ) ;
switch_cache_db_execute_sql_callback ( callback_data - > dbh , members_sql , NULL , NULL , NULL ) ;
switch_cache_db_execute_sql_callback ( callback_data - > dbh , " INSERT INTO members SELECT * FROM members_tmp; " , NULL , NULL , NULL ) ;
switch_cache_db_execute_sql_callback ( callback_data - > dbh , " drop table members_tmp " , NULL , NULL , NULL ) ;
}
}
return 0 ;
}
2010-08-13 13:48:37 +00:00
static switch_status_t load_config ( void )
{
2011-04-15 17:53:04 +00:00
switch_status_t status = SWITCH_STATUS_SUCCESS ;
2017-07-18 16:25:22 +00:00
switch_xml_t cfg , xml , settings , param , x_queues , x_queue , x_agents , x_agent , x_tiers ;
switch_cache_db_handle_t * dbh = NULL ;
char * sql = NULL ;
2013-11-28 06:28:00 +00:00
2010-08-13 13:48:37 +00:00
if ( ! ( xml = switch_xml_open_cfg ( global_cf , & cfg , NULL ) ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Open of %s failed \n " , global_cf ) ;
2011-04-15 17:53:04 +00:00
status = SWITCH_STATUS_TERM ;
goto end ;
2010-08-13 13:48:37 +00:00
}
switch_mutex_lock ( globals . mutex ) ;
2018-01-24 19:46:39 +00:00
globals . global_database_lock = SWITCH_TRUE ;
2017-07-18 16:25:22 +00:00
if ( ( settings = switch_xml_child ( cfg , " settings " ) ) ) {
for ( param = switch_xml_child ( settings , " param " ) ; param ; param = param - > next ) {
char * var = ( char * ) switch_xml_attr_soft ( param , " name " ) ;
char * val = ( char * ) switch_xml_attr_soft ( param , " value " ) ;
2010-08-13 13:48:37 +00:00
2017-07-18 16:25:22 +00:00
if ( ! strcasecmp ( var , " debug " ) ) {
globals . debug = atoi ( val ) ;
} else if ( ! strcasecmp ( var , " dbname " ) ) {
globals . dbname = strdup ( val ) ;
} else if ( ! strcasecmp ( var , " odbc-dsn " ) ) {
globals . odbc_dsn = strdup ( val ) ;
} else if ( ! strcasecmp ( var , " reserve-agents " ) ) {
globals . reserve_agents = switch_true ( val ) ;
} else if ( ! strcasecmp ( var , " truncate-tiers-on-load " ) ) {
globals . truncate_tiers = switch_true ( val ) ;
} else if ( ! strcasecmp ( var , " truncate-agents-on-load " ) ) {
globals . truncate_agents = switch_true ( val ) ;
2018-01-24 19:46:39 +00:00
} else if ( ! strcasecmp ( var , " global-database-lock " ) ) {
globals . global_database_lock = switch_true ( val ) ;
2017-07-12 16:01:06 +00:00
} else if ( ! strcasecmp ( var , " cc-instance-id " ) ) {
globals . cc_instance_id = strdup ( val ) ;
2019-04-15 14:15:43 +00:00
} else if ( ! strcasecmp ( var , " agent-originate-timeout " ) ) {
globals . agent_originate_timeout = atoi ( val ) ;
2017-07-18 16:25:22 +00:00
}
}
}
2017-07-12 16:01:06 +00:00
if ( zstr ( globals . dbname ) ) {
2017-07-18 16:25:22 +00:00
globals . dbname = strdup ( CC_SQLITE_DB_NAME ) ;
}
2017-07-12 16:01:06 +00:00
if ( zstr ( globals . cc_instance_id ) ) {
globals . cc_instance_id = strdup ( " single_box " ) ;
}
2017-07-18 16:25:22 +00:00
if ( ! globals . reserve_agents ) {
globals . reserve_agents = SWITCH_FALSE ;
2016-10-05 02:07:42 +00:00
} else {
2017-07-18 16:25:22 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Reserving Agents before offering calls. \n " ) ;
}
2018-01-24 19:46:39 +00:00
if ( ! globals . global_database_lock ) {
2019-04-15 14:15:43 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Disabling global database lock \n " ) ;
2018-01-24 19:46:39 +00:00
}
2019-04-15 14:15:43 +00:00
if ( ! globals . agent_originate_timeout ) globals . agent_originate_timeout = 60 ;
2017-07-18 16:25:22 +00:00
/* Initialize database */
if ( ! ( dbh = cc_get_db_handle ( ) ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_CRIT , " Cannot open DB! \n " ) ;
status = SWITCH_STATUS_TERM ;
goto end ;
}
switch_cache_db_test_reactive ( dbh , " select count(session_uuid) from members " , " drop table members " , members_sql ) ;
switch_cache_db_test_reactive ( dbh , " select count(ready_time) from agents " , NULL , " alter table agents add ready_time integer not null default 0; "
" alter table agents add reject_delay_time integer not null default 0; "
" alter table agents add busy_delay_time integer not null default 0; " ) ;
switch_cache_db_test_reactive ( dbh , " select count(no_answer_delay_time) from agents " , NULL , " alter table agents add no_answer_delay_time integer not null default 0; " ) ;
switch_cache_db_test_reactive ( dbh , " select count(ready_time) from agents " , " drop table agents " , agents_sql ) ;
2019-08-14 14:28:37 +00:00
switch_cache_db_test_reactive ( dbh , " select count(external_calls_count) from agents " , NULL , " alter table agents add external_calls_count integer not null default 0; " ) ;
2017-07-18 16:25:22 +00:00
switch_cache_db_test_reactive ( dbh , " select count(queue) from tiers " , " drop table tiers " , tiers_sql ) ;
2019-08-14 14:28:37 +00:00
/* This will rename column system for SQLite */
2019-08-20 14:39:01 +00:00
if ( switch_cache_db_get_type ( dbh ) = = SCDB_TYPE_CORE_DB ) {
char * errmsg = NULL ;
sqlite_column_rename_callback_data_t callback_data ;
/* SQLite < 3.27.0 ( https://www.sqlite.org/changes.html ) has issues with renaming columns and tables */
callback_data . dbh = dbh ;
callback_data . table = " agents " ;
switch_cache_db_execute_sql_callback_err ( dbh , " select count(instance_id) from agents " , NULL , sqlite_column_rename_callback , & callback_data , & errmsg ) ;
callback_data . table = " members " ;
switch_cache_db_execute_sql_callback_err ( dbh , " select count(instance_id) from members " , NULL , sqlite_column_rename_callback , & callback_data , & errmsg ) ;
} else {
/* This will rename column system for MySQL */
switch_cache_db_test_reactive ( dbh , " select count(instance_id) from agents " , NULL , " alter table agents rename column `system` TO instance_id; " ) ;
switch_cache_db_test_reactive ( dbh , " select count(instance_id) from members " , NULL , " alter table members rename column `system` TO instance_id; " ) ;
/* This will rename column system for PGSql */
switch_cache_db_test_reactive ( dbh , " select count(instance_id) from agents " , NULL , " alter table agents rename system TO instance_id; " ) ;
switch_cache_db_test_reactive ( dbh , " select count(instance_id) from members " , NULL , " alter table members rename system TO instance_id; " ) ;
}
2017-07-18 16:25:22 +00:00
switch_cache_db_release_db_handle ( & dbh ) ;
/* Reset a unclean shutdown */
2019-08-14 14:28:37 +00:00
sql = switch_mprintf ( " update agents set state = 'Waiting', uuid = '' where instance_id = 'single_box'; "
" update tiers set state = 'Ready' where agent IN (select name from agents where instance_id = 'single_box'); "
" update members set state = '%q', session_uuid = '' where instance_id = '%q'; "
" update agents set external_calls_count = 0 where instance_id = 'single_box'; " ,
2017-07-12 16:01:06 +00:00
cc_member_state2str ( CC_MEMBER_STATE_ABANDONED ) , globals . cc_instance_id ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
switch_safe_free ( sql ) ;
/* Truncating tiers if needed */
if ( globals . truncate_tiers ) {
sql = switch_mprintf ( " delete from tiers; " ) ;
cc_execute_sql ( NULL , sql , NULL ) ;
switch_safe_free ( sql ) ;
}
/* Truncating agents if needed */
if ( globals . truncate_agents ) {
sql = switch_mprintf ( " delete from agents; " ) ;
cc_execute_sql ( NULL , sql , NULL ) ;
switch_safe_free ( sql ) ;
}
/* Loading queue into memory struct */
if ( ( x_queues = switch_xml_child ( cfg , " queues " ) ) ) {
for ( x_queue = switch_xml_child ( x_queues , " queue " ) ; x_queue ; x_queue = x_queue - > next ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Loading queue %s \n " , switch_xml_attr_soft ( x_queue , " name " ) ) ;
load_queue ( switch_xml_attr_soft ( x_queue , " name " ) , SWITCH_FALSE , SWITCH_FALSE , x_queues ) ;
}
}
/* Importing from XML config Agents */
if ( ( x_agents = switch_xml_child ( cfg , " agents " ) ) ) {
for ( x_agent = switch_xml_child ( x_agents , " agent " ) ; x_agent ; x_agent = x_agent - > next ) {
const char * agent = switch_xml_attr ( x_agent , " name " ) ;
if ( agent ) {
load_agent ( agent , NULL , x_agents ) ;
2017-07-18 14:34:07 +00:00
}
}
2016-10-05 02:07:42 +00:00
}
2010-08-13 13:48:37 +00:00
2017-07-18 16:25:22 +00:00
/* Importing from XML config Agent Tiers */
if ( ( x_tiers = switch_xml_child ( cfg , " tiers " ) ) ) {
load_tiers ( SWITCH_TRUE , NULL , NULL , NULL , x_tiers ) ;
} else {
load_tiers ( SWITCH_TRUE , NULL , NULL , NULL , NULL ) ;
}
2011-04-15 17:53:04 +00:00
end :
2010-08-13 13:48:37 +00:00
switch_mutex_unlock ( globals . mutex ) ;
2011-04-15 17:53:04 +00:00
if ( xml ) {
switch_xml_free ( xml ) ;
}
2010-08-13 13:48:37 +00:00
2011-04-15 17:53:04 +00:00
return status ;
2010-08-13 13:48:37 +00:00
}
2014-05-16 21:18:17 +00:00
static switch_status_t playback_array ( switch_core_session_t * session , const char * str ) {
switch_status_t status = SWITCH_STATUS_FALSE ;
2011-07-18 10:17:42 +00:00
if ( str & & ! strncmp ( str , " ARRAY:: " , 7 ) ) {
char * i = ( char * ) str + 7 , * j = i ;
while ( 1 ) {
if ( ( j = strstr ( i , " :: " ) ) ) {
* j = 0 ;
}
2014-05-16 21:18:17 +00:00
status = switch_ivr_play_file ( session , NULL , i , NULL ) ;
if ( status = = SWITCH_STATUS_FALSE /* Invalid Recording */ & & SWITCH_READ_ACCEPTABLE ( status ) ) {
/* Sadly, there doesn't seem to be a return to switch_ivr_play_file that tell you the file wasn't found. FALSE also mean that the channel got switch to BRAKE state, so we check for read acceptable */
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_WARNING , " Couldn't play file '%s' \n " , i ) ;
} else if ( ! SWITCH_READ_ACCEPTABLE ( status ) ) {
break ;
}
2011-07-18 10:17:42 +00:00
if ( ! j ) break ;
i = j + 2 ;
}
} else {
2014-05-16 21:18:17 +00:00
status = switch_ivr_play_file ( session , NULL , str , NULL ) ;
2011-07-18 10:17:42 +00:00
}
2014-05-16 21:18:17 +00:00
return status ;
2011-07-18 10:17:42 +00:00
}
2010-08-13 13:48:37 +00:00
static void * SWITCH_THREAD_FUNC outbound_agent_thread_run ( switch_thread_t * thread , void * obj )
{
struct call_helper * h = ( struct call_helper * ) obj ;
switch_core_session_t * agent_session = NULL ;
switch_call_cause_t cause = SWITCH_CAUSE_NONE ;
switch_status_t status = SWITCH_STATUS_FALSE ;
char * sql = NULL ;
char * dialstr = NULL ;
cc_tier_state_t tiers_state = CC_TIER_STATE_READY ;
2011-03-31 09:17:04 +00:00
switch_core_session_t * member_session = switch_core_session_locate ( h - > member_session_uuid ) ;
2010-08-13 13:48:37 +00:00
switch_event_t * ovars ;
switch_time_t t_agent_called = 0 ;
switch_time_t t_agent_answered = 0 ;
switch_time_t t_member_called = atoi ( h - > member_joined_epoch ) ;
2010-11-27 01:38:46 +00:00
switch_event_t * event = NULL ;
2016-08-24 02:31:47 +00:00
int bridged = 1 ;
2010-08-13 13:48:37 +00:00
switch_mutex_lock ( globals . mutex ) ;
globals . threads + + ;
switch_mutex_unlock ( globals . mutex ) ;
/* member is gone before we could process it */
if ( ! member_session ) {
2016-12-28 00:19:57 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Member %s <%s> with uuid %s in queue %s is gone just before we assigned an agent \n " , h - > member_cid_name , h - > member_cid_number , h - > member_session_uuid , h - > queue_name ) ;
2017-10-17 23:32:35 +00:00
bridged = 0 ;
2016-11-21 22:32:22 +00:00
2019-08-14 14:28:37 +00:00
sql = switch_mprintf ( " UPDATE members SET state = '%q', session_uuid = '', abandoned_epoch = '% " SWITCH_TIME_T_FMT " ' WHERE uuid = '%q' AND instance_id = '%q' AND state != '%q' " ,
2017-07-12 16:01:06 +00:00
cc_member_state2str ( CC_MEMBER_STATE_ABANDONED ) , local_epoch_time_now ( NULL ) , h - > member_uuid , globals . cc_instance_id , cc_member_state2str ( CC_MEMBER_STATE_ABANDONED ) ) ;
2010-08-26 10:19:58 +00:00
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
goto done ;
}
2010-11-27 01:38:46 +00:00
/* Proceed contact the agent to offer the member */
if ( switch_event_create_subclass ( & event , SWITCH_EVENT_CUSTOM , CALLCENTER_EVENT ) = = SWITCH_STATUS_SUCCESS ) {
2011-08-16 23:39:06 +00:00
switch_channel_t * member_channel = switch_core_session_get_channel ( member_session ) ;
switch_caller_profile_t * member_profile = switch_channel_get_caller_profile ( member_channel ) ;
const char * member_dnis = member_profile - > rdnis ;
2017-07-18 16:25:22 +00:00
2010-11-27 01:38:46 +00:00
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Queue " , h - > queue_name ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Action " , " agent-offering " ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Agent " , h - > agent_name ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Agent-Type " , h - > agent_type ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Agent-System " , h - > agent_system ) ;
2011-03-31 09:17:04 +00:00
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Member-UUID " , h - > member_uuid ) ;
2011-03-31 09:22:50 +00:00
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Member-Session-UUID " , h - > member_session_uuid ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Member-CID-Name " , h - > member_cid_name ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Member-CID-Number " , h - > member_cid_number ) ;
2011-08-16 23:39:06 +00:00
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Member-DNIS " , member_dnis ) ;
2010-11-27 01:38:46 +00:00
switch_event_fire ( & event ) ;
}
/* CallBack Mode */
2010-08-26 01:40:33 +00:00
if ( ! strcasecmp ( h - > agent_type , CC_AGENT_TYPE_CALLBACK ) ) {
2011-08-19 05:28:39 +00:00
switch_channel_t * member_channel = switch_core_session_get_channel ( member_session ) ;
2014-02-24 13:52:36 +00:00
const char * cid_name = NULL ;
char * cid_name_freeable = NULL ;
const char * cid_number = NULL ;
2011-08-19 05:28:39 +00:00
const char * cid_name_prefix = NULL ;
2014-02-24 13:52:36 +00:00
2015-04-17 21:50:16 +00:00
if ( ! ( cid_name = switch_channel_get_variable ( member_channel , " effective_caller_id_name " ) ) ) {
cid_name = h - > member_cid_name ;
}
if ( ! ( cid_number = switch_channel_get_variable ( member_channel , " effective_caller_id_number " ) ) ) {
cid_number = h - > member_cid_number ;
}
2011-08-19 05:28:39 +00:00
if ( ( cid_name_prefix = switch_channel_get_variable ( member_channel , " cc_outbound_cid_name_prefix " ) ) ) {
2015-04-17 21:50:16 +00:00
cid_name_freeable = switch_mprintf ( " %s%s " , cid_name_prefix , cid_name ) ;
2014-02-24 13:52:36 +00:00
cid_name = cid_name_freeable ;
}
2015-04-17 21:50:16 +00:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( member_session ) , SWITCH_LOG_DEBUG , " Setting outbound caller_id_name to: %s \n " , cid_name ) ;
2010-08-26 01:40:33 +00:00
switch_event_create ( & ovars , SWITCH_EVENT_REQUEST_PARAMS ) ;
2010-09-08 04:07:19 +00:00
switch_event_add_header ( ovars , SWITCH_STACK_BOTTOM , " cc_queue " , " %s " , h - > queue_name ) ;
2016-12-21 15:14:04 +00:00
switch_event_add_header ( ovars , SWITCH_STACK_BOTTOM , " cc_queue_joined_epoch " , " %s " , h - > member_joined_epoch ) ;
2010-08-26 01:40:33 +00:00
switch_event_add_header ( ovars , SWITCH_STACK_BOTTOM , " cc_member_uuid " , " %s " , h - > member_uuid ) ;
2011-03-31 09:17:04 +00:00
switch_event_add_header ( ovars , SWITCH_STACK_BOTTOM , " cc_member_session_uuid " , " %s " , h - > member_session_uuid ) ;
2010-08-26 01:40:33 +00:00
switch_event_add_header ( ovars , SWITCH_STACK_BOTTOM , " cc_member_pre_answer_uuid " , " %s " , h - > member_uuid ) ;
2010-09-04 00:12:45 +00:00
switch_event_add_header ( ovars , SWITCH_STACK_BOTTOM , " cc_agent " , " %s " , h - > agent_name ) ;
switch_event_add_header ( ovars , SWITCH_STACK_BOTTOM , " cc_agent_type " , " %s " , h - > agent_type ) ;
2011-03-31 09:17:04 +00:00
switch_event_add_header ( ovars , SWITCH_STACK_BOTTOM , " cc_side " , " %s " , " agent " ) ;
2011-11-02 21:49:46 +00:00
switch_event_add_header ( ovars , SWITCH_STACK_BOTTOM , " loopback_bowout " , " false " ) ;
switch_event_add_header ( ovars , SWITCH_STACK_BOTTOM , " loopback_bowout_on_execute " , " false " ) ;
2010-08-26 01:40:33 +00:00
switch_event_add_header ( ovars , SWITCH_STACK_BOTTOM , " ignore_early_media " , " true " ) ;
2012-05-25 17:45:02 +00:00
switch_channel_process_export ( member_channel , NULL , ovars , " cc_export_vars " ) ;
2011-04-23 21:01:35 +00:00
t_agent_called = local_epoch_time_now ( NULL ) ;
2013-10-04 19:47:19 +00:00
2013-10-04 19:21:27 +00:00
dialstr = switch_channel_expand_variables ( member_channel , h - > originate_string ) ;
2017-08-10 05:36:58 +00:00
switch_channel_set_app_flag_key ( CC_APP_KEY , member_channel , CC_APP_AGENT_CONNECTING ) ;
2019-04-15 14:15:43 +00:00
status = switch_ivr_originate ( NULL , & agent_session , & cause , dialstr , globals . agent_originate_timeout , NULL , cid_name ? cid_name : h - > member_cid_name , cid_number ? cid_number : h - > member_cid_number , NULL , ovars , SOF_NONE , NULL , NULL ) ;
2016-09-15 09:28:54 +00:00
/* Search for loopback agent */
if ( status = = SWITCH_STATUS_SUCCESS ) {
switch_channel_t * agent_channel = switch_core_session_get_channel ( agent_session ) ;
const char * other_loopback_leg_uuid = switch_channel_get_variable ( agent_channel , " other_loopback_leg_uuid " ) ;
/* Our agent channel is a loopback. Try to find if a real channel is bridged to it in order
to use it as our new agent channel .
- Locate the loopback - b channel using ' other_loopback_leg_uuid ' variable
- Locate the real agent channel using ' switch_channel_get_partner_uuid ( ) ' function on loopback - b
*/
if ( other_loopback_leg_uuid ) {
switch_core_session_t * loopback_a_session = agent_session ;
switch_core_session_t * loopback_b_session = NULL ;
switch_channel_t * loopback_a_channel = agent_channel ;
switch_channel_t * loopback_b_channel = NULL ;
const char * real_uuid = NULL ;
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( member_session ) , SWITCH_LOG_DEBUG , " Agent '%s' is a loopback channel. Searching for real channel... \n " , h - > agent_name ) ;
/* Locate loopback-b channel */
if ( ! ( loopback_b_session = switch_core_session_locate ( other_loopback_leg_uuid ) ) ) {
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( member_session ) , SWITCH_LOG_DEBUG , " Failed to locate loopback-b channel of agent '%s' \n " , h - > agent_name ) ;
switch_channel_hangup ( loopback_a_channel , SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER ) ;
status = SWITCH_STATUS_FALSE ;
} else {
loopback_b_channel = switch_core_session_get_channel ( loopback_b_session ) ;
/* Wait for loopback-b to be fully bridged */
if ( switch_channel_wait_for_flag ( loopback_b_channel , CF_BRIDGED , SWITCH_TRUE , 5000 , member_channel ) ! = SWITCH_STATUS_SUCCESS ) {
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( member_session ) , SWITCH_LOG_DEBUG , " Timeout while waiting for the loopback-b channel to be bridged (agent '%s') \n " , h - > agent_name ) ;
switch_channel_hangup ( loopback_a_channel , SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER ) ;
status = SWITCH_STATUS_FALSE ;
} else {
/* Get real agent channel uuid */
real_uuid = switch_channel_get_partner_uuid ( loopback_b_channel ) ;
if ( ! real_uuid ) {
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( member_session ) , SWITCH_LOG_DEBUG , " Failed to find a real channel behind loopback agent '%s' \n " , h - > agent_name ) ;
switch_channel_hangup ( loopback_a_channel , SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER ) ;
status = SWITCH_STATUS_FALSE ;
} else {
/* Locate real agent session */
if ( ! ( agent_session = switch_core_session_locate ( real_uuid ) ) ) {
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( member_session ) , SWITCH_LOG_DEBUG , " Real session is already gone (agent '%s') \n " , h - > agent_name ) ;
switch_channel_hangup ( loopback_a_channel , SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER ) ;
status = SWITCH_STATUS_FALSE ;
} else {
/* Found! Switch agent channel and set variables */
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( member_session ) , SWITCH_LOG_DEBUG , " Real channel found behind loopback agent '%s' \n " , h - > agent_name ) ;
agent_channel = switch_core_session_get_channel ( agent_session ) ;
switch_channel_set_variable ( agent_channel , " cc_side " , " agent " ) ;
switch_channel_set_variable ( agent_channel , " cc_queue " , h - > queue_name ) ;
switch_channel_set_variable ( agent_channel , " cc_agent " , h - > agent_name ) ;
switch_channel_set_variable ( agent_channel , " cc_agent_type " , h - > agent_type ) ;
switch_channel_set_variable ( agent_channel , " cc_member_uuid " , h - > member_uuid ) ;
switch_channel_set_variable ( agent_channel , " cc_member_session_uuid " , h - > member_session_uuid ) ;
switch_channel_process_export ( member_channel , agent_channel , NULL , " cc_export_vars " ) ;
/* Mark loopback to not be hungup in case of ring-all */
switch_channel_set_variable ( loopback_a_channel , " cc_member_pre_answer_uuid " , NULL ) ;
switch_channel_set_variable ( loopback_b_channel , " cc_member_pre_answer_uuid " , NULL ) ;
switch_core_session_rwunlock ( loopback_a_session ) ;
}
}
}
switch_core_session_rwunlock ( loopback_b_session ) ;
}
}
}
2013-10-04 19:47:19 +00:00
if ( dialstr ! = h - > originate_string ) {
switch_safe_free ( dialstr ) ;
}
2014-02-24 13:52:36 +00:00
switch_safe_free ( cid_name_freeable ) ;
2010-08-26 01:40:33 +00:00
switch_event_destroy ( & ovars ) ;
2010-11-27 01:38:46 +00:00
/* UUID Standby Mode */
2010-08-26 01:40:33 +00:00
} else if ( ! strcasecmp ( h - > agent_type , CC_AGENT_TYPE_UUID_STANDBY ) ) {
agent_session = switch_core_session_locate ( h - > agent_uuid ) ;
if ( agent_session ) {
switch_channel_t * agent_channel = switch_core_session_get_channel ( agent_session ) ;
const char * cc_warning_tone = switch_channel_get_variable ( agent_channel , " cc_warning_tone " ) ;
2017-09-15 01:18:20 +00:00
switch_channel_t * member_channel = switch_core_session_get_channel ( member_session ) ;
const char * cc_export_vars = switch_channel_get_variable ( member_channel , " cc_export_vars " ) ;
char * cc_export_vars_dup = switch_core_session_strdup ( member_session , cc_export_vars ) ;
int argc ;
char * argv [ 256 ] ;
2010-09-04 00:12:45 +00:00
2011-03-31 09:17:04 +00:00
switch_channel_set_variable ( agent_channel , " cc_side " , " agent " ) ;
2010-09-08 04:07:19 +00:00
switch_channel_set_variable ( agent_channel , " cc_queue " , h - > queue_name ) ;
2010-09-04 00:12:45 +00:00
switch_channel_set_variable ( agent_channel , " cc_agent " , h - > agent_name ) ;
switch_channel_set_variable ( agent_channel , " cc_agent_type " , h - > agent_type ) ;
switch_channel_set_variable ( agent_channel , " cc_member_uuid " , h - > member_uuid ) ;
2011-03-31 09:17:04 +00:00
switch_channel_set_variable ( agent_channel , " cc_member_session_uuid " , h - > member_session_uuid ) ;
2010-09-04 00:12:45 +00:00
2017-09-15 01:18:20 +00:00
argc = switch_separate_string ( cc_export_vars_dup , ' , ' , argv , ( sizeof ( argv ) / sizeof ( argv [ 0 ] ) ) ) ;
for ( int i = 0 ; i < argc ; + + i ) {
switch_channel_set_variable ( agent_channel , argv [ i ] , switch_channel_get_variable ( member_channel , argv [ i ] ) ) ;
}
2010-08-26 01:40:33 +00:00
/* Playback this to the agent */
2016-12-27 23:49:59 +00:00
if ( cc_warning_tone ) {
2017-09-15 00:02:47 +00:00
switch_ivr_park_session ( agent_session ) ;
switch_channel_wait_for_flag ( agent_channel , CF_PARK , SWITCH_TRUE , 5000 , NULL ) ;
2016-12-27 23:49:59 +00:00
playback_array ( agent_session , cc_warning_tone ) ;
2010-08-26 01:40:33 +00:00
}
2010-08-13 13:48:37 +00:00
2010-08-26 01:40:33 +00:00
status = SWITCH_STATUS_SUCCESS ;
} else {
2017-07-18 16:25:22 +00:00
cc_agent_update ( " status " , cc_agent_status2str ( CC_AGENT_STATUS_LOGGED_OUT ) , h - > agent_name ) ;
cc_agent_update ( " uuid " , " " , h - > agent_name ) ;
2010-08-26 01:40:33 +00:00
}
2010-11-27 01:38:46 +00:00
} else {
2011-04-22 06:22:30 +00:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( member_session ) , SWITCH_LOG_DEBUG , " Invalid agent type '%s' for agent '%s', aborting member offering " , h - > agent_type , h - > agent_name ) ;
2012-01-29 20:50:14 +00:00
cause = SWITCH_CAUSE_USER_NOT_REGISTERED ;
2010-08-26 01:40:33 +00:00
}
2010-08-13 13:48:37 +00:00
2010-11-27 01:38:46 +00:00
/* Originate/Bridge is not finished, processing the return value */
2010-08-13 13:48:37 +00:00
if ( status = = SWITCH_STATUS_SUCCESS ) {
/* Agent Answered */
const char * agent_uuid = switch_core_session_get_uuid ( agent_session ) ;
switch_channel_t * member_channel = switch_core_session_get_channel ( member_session ) ;
switch_channel_t * agent_channel = switch_core_session_get_channel ( agent_session ) ;
2011-07-18 10:17:42 +00:00
const char * o_announce = NULL ;
2010-08-13 13:48:37 +00:00
switch_channel_set_variable ( agent_channel , " cc_member_pre_answer_uuid " , NULL ) ;
2016-04-20 08:15:34 +00:00
if ( ! strcasecmp ( h - > queue_strategy , " ring-all " ) | | ! strcasecmp ( h - > queue_strategy , " ring-progressively " ) ) {
2010-08-13 13:48:37 +00:00
char res [ 256 ] ;
/* Map the Agent to the member */
sql = switch_mprintf ( " UPDATE members SET serving_agent = '%q', serving_system = 'single_box', state = '%q' "
2019-08-14 14:28:37 +00:00
" WHERE state = '%q' AND uuid = '%q' AND instance_id = '%q' AND serving_agent = '%q' " ,
2010-08-13 13:48:37 +00:00
h - > agent_name , cc_member_state2str ( CC_MEMBER_STATE_TRYING ) ,
2017-07-12 16:01:06 +00:00
cc_member_state2str ( CC_MEMBER_STATE_TRYING ) , h - > member_uuid , globals . cc_instance_id , h - > queue_strategy ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
/* Check if we won the race to get the member to our selected agent (Used for Multi system purposes) */
sql = switch_mprintf ( " SELECT count(*) FROM members "
2019-08-14 14:28:37 +00:00
" WHERE serving_agent = '%q' AND serving_system = 'single_box' AND uuid = '%q' AND instance_id = '%q' " ,
2017-07-12 16:01:06 +00:00
h - > agent_name , h - > member_uuid , globals . cc_instance_id ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql2str ( NULL , NULL , sql , res , sizeof ( res ) ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
if ( atoi ( res ) = = 0 ) {
goto done ;
}
2013-11-28 00:19:31 +00:00
switch_core_session_hupall_matching_var ( " cc_member_pre_answer_uuid " , h - > member_uuid , SWITCH_CAUSE_LOSE_RACE ) ;
2010-08-13 13:48:37 +00:00
}
2016-04-20 08:15:34 +00:00
2011-04-23 21:01:35 +00:00
t_agent_answered = local_epoch_time_now ( NULL ) ;
2010-08-13 13:48:37 +00:00
if ( switch_event_create_subclass ( & event , SWITCH_EVENT_CUSTOM , CALLCENTER_EVENT ) = = SWITCH_STATUS_SUCCESS ) {
2011-08-16 23:39:06 +00:00
switch_caller_profile_t * member_profile = switch_channel_get_caller_profile ( member_channel ) ;
const char * member_dnis = member_profile - > rdnis ;
2010-08-13 13:48:37 +00:00
switch_channel_event_set_data ( agent_channel , event ) ;
2010-09-08 04:07:19 +00:00
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Queue " , h - > queue_name ) ;
2010-08-13 13:48:37 +00:00
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Action " , " bridge-agent-start " ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Agent " , h - > agent_name ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Agent-System " , h - > agent_system ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Agent-UUID " , agent_uuid ) ;
2011-04-23 15:55:56 +00:00
switch_event_add_header ( event , SWITCH_STACK_BOTTOM , " CC-Agent-Called-Time " , " % " SWITCH_TIME_T_FMT , t_agent_called ) ;
switch_event_add_header ( event , SWITCH_STACK_BOTTOM , " CC-Agent-Answered-Time " , " % " SWITCH_TIME_T_FMT , t_agent_answered ) ;
switch_event_add_header ( event , SWITCH_STACK_BOTTOM , " CC-Member-Joined-Time " , " % " SWITCH_TIME_T_FMT , t_member_called ) ;
2011-03-31 09:17:04 +00:00
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Member-UUID " , h - > member_uuid ) ;
2011-03-31 09:22:50 +00:00
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Member-Session-UUID " , h - > member_session_uuid ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Member-CID-Name " , h - > member_cid_name ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Member-CID-Number " , h - > member_cid_number ) ;
2011-08-16 23:39:06 +00:00
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Member-DNIS " , member_dnis ) ;
2010-08-13 13:48:37 +00:00
switch_event_fire ( & event ) ;
}
/* Record session if record-template is provided */
if ( h - > record_template ) {
char * expanded = switch_channel_expand_variables ( member_channel , h - > record_template ) ;
switch_channel_set_variable ( member_channel , " cc_record_filename " , expanded ) ;
switch_ivr_record_session ( member_session , expanded , 0 , NULL ) ;
if ( expanded ! = h - > record_template ) {
switch_safe_free ( expanded ) ;
2016-11-21 22:32:22 +00:00
}
2010-08-13 13:48:37 +00:00
}
2011-04-22 06:22:30 +00:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( member_session ) , SWITCH_LOG_DEBUG , " Agent %s answered \" %s \" <%s> from queue %s%s \n " ,
2011-03-31 09:22:50 +00:00
h - > agent_name , h - > member_cid_name , h - > member_cid_number , h - > queue_name , ( h - > record_template ? " (Recorded) " : " " ) ) ;
2011-04-25 18:22:57 +00:00
2011-07-18 10:17:42 +00:00
if ( ( o_announce = switch_channel_get_variable ( member_channel , " cc_outbound_announce " ) ) ) {
playback_array ( agent_session , o_announce ) ;
}
2011-04-22 07:01:08 +00:00
/* This is used for the waiting caller to quit waiting for a agent */
switch_channel_set_variable ( member_channel , " cc_agent_found " , " true " ) ;
2015-06-22 20:51:08 +00:00
switch_channel_set_variable ( member_channel , " cc_agent_uuid " , agent_uuid ) ;
2017-02-05 17:56:47 +00:00
if ( switch_true ( switch_channel_get_variable ( member_channel , SWITCH_BYPASS_MEDIA_AFTER_BRIDGE_VARIABLE ) ) | | switch_true ( switch_channel_get_variable ( agent_channel , SWITCH_BYPASS_MEDIA_AFTER_BRIDGE_VARIABLE ) ) ) {
2016-04-07 20:54:05 +00:00
switch_channel_set_flag ( member_channel , CF_BYPASS_MEDIA_AFTER_BRIDGE ) ;
}
2011-04-22 07:01:08 +00:00
2016-08-24 02:31:47 +00:00
if ( switch_ivr_uuid_bridge ( h - > member_session_uuid , switch_core_session_get_uuid ( agent_session ) ) ! = SWITCH_STATUS_SUCCESS ) {
bridged = 0 ;
}
if ( bridged & & ( switch_channel_wait_for_flag ( agent_channel , CF_BRIDGED , SWITCH_TRUE , 3000 , NULL ) ! = SWITCH_STATUS_SUCCESS ) ) {
bridged = 0 ;
}
if ( ! bridged & & ! switch_channel_up ( member_channel ) ) {
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( member_session ) , SWITCH_LOG_DEBUG , " Failed to bridge, member \" %s \" <%s> has gone just before we called agent %s \n " ,
h - > member_cid_name , h - > member_cid_number , h - > agent_name ) ;
switch_channel_set_variable ( agent_channel , " cc_agent_bridged " , " false " ) ;
switch_channel_set_variable ( member_channel , " cc_agent_bridged " , " false " ) ;
if ( ( o_announce = switch_channel_get_variable ( member_channel , " cc_bridge_failed_outbound_announce " ) ) ) {
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( member_session ) , SWITCH_LOG_DEBUG , " Playing bridge failed audio to agent %s, audio: %s \n " , h - > agent_name , o_announce ) ;
playback_array ( agent_session , o_announce ) ;
}
if ( ! strcasecmp ( h - > agent_type , CC_AGENT_TYPE_CALLBACK ) ) {
switch_channel_hangup ( agent_channel , SWITCH_CAUSE_ORIGINATOR_CANCEL ) ;
}
2016-12-28 00:14:57 +00:00
} else if ( ! bridged & & ! switch_channel_up ( agent_channel ) ) {
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( member_session ) , SWITCH_LOG_DEBUG , " Failed to bridge, agent %s has no session \n " , h - > agent_name ) ;
/* Put back member on Waiting state, previous Trying */
2019-08-14 14:28:37 +00:00
sql = switch_mprintf ( " UPDATE members SET state = 'Waiting' WHERE uuid = '%q' AND instance_id = '%q' " , h - > member_uuid , globals . cc_instance_id ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2016-12-28 00:14:57 +00:00
switch_safe_free ( sql ) ;
2016-08-24 02:31:47 +00:00
} else {
bridged = 1 ;
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( member_session ) , SWITCH_LOG_DEBUG , " Member \" %s \" %s is bridged to agent %s \n " ,
h - > member_cid_name , h - > member_cid_number , h - > agent_name ) ;
switch_channel_set_variable ( member_channel , " cc_agent_bridged " , " true " ) ;
switch_channel_set_variable ( agent_channel , " cc_agent_bridged " , " true " ) ;
switch_channel_set_variable ( member_channel , " cc_agent_uuid " , agent_uuid ) ;
}
if ( bridged ) {
/* for xml_cdr needs */
switch_channel_set_variable ( member_channel , " cc_agent " , h - > agent_name ) ;
switch_channel_set_variable_printf ( member_channel , " cc_queue_answered_epoch " , " % " SWITCH_TIME_T_FMT , local_epoch_time_now ( NULL ) ) ;
/* Set UUID of the Agent channel */
sql = switch_mprintf ( " UPDATE agents SET uuid = '%q', last_bridge_start = '% " SWITCH_TIME_T_FMT " ', calls_answered = calls_answered + 1, no_answer_count = 0 "
2019-08-14 14:28:37 +00:00
" WHERE name = '%q' AND instance_id = '%q' " ,
2016-08-24 02:31:47 +00:00
agent_uuid , local_epoch_time_now ( NULL ) ,
h - > agent_name , h - > agent_system ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2016-08-24 02:31:47 +00:00
switch_safe_free ( sql ) ;
/* Change the agents Status in the tiers */
2017-07-18 16:25:22 +00:00
cc_tier_update ( " state " , cc_tier_state2str ( CC_TIER_STATE_ACTIVE_INBOUND ) , h - > queue_name , h - > agent_name ) ;
cc_agent_update ( " state " , cc_agent_state2str ( CC_AGENT_STATE_IN_A_QUEUE_CALL ) , h - > agent_name ) ;
2016-08-24 02:31:47 +00:00
}
2017-08-10 05:36:58 +00:00
switch_channel_clear_app_flag_key ( CC_APP_KEY , member_channel , CC_APP_AGENT_CONNECTING ) ;
2017-09-13 20:39:43 +00:00
/* Wait until the agent hangup. This will quit also if the agent transfer the call or callcenter_break is used*/
2016-08-24 02:31:47 +00:00
while ( bridged & & switch_channel_up ( agent_channel ) & & globals . running ) {
2015-06-22 20:51:08 +00:00
if ( ! strcasecmp ( h - > agent_type , CC_AGENT_TYPE_UUID_STANDBY ) ) {
if ( ! switch_channel_test_flag ( agent_channel , CF_BRIDGED ) ) {
break ;
}
}
2017-09-13 20:39:43 +00:00
if ( switch_channel_test_app_flag_key ( CC_APP_KEY , agent_channel , CC_APP_SHOULD_BREAK ) ) {
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( agent_session ) , SWITCH_LOG_DEBUG , " callcenter_break requested at agent channel, releasing agent %s \n " , h - > agent_name ) ;
switch_channel_clear_app_flag_key ( CC_APP_KEY , agent_channel , CC_APP_SHOULD_BREAK ) ;
break ;
}
2010-08-13 13:48:37 +00:00
switch_yield ( 100000 ) ;
}
2011-03-14 16:31:56 +00:00
tiers_state = CC_TIER_STATE_READY ;
2010-08-13 13:48:37 +00:00
2017-05-09 14:19:59 +00:00
if ( switch_event_create_subclass ( & event , SWITCH_EVENT_CUSTOM , CALLCENTER_EVENT ) = = SWITCH_STATUS_SUCCESS ) {
2010-08-13 13:48:37 +00:00
switch_channel_event_set_data ( agent_channel , event ) ;
2010-09-08 04:07:19 +00:00
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Queue " , h - > queue_name ) ;
2010-08-13 13:48:37 +00:00
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Action " , " bridge-agent-end " ) ;
2011-03-12 16:29:20 +00:00
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Hangup-Cause " , switch_channel_cause2str ( cause ) ) ;
2010-08-13 13:48:37 +00:00
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Agent " , h - > agent_name ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Agent-System " , h - > agent_system ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Agent-UUID " , agent_uuid ) ;
2016-08-24 02:31:47 +00:00
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Agent-Bridged " , bridged ? " true " : " false " ) ;
2011-04-23 15:55:56 +00:00
switch_event_add_header ( event , SWITCH_STACK_BOTTOM , " CC-Agent-Called-Time " , " % " SWITCH_TIME_T_FMT , t_agent_called ) ;
switch_event_add_header ( event , SWITCH_STACK_BOTTOM , " CC-Agent-Answered-Time " , " % " SWITCH_TIME_T_FMT , t_agent_answered ) ;
switch_event_add_header ( event , SWITCH_STACK_BOTTOM , " CC-Member-Joined-Time " , " % " SWITCH_TIME_T_FMT , t_member_called ) ;
2011-04-23 21:01:35 +00:00
switch_event_add_header ( event , SWITCH_STACK_BOTTOM , " CC-Bridge-Terminated-Time " , " % " SWITCH_TIME_T_FMT , local_epoch_time_now ( NULL ) ) ;
2011-03-31 09:17:04 +00:00
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Member-UUID " , h - > member_uuid ) ;
2011-03-31 09:22:50 +00:00
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Member-Session-UUID " , h - > member_session_uuid ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Member-CID-Name " , h - > member_cid_name ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Member-CID-Number " , h - > member_cid_number ) ;
2010-08-13 13:48:37 +00:00
switch_event_fire ( & event ) ;
}
2016-08-24 02:31:47 +00:00
if ( bridged ) {
/* for xml_cdr needs */
switch_channel_set_variable_printf ( member_channel , " cc_queue_terminated_epoch " , " % " SWITCH_TIME_T_FMT , local_epoch_time_now ( NULL ) ) ;
/* Update Agents Items */
/* Do not remove uuid of the agent if we are a standby agent */
2019-08-14 14:28:37 +00:00
sql = switch_mprintf ( " UPDATE agents SET %s last_bridge_end = % " SWITCH_TIME_T_FMT " , talk_time = talk_time + (% " SWITCH_TIME_T_FMT " -last_bridge_start) WHERE name = '%q' AND instance_id = '%q'; "
2016-08-24 02:31:47 +00:00
, ( strcasecmp ( h - > agent_type , CC_AGENT_TYPE_UUID_STANDBY ) ? " uuid = '', " : " " ) , local_epoch_time_now ( NULL ) , local_epoch_time_now ( NULL ) , h - > agent_name , h - > agent_system ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2016-08-24 02:31:47 +00:00
switch_safe_free ( sql ) ;
2010-08-13 13:48:37 +00:00
2016-12-28 00:14:57 +00:00
/* Remove the member entry from the db (Could become optional to support latter processing) */
2019-08-14 14:28:37 +00:00
sql = switch_mprintf ( " DELETE FROM members WHERE uuid = '%q' AND instance_id = '%q' " , h - > member_uuid , globals . cc_instance_id ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2016-12-28 00:14:57 +00:00
switch_safe_free ( sql ) ;
2010-08-13 13:48:37 +00:00
2016-12-28 00:14:57 +00:00
/* Caller off event */
if ( switch_event_create_subclass ( & event , SWITCH_EVENT_CUSTOM , CALLCENTER_EVENT ) = = SWITCH_STATUS_SUCCESS ) {
switch_channel_event_set_data ( member_channel , event ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Queue " , h - > queue_name ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Action " , " member-queue-end " ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Hangup-Cause " ,
switch_channel_cause2str ( cause ) ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Cause " , " Terminated " ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Agent " , h - > agent_name ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Agent-System " , h - > agent_system ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Agent-UUID " , agent_uuid ) ;
switch_event_add_header ( event , SWITCH_STACK_BOTTOM , " CC-Agent-Called-Time " , " % " SWITCH_TIME_T_FMT ,
t_agent_called ) ;
switch_event_add_header ( event , SWITCH_STACK_BOTTOM , " CC-Agent-Answered-Time " , " % " SWITCH_TIME_T_FMT ,
t_agent_answered ) ;
switch_event_add_header ( event , SWITCH_STACK_BOTTOM , " CC-Member-Leaving-Time " , " % " SWITCH_TIME_T_FMT ,
local_epoch_time_now ( NULL ) ) ;
switch_event_add_header ( event , SWITCH_STACK_BOTTOM , " CC-Member-Joined-Time " , " % " SWITCH_TIME_T_FMT ,
t_member_called ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Member-UUID " , h - > member_uuid ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Member-Session-UUID " ,
h - > member_session_uuid ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Member-CID-Name " , h - > member_cid_name ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Member-CID-Number " ,
h - > member_cid_number ) ;
switch_event_fire ( & event ) ;
}
2010-08-13 13:48:37 +00:00
}
} else {
/* Agent didn't answer or originate failed */
2011-03-12 16:58:35 +00:00
int delay_next_agent_call = 0 ;
2017-08-10 05:36:58 +00:00
switch_channel_t * member_channel = switch_core_session_get_channel ( member_session ) ;
switch_channel_clear_app_flag_key ( CC_APP_KEY , member_channel , CC_APP_AGENT_CONNECTING ) ;
2015-02-18 13:26:02 +00:00
sql = switch_mprintf ( " UPDATE members SET state = case state when '%q' then '%q' else state end, serving_agent = '', serving_system = '' "
2019-08-14 14:28:37 +00:00
" WHERE serving_agent = '%q' AND serving_system = '%q' AND uuid = '%q' AND instance_id = '%q' " ,
2015-02-18 13:26:02 +00:00
cc_member_state2str ( CC_MEMBER_STATE_TRYING ) , /* Only switch to Waiting from Trying (state may be set to Abandoned in callcenter_function()) */
2010-08-13 13:48:37 +00:00
cc_member_state2str ( CC_MEMBER_STATE_WAITING ) ,
2017-07-12 16:01:06 +00:00
h - > agent_name , h - > agent_system , h - > member_uuid , globals . cc_instance_id ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
2017-10-17 19:26:57 +00:00
bridged = 0 ;
2011-04-22 06:22:30 +00:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( member_session ) , SWITCH_LOG_DEBUG , " Agent %s Origination Canceled : %s \n " , h - > agent_name , switch_channel_cause2str ( cause ) ) ;
2010-08-13 13:48:37 +00:00
switch ( cause ) {
2011-03-12 16:58:35 +00:00
/* When we hang-up agents that did not answer in ring-all strategy */
2017-03-03 11:23:57 +00:00
case SWITCH_CAUSE_LOSE_RACE :
2011-03-12 16:58:35 +00:00
case SWITCH_CAUSE_ORIGINATOR_CANCEL :
2010-08-20 01:43:07 +00:00
break ;
2011-03-12 16:58:35 +00:00
/* Busy: Do Not Disturb, Circuit congestion */
case SWITCH_CAUSE_NORMAL_CIRCUIT_CONGESTION :
case SWITCH_CAUSE_USER_BUSY :
delay_next_agent_call = ( h - > busy_delay_time > delay_next_agent_call ? h - > busy_delay_time : delay_next_agent_call ) ;
2010-08-20 01:43:07 +00:00
break ;
2011-03-12 16:58:35 +00:00
/* Reject: User rejected the call */
case SWITCH_CAUSE_CALL_REJECTED :
delay_next_agent_call = ( h - > reject_delay_time > delay_next_agent_call ? h - > reject_delay_time : delay_next_agent_call ) ;
2010-08-13 13:48:37 +00:00
break ;
2016-11-21 22:32:22 +00:00
/* Protection againts super fast loop due to unregistrer */
2011-07-17 21:00:38 +00:00
case SWITCH_CAUSE_USER_NOT_REGISTERED :
2013-05-25 14:02:33 +00:00
delay_next_agent_call = 5 ;
2011-07-17 21:00:38 +00:00
break ;
2011-03-12 16:58:35 +00:00
/* No answer: Destination does not answer for some other reason */
2010-08-13 13:48:37 +00:00
default :
2011-03-12 16:58:35 +00:00
delay_next_agent_call = ( h - > no_answer_delay_time > delay_next_agent_call ? h - > no_answer_delay_time : delay_next_agent_call ) ;
2010-08-13 13:48:37 +00:00
tiers_state = CC_TIER_STATE_NO_ANSWER ;
/* Update Agent NO Answer count */
2019-08-14 14:28:37 +00:00
sql = switch_mprintf ( " UPDATE agents SET no_answer_count = no_answer_count + 1 WHERE name = '%q' AND instance_id = '%q'; " ,
2010-08-13 13:48:37 +00:00
h - > agent_name , h - > agent_system ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
2016-10-05 02:07:42 +00:00
/* Change Agent Status because he didn't answer often */
2010-08-13 13:48:37 +00:00
if ( h - > max_no_answer > 0 & & ( h - > no_answer_count + 1 ) > = h - > max_no_answer ) {
2016-10-05 02:07:42 +00:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( member_session ) , SWITCH_LOG_DEBUG , " Agent %s reach maximum no answer of %d, setting agent status to %s \n " ,
h - > agent_name , h - > max_no_answer , cc_agent_status2str ( h - > agent_no_answer_status ) ) ;
2016-11-14 10:01:41 +00:00
if ( switch_event_create_subclass ( & event , SWITCH_EVENT_CUSTOM , CALLCENTER_EVENT ) = = SWITCH_STATUS_SUCCESS ) {
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Agent " , h - > agent_name ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Action " , " agent-max-no-answer " ) ;
switch_event_add_header ( event , SWITCH_STACK_BOTTOM , " CC-Agent-No-Answer-Count " , " %d " , h - > max_no_answer ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Agent-No-Answer-Status " , cc_agent_status2str ( h - > agent_no_answer_status ) ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Member-UUID " , h - > member_uuid ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Member-Session-UUID " , h - > member_session_uuid ) ;
switch_event_fire ( & event ) ;
}
2017-07-18 16:25:22 +00:00
cc_agent_update ( " status " , cc_agent_status2str ( h - > agent_no_answer_status ) , h - > agent_name ) ;
2010-08-13 13:48:37 +00:00
}
2011-03-12 16:58:35 +00:00
break ;
}
/* Put agent to sleep for some time if necessary */
if ( delay_next_agent_call > 0 ) {
char ready_epoch [ 64 ] ;
2011-04-23 21:01:35 +00:00
switch_snprintf ( ready_epoch , sizeof ( ready_epoch ) , " % " SWITCH_TIME_T_FMT , local_epoch_time_now ( NULL ) + delay_next_agent_call ) ;
2017-07-18 16:25:22 +00:00
cc_agent_update ( " ready_time " , ready_epoch , h - > agent_name ) ;
2011-04-22 06:22:30 +00:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( member_session ) , SWITCH_LOG_DEBUG , " Agent %s sleeping for %d seconds \n " , h - > agent_name , delay_next_agent_call ) ;
2010-08-13 13:48:37 +00:00
}
2011-03-12 16:29:20 +00:00
/* Fire up event when contact agent fails */
if ( switch_event_create_subclass ( & event , SWITCH_EVENT_CUSTOM , CALLCENTER_EVENT ) = = SWITCH_STATUS_SUCCESS ) {
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Queue " , h - > queue_name ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Action " , " bridge-agent-fail " ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Hangup-Cause " , switch_channel_cause2str ( cause ) ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Agent " , h - > agent_name ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Agent-System " , h - > agent_system ) ;
2012-11-29 20:42:46 +00:00
switch_event_add_header ( event , SWITCH_STACK_BOTTOM , " CC-Agent-Called-Time " , " % " SWITCH_TIME_T_FMT , t_agent_called ) ;
switch_event_add_header ( event , SWITCH_STACK_BOTTOM , " CC-Agent-Aborted-Time " , " % " SWITCH_TIME_T_FMT , local_epoch_time_now ( NULL ) ) ;
2011-03-31 09:17:04 +00:00
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Member-UUID " , h - > member_uuid ) ;
2011-03-31 09:22:50 +00:00
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Member-Session-UUID " , h - > member_session_uuid ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Member-CID-Name " , h - > member_cid_name ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Member-CID-Number " , h - > member_cid_number ) ;
2012-11-29 20:42:46 +00:00
switch_event_add_header ( event , SWITCH_STACK_BOTTOM , " CC-Member-Joined-Time " , " % " SWITCH_TIME_T_FMT , t_member_called ) ;
2011-03-12 16:29:20 +00:00
switch_event_fire ( & event ) ;
}
2010-08-13 13:48:37 +00:00
}
done :
2017-07-18 16:25:22 +00:00
/* Make Agent Available Again */
sql = switch_mprintf (
" UPDATE tiers SET state = '%q' WHERE agent = '%q' AND queue = '%q' AND (state = '%q' OR state = '%q' OR state = '%q'); "
" UPDATE tiers SET state = '%q' WHERE agent = '%q' AND NOT queue = '%q' AND state = '%q' "
, cc_tier_state2str ( tiers_state ) , h - > agent_name , h - > queue_name , cc_tier_state2str ( CC_TIER_STATE_ACTIVE_INBOUND ) , cc_tier_state2str ( CC_TIER_STATE_STANDBY ) , cc_tier_state2str ( CC_TIER_STATE_OFFERING ) ,
cc_tier_state2str ( CC_TIER_STATE_READY ) , h - > agent_name , h - > queue_name , cc_tier_state2str ( CC_TIER_STATE_STANDBY ) ) ;
cc_execute_sql ( NULL , sql , NULL ) ;
switch_safe_free ( sql ) ;
/* If we are in Status Available On Demand, set state to Idle so we do not receive another call until state manually changed to Waiting */
2017-10-17 19:26:57 +00:00
if ( ! strcasecmp ( cc_agent_status2str ( CC_AGENT_STATUS_AVAILABLE_ON_DEMAND ) , h - > agent_status ) & & bridged ) {
2017-07-18 16:25:22 +00:00
cc_agent_update ( " state " , cc_agent_state2str ( CC_AGENT_STATE_IDLE ) , h - > agent_name ) ;
} else {
cc_agent_update ( " state " , cc_agent_state2str ( CC_AGENT_STATE_WAITING ) , h - > agent_name ) ;
2010-08-13 13:48:37 +00:00
}
2017-07-18 16:25:22 +00:00
2010-08-13 13:48:37 +00:00
if ( agent_session ) {
switch_core_session_rwunlock ( agent_session ) ;
}
if ( member_session ) {
switch_core_session_rwunlock ( member_session ) ;
}
switch_core_destroy_memory_pool ( & h - > pool ) ;
switch_mutex_lock ( globals . mutex ) ;
globals . threads - - ;
switch_mutex_unlock ( globals . mutex ) ;
return NULL ;
}
struct agent_callback {
2010-09-08 04:07:19 +00:00
const char * queue_name ;
2010-08-13 13:48:37 +00:00
const char * system ;
2011-03-31 09:17:04 +00:00
const char * member_uuid ;
const char * member_session_uuid ;
2011-03-31 09:22:50 +00:00
const char * member_cid_number ;
const char * member_cid_name ;
2011-03-31 09:17:04 +00:00
const char * member_joined_epoch ;
const char * member_score ;
2017-01-05 15:18:28 +00:00
const char * member_system ;
2010-08-13 13:48:37 +00:00
const char * strategy ;
const char * record_template ;
2010-08-26 01:40:33 +00:00
switch_bool_t tier_rules_apply ;
uint32_t tier_rule_wait_second ;
switch_bool_t tier_rule_wait_multiply_level ;
switch_bool_t tier_rule_no_agent_no_wait ;
2010-09-08 04:07:19 +00:00
switch_bool_t agent_found ;
2016-10-05 02:07:42 +00:00
switch_bool_t skip_agents_with_external_calls ;
cc_agent_status_t agent_no_answer_status ;
2010-08-26 01:40:33 +00:00
2010-08-13 13:48:37 +00:00
int tier ;
2010-08-26 01:40:33 +00:00
int tier_agent_available ;
2010-08-13 13:48:37 +00:00
} ;
typedef struct agent_callback agent_callback_t ;
static int agents_callback ( void * pArg , int argc , char * * argv , char * * columnNames )
{
agent_callback_t * cbt = ( agent_callback_t * ) pArg ;
char * sql = NULL ;
char res [ 256 ] ;
2011-03-31 09:17:04 +00:00
const char * agent_system = argv [ 0 ] ;
const char * agent_name = argv [ 1 ] ;
const char * agent_status = argv [ 2 ] ;
const char * agent_originate_string = argv [ 3 ] ;
const char * agent_no_answer_count = argv [ 4 ] ;
const char * agent_max_no_answer = argv [ 5 ] ;
const char * agent_reject_delay_time = argv [ 6 ] ;
const char * agent_busy_delay_time = argv [ 7 ] ;
const char * agent_no_answer_delay_time = argv [ 8 ] ;
const char * agent_tier_state = argv [ 9 ] ;
const char * agent_last_bridge_end = argv [ 10 ] ;
const char * agent_wrap_up_time = argv [ 11 ] ;
const char * agent_state = argv [ 12 ] ;
const char * agent_ready_time = argv [ 13 ] ;
2011-04-19 05:25:09 +00:00
const char * agent_tier_position = argv [ 14 ] ;
const char * agent_tier_level = argv [ 15 ] ;
const char * agent_type = argv [ 16 ] ;
const char * agent_uuid = argv [ 17 ] ;
2016-10-05 02:07:42 +00:00
const char * agent_external_calls_count = argv [ 18 ] ;
2010-08-26 01:40:33 +00:00
switch_bool_t contact_agent = SWITCH_TRUE ;
2010-09-08 04:07:19 +00:00
cbt - > agent_found = SWITCH_TRUE ;
2010-08-26 01:40:33 +00:00
/* Check if we switch to a different tier, if so, check if we should continue further for that member */
if ( cbt - > tier_rules_apply = = SWITCH_TRUE & & atoi ( agent_tier_level ) > cbt - > tier ) {
/* Continue if no agent was logged in in the previous tier and noagent = true */
if ( cbt - > tier_rule_no_agent_no_wait = = SWITCH_TRUE & & cbt - > tier_agent_available = = 0 ) {
cbt - > tier = atoi ( agent_tier_level ) ;
/* Multiple the tier level by the tier wait time */
2014-01-22 21:28:53 +00:00
} else if ( cbt - > tier_rule_wait_multiply_level = = SWITCH_TRUE & & ( long ) local_epoch_time_now ( NULL ) - atol ( cbt - > member_joined_epoch ) > = atoi ( agent_tier_level ) * ( int ) cbt - > tier_rule_wait_second ) {
2010-08-26 01:40:33 +00:00
cbt - > tier = atoi ( agent_tier_level ) ;
cbt - > tier_agent_available = 0 ;
/* Just check if joined is bigger than next tier wait time */
2014-01-22 21:28:53 +00:00
} else if ( cbt - > tier_rule_wait_multiply_level = = SWITCH_FALSE & & ( long ) local_epoch_time_now ( NULL ) - atol ( cbt - > member_joined_epoch ) > = ( int ) cbt - > tier_rule_wait_second ) {
2010-08-26 01:40:33 +00:00
cbt - > tier = atoi ( agent_tier_level ) ;
cbt - > tier_agent_available = 0 ;
} else {
/* We are not allowed to continue to the next tier of agent */
return 1 ;
}
}
cbt - > tier_agent_available + + ;
2010-08-13 13:48:37 +00:00
2010-08-26 01:40:33 +00:00
/* If Agent is not in a acceptable tier state, continue */
if ( ! ( ! strcasecmp ( agent_tier_state , cc_tier_state2str ( CC_TIER_STATE_NO_ANSWER ) ) | | ! strcasecmp ( agent_tier_state , cc_tier_state2str ( CC_TIER_STATE_READY ) ) ) ) {
contact_agent = SWITCH_FALSE ;
}
if ( ! ( ! strcasecmp ( agent_state , cc_agent_state2str ( CC_AGENT_STATE_WAITING ) ) ) ) {
contact_agent = SWITCH_FALSE ;
}
2011-04-23 21:01:35 +00:00
if ( ! ( atol ( agent_last_bridge_end ) < ( ( long ) local_epoch_time_now ( NULL ) - atol ( agent_wrap_up_time ) ) ) ) {
2010-08-26 01:40:33 +00:00
contact_agent = SWITCH_FALSE ;
}
2011-04-23 21:01:35 +00:00
if ( ! ( atol ( agent_ready_time ) < = ( long ) local_epoch_time_now ( NULL ) ) ) {
2010-08-26 01:40:33 +00:00
contact_agent = SWITCH_FALSE ;
}
if ( ! ( strcasecmp ( agent_status , cc_agent_status2str ( CC_AGENT_STATUS_ON_BREAK ) ) ) ) {
contact_agent = SWITCH_FALSE ;
}
2016-10-05 02:07:42 +00:00
/* XXX callcenter_track app can update this counter after we selected this agent on database */
if ( cbt - > skip_agents_with_external_calls & & atoi ( agent_external_calls_count ) > 0 ) {
contact_agent = SWITCH_FALSE ;
}
2010-08-26 01:40:33 +00:00
if ( contact_agent = = SWITCH_FALSE ) {
return 0 ; /* Continue to next Agent */
2010-08-13 13:48:37 +00:00
}
/* If agent isn't on this box */
2011-03-31 09:17:04 +00:00
if ( strcasecmp ( agent_system , " single_box " /* SELF */ ) ) {
2010-08-13 13:48:37 +00:00
if ( ! strcasecmp ( cbt - > strategy , " ring-all " ) ) {
return 1 ; /* Abort finding agent for member if we found a match but for a different Server */
} else {
return 0 ; /* Skip this Agents only, so we can ring the other one */
}
}
2017-07-18 16:25:22 +00:00
if ( globals . reserve_agents ) {
2015-05-03 04:10:02 +00:00
/* Updating agent state to Reserved only if it was Waiting previously, this is done to avoid race conditions
when updating agents table with external applications */
2017-07-18 16:25:22 +00:00
if ( cc_agent_update ( " state_if_waiting " , cc_agent_state2str ( CC_AGENT_STATE_RESERVED ) , agent_name ) = = CC_STATUS_SUCCESS ) {
2015-05-03 04:10:02 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Reserved Agent %s \n " , agent_name ) ;
} else {
/* Agent changed state just before we tried to update his state to Reserved. */
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Failed to Reserve Agent: %s. Skipping... \n " , agent_name ) ;
return 0 ;
}
}
2016-04-20 08:15:34 +00:00
if ( ! strcasecmp ( cbt - > strategy , " ring-all " ) | | ! strcasecmp ( cbt - > strategy , " ring-progressively " ) ) {
2010-08-13 13:48:37 +00:00
/* Check if member is a ring-all mode */
2019-08-14 14:28:37 +00:00
sql = switch_mprintf ( " SELECT count(*) FROM members WHERE serving_agent = '%q' AND uuid = '%q' AND instance_id = '%q' " , cbt - > strategy , cbt - > member_uuid , globals . cc_instance_id ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql2str ( NULL , NULL , sql , res , sizeof ( res ) ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
} else {
/* Map the Agent to the member */
2017-01-05 15:18:28 +00:00
sql = switch_mprintf ( " UPDATE members SET serving_agent = '%q', serving_system = '%q', state = '%q' "
2019-08-14 14:28:37 +00:00
" WHERE state = '%q' AND uuid = '%q' AND instance_id = '%q' " ,
2017-01-05 15:18:28 +00:00
agent_name , agent_system , cc_member_state2str ( CC_MEMBER_STATE_TRYING ) ,
2017-07-12 16:01:06 +00:00
cc_member_state2str ( CC_MEMBER_STATE_WAITING ) , cbt - > member_uuid , globals . cc_instance_id ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
/* Check if we won the race to get the member to our selected agent (Used for Multi system purposes) */
2019-08-14 14:28:37 +00:00
sql = switch_mprintf ( " SELECT count(*) FROM members WHERE serving_agent = '%q' AND serving_system = '%q' AND uuid = '%q' AND instance_id = '%q' " ,
2017-07-12 16:01:06 +00:00
agent_name , agent_system , cbt - > member_uuid , globals . cc_instance_id ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql2str ( NULL , NULL , sql , res , sizeof ( res ) ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
}
switch ( atoi ( res ) ) {
case 0 : /* Ok, someone else took it, or user hanged up already */
return 1 ;
/* We default to default even if more entry is returned... Should never happen anyway */
default : /* Go ahead, start thread to try to bridge these 2 caller */
{
switch_thread_t * thread ;
switch_threadattr_t * thd_attr = NULL ;
switch_memory_pool_t * pool ;
struct call_helper * h ;
switch_core_new_memory_pool ( & pool ) ;
h = switch_core_alloc ( pool , sizeof ( * h ) ) ;
h - > pool = pool ;
2011-03-31 09:17:04 +00:00
h - > member_uuid = switch_core_strdup ( h - > pool , cbt - > member_uuid ) ;
h - > member_session_uuid = switch_core_strdup ( h - > pool , cbt - > member_session_uuid ) ;
2010-08-13 13:48:37 +00:00
h - > queue_strategy = switch_core_strdup ( h - > pool , cbt - > strategy ) ;
2011-03-31 09:17:04 +00:00
h - > originate_string = switch_core_strdup ( h - > pool , agent_originate_string ) ;
h - > agent_name = switch_core_strdup ( h - > pool , agent_name ) ;
2017-01-05 15:18:28 +00:00
h - > agent_system = switch_core_strdup ( h - > pool , agent_system ) ;
2011-03-31 09:17:04 +00:00
h - > agent_status = switch_core_strdup ( h - > pool , agent_status ) ;
2010-08-26 01:40:33 +00:00
h - > agent_type = switch_core_strdup ( h - > pool , agent_type ) ;
h - > agent_uuid = switch_core_strdup ( h - > pool , agent_uuid ) ;
2016-11-21 22:32:22 +00:00
h - > member_joined_epoch = switch_core_strdup ( h - > pool , cbt - > member_joined_epoch ) ;
2011-03-31 09:22:50 +00:00
h - > member_cid_name = switch_core_strdup ( h - > pool , cbt - > member_cid_name ) ;
h - > member_cid_number = switch_core_strdup ( h - > pool , cbt - > member_cid_number ) ;
2010-09-08 04:07:19 +00:00
h - > queue_name = switch_core_strdup ( h - > pool , cbt - > queue_name ) ;
2010-08-13 13:48:37 +00:00
h - > record_template = switch_core_strdup ( h - > pool , cbt - > record_template ) ;
2011-03-31 09:17:04 +00:00
h - > no_answer_count = atoi ( agent_no_answer_count ) ;
h - > max_no_answer = atoi ( agent_max_no_answer ) ;
h - > reject_delay_time = atoi ( agent_reject_delay_time ) ;
h - > busy_delay_time = atoi ( agent_busy_delay_time ) ;
h - > no_answer_delay_time = atoi ( agent_no_answer_delay_time ) ;
2016-10-05 02:07:42 +00:00
h - > agent_no_answer_status = cbt - > agent_no_answer_status ;
2010-08-13 13:48:37 +00:00
2016-04-20 08:15:34 +00:00
if ( ! strcasecmp ( cbt - > strategy , " ring-progressively " ) ) {
switch_core_session_t * member_session = switch_core_session_locate ( cbt - > member_session_uuid ) ;
if ( member_session ) {
switch_channel_t * member_channel = switch_core_session_get_channel ( member_session ) ;
switch_channel_set_variable_printf ( member_channel , " cc_last_originated_call " , " % " SWITCH_TIME_T_FMT , local_epoch_time_now ( NULL ) ) ;
switch_core_session_rwunlock ( member_session ) ;
}
}
2011-04-19 05:25:09 +00:00
if ( ! strcasecmp ( cbt - > strategy , " top-down " ) ) {
switch_core_session_t * member_session = switch_core_session_locate ( cbt - > member_session_uuid ) ;
if ( member_session ) {
switch_channel_t * member_channel = switch_core_session_get_channel ( member_session ) ;
switch_channel_set_variable ( member_channel , " cc_last_agent_tier_position " , agent_tier_position ) ;
switch_channel_set_variable ( member_channel , " cc_last_agent_tier_level " , agent_tier_level ) ;
switch_core_session_rwunlock ( member_session ) ;
}
}
2017-07-18 16:25:22 +00:00
cc_agent_update ( " state " , cc_agent_state2str ( CC_AGENT_STATE_RECEIVING ) , h - > agent_name ) ;
2010-08-13 13:48:37 +00:00
sql = switch_mprintf (
" UPDATE tiers SET state = '%q' WHERE agent = '%q' AND queue = '%q'; "
" UPDATE tiers SET state = '%q' WHERE agent = '%q' AND NOT queue = '%q' AND state = '%q'; " ,
2010-09-08 04:07:19 +00:00
cc_tier_state2str ( CC_TIER_STATE_OFFERING ) , h - > agent_name , h - > queue_name ,
cc_tier_state2str ( CC_TIER_STATE_STANDBY ) , h - > agent_name , h - > queue_name , cc_tier_state2str ( CC_TIER_STATE_READY ) ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
switch_threadattr_create ( & thd_attr , h - > pool ) ;
switch_threadattr_detach_set ( thd_attr , 1 ) ;
switch_threadattr_stacksize_set ( thd_attr , SWITCH_THREAD_STACKSIZE ) ;
switch_thread_create ( & thread , thd_attr , outbound_agent_thread_run , h , h - > pool ) ;
}
if ( ! strcasecmp ( cbt - > strategy , " ring-all " ) ) {
return 0 ;
2016-04-20 08:15:34 +00:00
} else if ( ! strcasecmp ( cbt - > strategy , " ring-progressively " ) ) {
return 1 ;
2010-08-13 13:48:37 +00:00
} else {
return 1 ;
}
}
}
static int members_callback ( void * pArg , int argc , char * * argv , char * * columnNames )
{
cc_queue_t * queue = NULL ;
char * sql = NULL ;
char * sql_order_by = NULL ;
2010-08-13 23:15:24 +00:00
char * queue_name = NULL ;
char * queue_strategy = NULL ;
char * queue_record_template = NULL ;
2016-04-20 08:15:34 +00:00
uint32_t ring_progressively_delay = 10 ; /* default ring-progressively-delay set to 10 seconds */
2010-08-26 01:40:33 +00:00
switch_bool_t tier_rules_apply ;
uint32_t tier_rule_wait_second ;
switch_bool_t tier_rule_wait_multiply_level ;
switch_bool_t tier_rule_no_agent_no_wait ;
2010-08-26 10:19:58 +00:00
uint32_t discard_abandoned_after ;
2010-08-13 13:48:37 +00:00
agent_callback_t cbt ;
2011-03-31 09:17:04 +00:00
const char * member_state = NULL ;
const char * member_abandoned_epoch = NULL ;
2015-05-25 17:55:57 +00:00
const char * serving_agent = NULL ;
2016-04-20 08:15:34 +00:00
const char * last_originated_call = NULL ;
2011-03-31 09:17:04 +00:00
memset ( & cbt , 0 , sizeof ( cbt ) ) ;
2010-08-13 13:48:37 +00:00
2011-03-31 09:17:04 +00:00
cbt . queue_name = argv [ 0 ] ;
cbt . member_uuid = argv [ 1 ] ;
cbt . member_session_uuid = argv [ 2 ] ;
2011-03-31 09:22:50 +00:00
cbt . member_cid_number = argv [ 3 ] ;
cbt . member_cid_name = argv [ 4 ] ;
2011-03-31 09:17:04 +00:00
cbt . member_joined_epoch = argv [ 5 ] ;
cbt . member_score = argv [ 6 ] ;
member_state = argv [ 7 ] ;
member_abandoned_epoch = argv [ 8 ] ;
2015-05-25 17:55:57 +00:00
serving_agent = argv [ 9 ] ;
2017-01-05 15:18:28 +00:00
cbt . member_system = argv [ 10 ] ;
2011-03-31 09:17:04 +00:00
2017-07-18 16:25:22 +00:00
if ( ! cbt . queue_name | | ! ( queue = get_queue ( cbt . queue_name ) ) ) {
2017-05-11 12:27:34 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_WARNING , " Queue %s not found locally, delete this member \n " , cbt . queue_name ) ;
2019-08-14 14:28:37 +00:00
sql = switch_mprintf ( " DELETE FROM members WHERE uuid = '%q' AND instance_id = '%q' " , cbt . member_uuid , cbt . member_system ) ;
2017-07-18 16:09:39 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2017-05-11 12:27:34 +00:00
switch_safe_free ( sql ) ;
2010-08-13 13:48:37 +00:00
goto end ;
2010-08-13 23:15:24 +00:00
} else {
queue_name = strdup ( queue - > name ) ;
queue_strategy = strdup ( queue - > strategy ) ;
2010-08-26 01:40:33 +00:00
tier_rules_apply = queue - > tier_rules_apply ;
tier_rule_wait_second = queue - > tier_rule_wait_second ;
tier_rule_wait_multiply_level = queue - > tier_rule_wait_multiply_level ;
tier_rule_no_agent_no_wait = queue - > tier_rule_no_agent_no_wait ;
2010-08-26 10:19:58 +00:00
discard_abandoned_after = queue - > discard_abandoned_after ;
2016-04-20 08:15:34 +00:00
if ( queue - > ring_progressively_delay ) {
ring_progressively_delay = queue - > ring_progressively_delay ;
}
2011-04-23 01:55:56 +00:00
2010-08-13 23:15:24 +00:00
if ( queue - > record_template ) {
queue_record_template = strdup ( queue - > record_template ) ;
}
2016-10-05 02:07:42 +00:00
cbt . skip_agents_with_external_calls = queue - > skip_agents_with_external_calls ;
cbt . agent_no_answer_status = cc_agent_str2status ( queue - > agent_no_answer_status ) ;
2010-08-13 23:15:24 +00:00
queue_rwunlock ( queue ) ;
2010-08-13 13:48:37 +00:00
}
2011-03-31 09:17:04 +00:00
/* Checking for cleanup Abandonded calls from the db */
if ( ! strcasecmp ( member_state , cc_member_state2str ( CC_MEMBER_STATE_ABANDONED ) ) ) {
2011-05-18 03:51:47 +00:00
switch_time_t abandoned_epoch = atoll ( member_abandoned_epoch ) ;
2010-08-26 10:19:58 +00:00
if ( abandoned_epoch = = 0 ) {
2011-05-18 03:51:47 +00:00
abandoned_epoch = atoll ( cbt . member_joined_epoch ) ;
2010-08-26 10:19:58 +00:00
}
/* Once we pass a certain point, we want to get rid of the abandoned call */
2011-05-18 03:51:47 +00:00
if ( abandoned_epoch + discard_abandoned_after < local_epoch_time_now ( NULL ) ) {
2019-08-14 14:28:37 +00:00
sql = switch_mprintf ( " DELETE FROM members WHERE uuid = '%q' AND instance_id = '%q' AND (abandoned_epoch = '% " SWITCH_TIME_T_FMT " ' OR joined_epoch = '%q') " , cbt . member_uuid , cbt . member_system , abandoned_epoch , cbt . member_joined_epoch ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2010-08-26 10:19:58 +00:00
switch_safe_free ( sql ) ;
}
/* Skip this member */
2010-09-04 00:12:45 +00:00
goto end ;
2015-05-25 17:55:57 +00:00
}
/* Tracking queue strategy changes */
/* member is ring-all but not the queue */
if ( ! strcasecmp ( serving_agent , " ring-all " ) & & ( strcasecmp ( queue_strategy , " ring-all " ) ! = 0 ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Queue '%s' changed strategy, adjusting member parameters " , queue_name ) ;
2016-04-20 08:15:34 +00:00
/* member was ring-all, becomes ring-progressively (no state change because of strategy similarities) */
if ( ! strcasecmp ( queue_strategy , " ring-progressively " ) ) {
2016-11-11 17:27:35 +00:00
sql = switch_mprintf ( " UPDATE members SET serving_agent = 'ring-progressively' WHERE uuid = '%q' AND state = '%q' AND serving_agent = 'ring-all' " , cbt . member_uuid , cc_member_state2str ( CC_MEMBER_STATE_TRYING ) ) ;
2016-04-20 08:15:34 +00:00
} else {
2016-11-11 17:27:35 +00:00
sql = switch_mprintf ( " UPDATE members SET serving_agent = '', state = '%q' WHERE uuid = '%q' AND state = '%q' AND serving_agent = 'ring-all' " , cc_member_state2str ( CC_MEMBER_STATE_WAITING ) , cbt . member_uuid , cc_member_state2str ( CC_MEMBER_STATE_TRYING ) ) ;
2016-04-20 08:15:34 +00:00
}
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2016-04-20 08:15:34 +00:00
switch_safe_free ( sql ) ;
}
/* member is ring-progressively but not the queue */
else if ( ! strcasecmp ( serving_agent , " ring-progressively " ) & & ( strcasecmp ( queue_strategy , " ring-progressively " ) ! = 0 ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Queue '%s' changed strategy, adjusting member parameters " , queue_name ) ;
/* member was ring-progressively, becomes ring-all (no state change because of strategy similarities) */
if ( ! strcasecmp ( queue_strategy , " ring-all " ) ) {
2016-11-11 17:27:35 +00:00
sql = switch_mprintf ( " UPDATE members SET serving_agent = 'ring-all' WHERE uuid = '%q' AND state = '%q' AND serving_agent = 'ring-progressively' " , cbt . member_uuid , cc_member_state2str ( CC_MEMBER_STATE_TRYING ) ) ;
2016-04-20 08:15:34 +00:00
} else {
2016-11-11 17:27:35 +00:00
sql = switch_mprintf ( " UPDATE members SET serving_agent = '', state = '%q' WHERE uuid = '%q' AND state = '%q' AND serving_agent = 'ring-progressively' " , cc_member_state2str ( CC_MEMBER_STATE_WAITING ) , cbt . member_uuid , cc_member_state2str ( CC_MEMBER_STATE_TRYING ) ) ;
2016-04-20 08:15:34 +00:00
}
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2015-05-25 17:55:57 +00:00
switch_safe_free ( sql ) ;
}
/* Queue is now ring-all and not the member */
else if ( ! strcasecmp ( queue_strategy , " ring-all " ) & & ( strcasecmp ( serving_agent , " ring-all " ) ! = 0 ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Queue '%s' changed strategy, adjusting member parameters " , queue_name ) ;
2016-04-20 08:15:34 +00:00
/* member was ring-progressively, its state is already set to TRYING */
if ( ! strcasecmp ( serving_agent , " ring-progressively " ) ) {
2016-11-11 17:27:35 +00:00
sql = switch_mprintf ( " UPDATE members SET serving_agent = 'ring-all' WHERE uuid = '%q' AND state = '%q' AND serving_agent = 'ring-progressively' " , cbt . member_uuid , cc_member_state2str ( CC_MEMBER_STATE_TRYING ) ) ;
2016-04-20 08:15:34 +00:00
} else {
2016-11-11 17:27:35 +00:00
sql = switch_mprintf ( " UPDATE members SET serving_agent = 'ring-all', state = '%q' WHERE uuid = '%q' AND state = '%q' AND serving_agent = '' " , cc_member_state2str ( CC_MEMBER_STATE_TRYING ) , cbt . member_uuid , cc_member_state2str ( CC_MEMBER_STATE_WAITING ) ) ;
2016-04-20 08:15:34 +00:00
}
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2016-04-20 08:15:34 +00:00
switch_safe_free ( sql ) ;
}
/* Queue is now ring-progressively and not the member */
else if ( ! strcasecmp ( queue_strategy , " ring-progressively " ) & & ( strcasecmp ( serving_agent , " ring-progressively " ) ! = 0 ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Queue '%s' changed strategy, adjusting member parameters " , queue_name ) ;
/* member was ring-all, its state is already set to TRYING */
if ( ! strcasecmp ( serving_agent , " ring-all " ) ) {
2016-11-11 17:27:35 +00:00
sql = switch_mprintf ( " UPDATE members SET serving_agent = 'ring-progressively' WHERE uuid = '%q' AND state = '%q' AND serving_agent = 'ring-all' " , cbt . member_uuid , cc_member_state2str ( CC_MEMBER_STATE_TRYING ) ) ;
2016-04-20 08:15:34 +00:00
} else {
2016-11-11 17:27:35 +00:00
sql = switch_mprintf ( " UPDATE members SET serving_agent = 'ring-progressively', state = '%q' WHERE uuid = '%q' AND state = '%q' AND serving_agent = '' " , cc_member_state2str ( CC_MEMBER_STATE_TRYING ) , cbt . member_uuid , cc_member_state2str ( CC_MEMBER_STATE_WAITING ) ) ;
2016-04-20 08:15:34 +00:00
}
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2015-05-25 17:55:57 +00:00
switch_safe_free ( sql ) ;
}
2011-03-31 09:17:04 +00:00
/* Check if member is in the queue waiting */
if ( zstr ( cbt . member_session_uuid ) ) {
2011-03-31 09:22:50 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_WARNING , " Member %s <%s> in Queue %s have no session uuid, skip this member \n " , cbt . member_cid_name , cbt . member_cid_number , cbt . queue_name ) ;
2011-03-31 09:17:04 +00:00
}
2010-08-26 01:40:33 +00:00
cbt . tier = 0 ;
cbt . tier_agent_available = 0 ;
cbt . tier_rules_apply = tier_rules_apply ;
cbt . tier_rule_wait_second = tier_rule_wait_second ;
cbt . tier_rule_wait_multiply_level = tier_rule_wait_multiply_level ;
cbt . tier_rule_no_agent_no_wait = tier_rule_no_agent_no_wait ;
2010-08-13 23:15:24 +00:00
cbt . strategy = queue_strategy ;
cbt . record_template = queue_record_template ;
2010-09-08 04:07:19 +00:00
cbt . agent_found = SWITCH_FALSE ;
2011-04-19 05:25:09 +00:00
2017-07-18 16:25:22 +00:00
if ( ! strcasecmp ( queue - > strategy , " top-down " ) ) {
2011-04-19 05:25:09 +00:00
/* WARNING this use channel variable to help dispatch... might need to be reviewed to save it in DB to make this multi server prooft in the future */
switch_core_session_t * member_session = switch_core_session_locate ( cbt . member_session_uuid ) ;
int position = 0 , level = 0 ;
const char * last_agent_tier_position , * last_agent_tier_level ;
if ( member_session ) {
switch_channel_t * member_channel = switch_core_session_get_channel ( member_session ) ;
if ( ( last_agent_tier_position = switch_channel_get_variable ( member_channel , " cc_last_agent_tier_position " ) ) ) {
position = atoi ( last_agent_tier_position ) ;
}
if ( ( last_agent_tier_level = switch_channel_get_variable ( member_channel , " cc_last_agent_tier_level " ) ) ) {
2011-04-19 07:05:32 +00:00
level = atoi ( last_agent_tier_level ) ;
2011-04-19 05:25:09 +00:00
}
switch_core_session_rwunlock ( member_session ) ;
}
2019-08-14 14:28:37 +00:00
sql = switch_mprintf ( " SELECT instance_id, name, status, contact, no_answer_count, max_no_answer, reject_delay_time, busy_delay_time, no_answer_delay_time, tiers.state, agents.last_bridge_end, agents.wrap_up_time, agents.state, agents.ready_time, tiers.position as tiers_position, tiers.level as tiers_level, agents.type, agents.uuid, external_calls_count, agents.last_offered_call as agents_last_offered_call, 1 as dyn_order FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent) "
2011-04-19 05:25:09 +00:00
" WHERE tiers.queue = '%q' "
2017-07-18 16:09:39 +00:00
" AND (agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q') "
2011-04-19 05:25:09 +00:00
" AND tiers.position > %d "
" AND tiers.level = %d "
" UNION "
2019-08-14 14:28:37 +00:00
" SELECT instance_id, name, status, contact, no_answer_count, max_no_answer, reject_delay_time, busy_delay_time, no_answer_delay_time, tiers.state, agents.last_bridge_end, agents.wrap_up_time, agents.state, agents.ready_time, tiers.position as tiers_position, tiers.level as tiers_level, agents.type, agents.uuid, external_calls_count, agents.last_offered_call as agents_last_offered_call, 2 as dyn_order FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent) "
2011-04-19 05:25:09 +00:00
" WHERE tiers.queue = '%q' "
2017-07-18 16:09:39 +00:00
" AND (agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q') "
2017-07-19 01:46:51 +00:00
" AND tiers.level > %d "
2011-04-19 05:25:09 +00:00
" ORDER BY dyn_order asc, tiers_level, tiers_position, agents_last_offered_call " ,
queue_name ,
2017-07-18 16:09:39 +00:00
cc_agent_status2str ( CC_AGENT_STATUS_AVAILABLE ) , cc_agent_status2str ( CC_AGENT_STATUS_ON_BREAK ) , cc_agent_status2str ( CC_AGENT_STATUS_AVAILABLE_ON_DEMAND ) ,
2011-04-19 05:25:09 +00:00
position ,
level ,
queue_name ,
2017-07-19 01:46:51 +00:00
cc_agent_status2str ( CC_AGENT_STATUS_AVAILABLE ) , cc_agent_status2str ( CC_AGENT_STATUS_ON_BREAK ) , cc_agent_status2str ( CC_AGENT_STATUS_AVAILABLE_ON_DEMAND ) ,
level
2011-04-19 05:25:09 +00:00
) ;
2017-07-18 16:25:22 +00:00
} else if ( ! strcasecmp ( queue - > strategy , " round-robin " ) ) {
2019-08-14 14:28:37 +00:00
sql = switch_mprintf ( " SELECT instance_id, name, status, contact, no_answer_count, max_no_answer, reject_delay_time, busy_delay_time, no_answer_delay_time, tiers.state, agents.last_bridge_end, agents.wrap_up_time, agents.state, agents.ready_time, tiers.position as tiers_position, tiers.level as tiers_level, agents.type, agents.uuid, external_calls_count, agents.last_offered_call as agents_last_offered_call, 1 as dyn_order FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent) "
2011-04-17 16:46:32 +00:00
" WHERE tiers.queue = '%q' "
2017-07-18 16:09:39 +00:00
" AND (agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q') "
2011-04-19 04:21:31 +00:00
" AND tiers.position > (SELECT tiers.position FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent) WHERE tiers.queue = '%q' AND agents.last_offered_call > 0 ORDER BY agents.last_offered_call DESC LIMIT 1) "
" AND tiers.level = (SELECT tiers.level FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent) WHERE tiers.queue = '%q' AND agents.last_offered_call > 0 ORDER BY agents.last_offered_call DESC LIMIT 1) "
2011-04-17 16:46:32 +00:00
" UNION "
2019-08-14 14:28:37 +00:00
" SELECT instance_id, name, status, contact, no_answer_count, max_no_answer, reject_delay_time, busy_delay_time, no_answer_delay_time, tiers.state, agents.last_bridge_end, agents.wrap_up_time, agents.state, agents.ready_time, tiers.position as tiers_position, tiers.level as tiers_level, agents.type, agents.uuid, external_calls_count, agents.last_offered_call as agents_last_offered_call, 2 as dyn_order FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent) "
2011-04-17 16:46:32 +00:00
" WHERE tiers.queue = '%q' "
2017-07-18 16:09:39 +00:00
" AND (agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q') "
2011-04-18 21:16:51 +00:00
" ORDER BY dyn_order asc, tiers_level, tiers_position, agents_last_offered_call " ,
2011-04-17 16:46:32 +00:00
queue_name ,
2017-07-18 16:09:39 +00:00
cc_agent_status2str ( CC_AGENT_STATUS_AVAILABLE ) , cc_agent_status2str ( CC_AGENT_STATUS_ON_BREAK ) , cc_agent_status2str ( CC_AGENT_STATUS_AVAILABLE_ON_DEMAND ) ,
2011-04-17 16:46:32 +00:00
queue_name ,
queue_name ,
queue_name ,
2017-07-18 16:09:39 +00:00
cc_agent_status2str ( CC_AGENT_STATUS_AVAILABLE ) , cc_agent_status2str ( CC_AGENT_STATUS_ON_BREAK ) , cc_agent_status2str ( CC_AGENT_STATUS_AVAILABLE_ON_DEMAND )
2011-04-17 16:46:32 +00:00
) ;
2010-08-13 13:48:37 +00:00
} else {
2017-07-18 16:25:22 +00:00
if ( ! strcasecmp ( queue - > strategy , " longest-idle-agent " ) ) {
2014-12-04 19:12:20 +00:00
sql_order_by = switch_mprintf ( " level, agents.last_bridge_end, position " ) ;
2011-04-17 16:46:32 +00:00
} else if ( ! strcasecmp ( queue_strategy , " agent-with-least-talk-time " ) ) {
sql_order_by = switch_mprintf ( " level, agents.talk_time, position " ) ;
} else if ( ! strcasecmp ( queue_strategy , " agent-with-fewest-calls " ) ) {
sql_order_by = switch_mprintf ( " level, agents.calls_answered, position " ) ;
2016-04-20 08:15:34 +00:00
} else if ( ! strcasecmp ( queue_strategy , " ring-all " ) | | ! strcasecmp ( queue_strategy , " ring-progressively " ) ) {
2019-08-14 14:28:37 +00:00
sql = switch_mprintf ( " UPDATE members SET state = '%q' WHERE state = '%q' AND uuid = '%q' AND instance_id = '%q' " ,
2017-01-05 15:18:28 +00:00
cc_member_state2str ( CC_MEMBER_STATE_TRYING ) , cc_member_state2str ( CC_MEMBER_STATE_WAITING ) , cbt . member_uuid , cbt . member_system ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2011-04-17 16:46:32 +00:00
switch_safe_free ( sql ) ;
sql_order_by = switch_mprintf ( " level, position " ) ;
2011-04-19 04:21:31 +00:00
} else if ( ! strcasecmp ( queue_strategy , " random " ) ) {
sql_order_by = switch_mprintf ( " level, random() " ) ;
2011-04-17 16:46:32 +00:00
} else if ( ! strcasecmp ( queue_strategy , " sequentially-by-agent-order " ) ) {
sql_order_by = switch_mprintf ( " level, position, agents.last_offered_call " ) ; /* Default to last_offered_call, let add new strategy if needing it differently */
} else {
/* If the strategy doesn't exist, just fallback to the following */
sql_order_by = switch_mprintf ( " level, position, agents.last_offered_call " ) ;
}
2019-08-14 14:28:37 +00:00
sql = switch_mprintf ( " SELECT instance_id, name, status, contact, no_answer_count, max_no_answer, reject_delay_time, busy_delay_time, no_answer_delay_time, tiers.state, agents.last_bridge_end, agents.wrap_up_time, agents.state, agents.ready_time, tiers.position, tiers.level, agents.type, agents.uuid, external_calls_count FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent) "
2011-04-17 16:46:32 +00:00
" WHERE tiers.queue = '%q' "
2017-07-18 16:09:39 +00:00
" AND (agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q') "
2011-04-17 16:46:32 +00:00
" ORDER BY %q " ,
queue_name ,
2017-07-18 16:09:39 +00:00
cc_agent_status2str ( CC_AGENT_STATUS_AVAILABLE ) , cc_agent_status2str ( CC_AGENT_STATUS_ON_BREAK ) , cc_agent_status2str ( CC_AGENT_STATUS_AVAILABLE_ON_DEMAND ) ,
2011-04-17 16:46:32 +00:00
sql_order_by ) ;
switch_safe_free ( sql_order_by ) ;
}
2010-08-13 13:48:37 +00:00
2017-07-18 16:25:22 +00:00
if ( ! strcasecmp ( queue - > strategy , " ring-progressively " ) ) {
2016-04-20 08:15:34 +00:00
switch_core_session_t * member_session = switch_core_session_locate ( cbt . member_session_uuid ) ;
2016-11-21 22:32:22 +00:00
2016-04-20 08:15:34 +00:00
if ( member_session ) {
switch_channel_t * member_channel = switch_core_session_get_channel ( member_session ) ;
last_originated_call = switch_channel_get_variable ( member_channel , " cc_last_originated_call " ) ;
2016-11-21 22:32:22 +00:00
2016-04-20 08:15:34 +00:00
if ( last_originated_call & & switch_channel_ready ( member_channel ) & & ( ( long ) local_epoch_time_now ( NULL ) < atoi ( last_originated_call ) + ring_progressively_delay ) & & ! switch_true ( switch_channel_get_variable ( member_channel , " cc_agent_found " ) ) ) {
/* We wait for 500 ms here */
switch_yield ( 500000 ) ;
switch_core_session_rwunlock ( member_session ) ;
goto end ;
}
2016-11-21 22:32:22 +00:00
2016-04-20 08:15:34 +00:00
switch_core_session_rwunlock ( member_session ) ;
}
}
2017-07-18 16:25:22 +00:00
cc_execute_sql_callback ( NULL /* queue */ , NULL /* mutex */ , sql , agents_callback , & cbt /* Call back variables */ ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
2010-09-08 04:07:19 +00:00
/* We update a field in the queue struct so we can kick caller out if waiting for too long with no agent */
2017-07-18 16:25:22 +00:00
if ( ! cbt . queue_name | | ! ( queue = get_queue ( cbt . queue_name ) ) ) {
2011-03-31 09:17:04 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_WARNING , " Queue %s not found locally, skip this member \n " , cbt . queue_name ) ;
2010-09-08 04:07:19 +00:00
goto end ;
} else {
2011-04-23 21:01:35 +00:00
queue - > last_agent_exist_check = local_epoch_time_now ( NULL ) ;
2010-09-08 04:07:19 +00:00
if ( cbt . agent_found ) {
queue - > last_agent_exist = queue - > last_agent_exist_check ;
2017-07-19 01:46:51 +00:00
} else {
/* If no agent found in top-down mode, restart to the begining */
if ( ! strcasecmp ( queue - > strategy , " top-down " ) ) {
switch_core_session_t * member_session = switch_core_session_locate ( cbt . member_session_uuid ) ;
if ( member_session ) {
switch_channel_t * member_channel = switch_core_session_get_channel ( member_session ) ;
switch_channel_set_variable ( member_channel , " cc_last_agent_tier_position " , NULL ) ;
switch_channel_set_variable ( member_channel , " cc_last_agent_tier_level " , NULL ) ;
switch_core_session_rwunlock ( member_session ) ;
}
}
2010-09-08 04:07:19 +00:00
}
queue_rwunlock ( queue ) ;
}
2010-08-13 13:48:37 +00:00
end :
2010-08-13 23:15:24 +00:00
switch_safe_free ( queue_name ) ;
switch_safe_free ( queue_strategy ) ;
switch_safe_free ( queue_record_template ) ;
2010-08-13 13:48:37 +00:00
return 0 ;
}
2017-07-18 16:25:22 +00:00
static int AGENT_DISPATCH_THREAD_RUNNING = 0 ;
static int AGENT_DISPATCH_THREAD_STARTED = 0 ;
2010-08-13 13:48:37 +00:00
void * SWITCH_THREAD_FUNC cc_agent_dispatch_thread_run ( switch_thread_t * thread , void * obj )
{
int done = 0 ;
switch_mutex_lock ( globals . mutex ) ;
2017-07-18 16:25:22 +00:00
if ( ! AGENT_DISPATCH_THREAD_RUNNING ) {
AGENT_DISPATCH_THREAD_RUNNING + + ;
globals . threads + + ;
2010-08-13 13:48:37 +00:00
} else {
done = 1 ;
}
2017-07-18 16:25:22 +00:00
switch_mutex_unlock ( globals . mutex ) ;
2010-08-13 13:48:37 +00:00
if ( done ) {
2017-07-18 16:25:22 +00:00
return NULL ;
2010-08-13 13:48:37 +00:00
}
2017-07-18 16:25:22 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_CONSOLE , " Agent Dispatch Thread Started \n " ) ;
2010-08-13 13:48:37 +00:00
2017-07-18 16:25:22 +00:00
while ( globals . running = = 1 ) {
2010-08-13 13:48:37 +00:00
char * sql = NULL ;
2019-08-14 14:28:37 +00:00
sql = switch_mprintf ( " SELECT queue,uuid,session_uuid,cid_number,cid_name,joined_epoch,(% " SWITCH_TIME_T_FMT " -joined_epoch)+base_score+skill_score AS score, state, abandoned_epoch, serving_agent, instance_id FROM members "
" WHERE (state = '%q' OR state = '%q' OR (serving_agent = 'ring-all' AND state = '%q') OR (serving_agent = 'ring-progressively' AND state = '%q')) AND instance_id = '%q' ORDER BY score DESC " ,
2017-07-18 16:25:22 +00:00
local_epoch_time_now ( NULL ) ,
2017-07-12 16:01:06 +00:00
cc_member_state2str ( CC_MEMBER_STATE_WAITING ) , cc_member_state2str ( CC_MEMBER_STATE_ABANDONED ) , cc_member_state2str ( CC_MEMBER_STATE_TRYING ) , cc_member_state2str ( CC_MEMBER_STATE_TRYING ) , globals . cc_instance_id ) ;
2010-08-13 13:48:37 +00:00
2017-07-18 16:25:22 +00:00
cc_execute_sql_callback ( NULL /* queue */ , NULL /* mutex */ , sql , members_callback , NULL /* Call back variables */ ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
switch_yield ( 100000 ) ;
}
2017-07-18 16:25:22 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_CONSOLE , " Agent Dispatch Thread Ended \n " ) ;
2010-08-13 13:48:37 +00:00
switch_mutex_lock ( globals . mutex ) ;
globals . threads - - ;
2017-07-18 16:25:22 +00:00
AGENT_DISPATCH_THREAD_RUNNING = AGENT_DISPATCH_THREAD_STARTED = 0 ;
2010-08-13 13:48:37 +00:00
switch_mutex_unlock ( globals . mutex ) ;
return NULL ;
}
2017-07-18 16:25:22 +00:00
void cc_agent_dispatch_thread_start ( void )
2010-08-13 13:48:37 +00:00
{
switch_thread_t * thread ;
switch_threadattr_t * thd_attr = NULL ;
int done = 0 ;
2017-07-18 16:25:22 +00:00
switch_mutex_lock ( globals . mutex ) ;
2017-07-18 14:34:07 +00:00
2017-07-18 16:25:22 +00:00
if ( ! AGENT_DISPATCH_THREAD_STARTED ) {
AGENT_DISPATCH_THREAD_STARTED + + ;
2010-08-13 13:48:37 +00:00
} else {
done = 1 ;
}
2017-07-18 16:25:22 +00:00
switch_mutex_unlock ( globals . mutex ) ;
2010-08-13 13:48:37 +00:00
if ( done ) {
return ;
}
2017-07-18 16:25:22 +00:00
switch_threadattr_create ( & thd_attr , globals . pool ) ;
2010-08-13 13:48:37 +00:00
switch_threadattr_detach_set ( thd_attr , 1 ) ;
switch_threadattr_stacksize_set ( thd_attr , SWITCH_THREAD_STACKSIZE ) ;
2012-10-01 15:20:48 +00:00
switch_threadattr_priority_set ( thd_attr , SWITCH_PRI_REALTIME ) ;
2017-07-18 16:25:22 +00:00
switch_thread_create ( & thread , thd_attr , cc_agent_dispatch_thread_run , NULL , globals . pool ) ;
2010-08-13 13:48:37 +00:00
}
2010-09-08 04:07:19 +00:00
struct member_thread_helper {
const char * queue_name ;
2011-03-31 09:17:04 +00:00
const char * member_uuid ;
const char * member_session_uuid ;
2011-04-15 00:23:12 +00:00
const char * member_cid_name ;
const char * member_cid_number ;
2010-09-08 04:07:19 +00:00
switch_time_t t_member_called ;
cc_member_cancel_reason_t member_cancel_reason ;
2010-08-13 13:48:37 +00:00
int running ;
switch_memory_pool_t * pool ;
} ;
void * SWITCH_THREAD_FUNC cc_member_thread_run ( switch_thread_t * thread , void * obj )
{
2010-09-08 04:07:19 +00:00
struct member_thread_helper * m = ( struct member_thread_helper * ) obj ;
2011-03-31 09:17:04 +00:00
switch_core_session_t * member_session = switch_core_session_locate ( m - > member_session_uuid ) ;
2010-09-08 04:07:19 +00:00
switch_channel_t * member_channel = NULL ;
2014-02-24 13:38:31 +00:00
switch_time_t last_announce = local_epoch_time_now ( NULL ) ;
switch_bool_t announce_valid = SWITCH_TRUE ;
2010-08-13 13:48:37 +00:00
if ( member_session ) {
2010-09-08 04:07:19 +00:00
member_channel = switch_core_session_get_channel ( member_session ) ;
2010-08-13 13:48:37 +00:00
} else {
2017-07-18 16:25:22 +00:00
switch_core_destroy_memory_pool ( & m - > pool ) ;
return NULL ;
2017-07-18 14:34:07 +00:00
}
2017-07-18 16:25:22 +00:00
switch_mutex_lock ( globals . mutex ) ;
globals . threads + + ;
switch_mutex_unlock ( globals . mutex ) ;
2011-04-22 05:42:47 +00:00
2010-09-08 04:07:19 +00:00
while ( switch_channel_ready ( member_channel ) & & m - > running & & globals . running ) {
cc_queue_t * queue = NULL ;
2014-02-24 13:38:31 +00:00
switch_time_t time_now = local_epoch_time_now ( NULL ) ;
2010-09-08 04:07:19 +00:00
2017-07-18 16:25:22 +00:00
if ( ! m - > queue_name | | ! ( queue = get_queue ( m - > queue_name ) ) ) {
2011-04-22 06:22:30 +00:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( member_session ) , SWITCH_LOG_WARNING , " Queue %s not found \n " , m - > queue_name ) ;
2010-09-08 04:07:19 +00:00
break ;
}
/* Make the Caller Leave if he went over his max wait time */
2017-08-08 19:45:05 +00:00
if ( queue - > max_wait_time > 0 & & queue - > max_wait_time < = time_now - m - > t_member_called ) {
2017-08-10 05:36:58 +00:00
/* timeout reached, check if we're originating at this time and give caller a one more chance */
if ( switch_channel_test_app_flag_key ( CC_APP_KEY , member_channel , CC_APP_AGENT_CONNECTING ) ) {
2017-11-03 16:38:04 +00:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( member_session ) , SWITCH_LOG_DEBUG , " Member %s <%s> in queue '%s' reached max wait time and we're connecting, waiting for agent to be connected... \n " , m - > member_cid_name , m - > member_cid_number , m - > queue_name ) ;
2017-08-10 05:36:58 +00:00
for ( ; ; ) {
if ( ! switch_channel_test_app_flag_key ( CC_APP_KEY , member_channel , CC_APP_AGENT_CONNECTING ) ) {
break ;
}
switch_cond_next ( ) ;
}
}
if ( ! switch_channel_test_flag ( member_channel , CF_BRIDGED ) ) {
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( member_session ) , SWITCH_LOG_DEBUG , " Member %s <%s> in queue '%s' reached max wait time \n " , m - > member_cid_name , m - > member_cid_number , m - > queue_name ) ;
m - > member_cancel_reason = CC_MEMBER_CANCEL_REASON_TIMEOUT ;
switch_channel_set_flag_value ( member_channel , CF_BREAK , 2 ) ;
}
2010-09-08 04:07:19 +00:00
}
2014-02-20 21:20:17 +00:00
/* Check if max wait time no agent is Active AND if there is no Agent AND if the last agent check was after the member join */
if ( queue - > max_wait_time_with_no_agent > 0 & & queue - > last_agent_exist_check > queue - > last_agent_exist & & m - > t_member_called < = queue - > last_agent_exist_check ) {
/* Check if the time without agent is bigger or equal than out threshold */
2013-01-27 20:06:43 +00:00
if ( queue - > last_agent_exist_check - queue - > last_agent_exist > = queue - > max_wait_time_with_no_agent ) {
2014-02-20 21:20:17 +00:00
/* Check for grace period with no agent when member join */
2013-01-27 20:06:43 +00:00
if ( queue - > max_wait_time_with_no_agent_time_reached > 0 ) {
2014-02-20 21:20:17 +00:00
/* Check if the last agent check was after the member join, and we waited atless the extra time */
if ( queue - > last_agent_exist_check - m - > t_member_called > = queue - > max_wait_time_with_no_agent_time_reached + queue - > max_wait_time_with_no_agent ) {
2013-01-27 20:06:43 +00:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( member_session ) , SWITCH_LOG_DEBUG , " Member %s <%s> in queue '%s' reached max wait of %d sec. with no agent plus join grace period of %d sec. \n " , m - > member_cid_name , m - > member_cid_number , m - > queue_name , queue - > max_wait_time_with_no_agent , queue - > max_wait_time_with_no_agent_time_reached ) ;
m - > member_cancel_reason = CC_MEMBER_CANCEL_REASON_NO_AGENT_TIMEOUT ;
switch_channel_set_flag_value ( member_channel , CF_BREAK , 2 ) ;
2011-12-11 01:45:25 +00:00
2013-01-27 20:06:43 +00:00
}
} else {
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( member_session ) , SWITCH_LOG_DEBUG , " Member %s <%s> in queue '%s' reached max wait of %d sec. with no agent \n " , m - > member_cid_name , m - > member_cid_number , m - > queue_name , queue - > max_wait_time_with_no_agent ) ;
m - > member_cancel_reason = CC_MEMBER_CANCEL_REASON_NO_AGENT_TIMEOUT ;
switch_channel_set_flag_value ( member_channel , CF_BREAK , 2 ) ;
2016-11-21 22:32:22 +00:00
}
2013-01-27 20:06:43 +00:00
}
2010-09-08 04:07:19 +00:00
}
2010-08-13 13:48:37 +00:00
/* TODO Go thought the list of phrases */
/* SAMPLE CODE to playback something over the MOH
switch_event_t * event ;
if ( switch_event_create ( & event , SWITCH_EVENT_COMMAND ) = = SWITCH_STATUS_SUCCESS ) {
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " call-command " , " execute " ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " execute-app-name " , " playback " ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " execute-app-arg " , " tone_stream://%(200,0,500,600,700) " ) ;
switch_core_session_queue_private_event ( member_session , & event , SWITCH_TRUE ) ;
}
*/
/* If Agent Logoff, we might need to recalculare score based on skill */
2014-02-24 13:38:31 +00:00
/* Play the periodic announcement if it is time to do so */
if ( announce_valid = = SWITCH_TRUE & & queue - > announce & & queue - > announce_freq > 0 & &
queue - > announce_freq < = time_now - last_announce ) {
switch_status_t status = SWITCH_STATUS_FALSE ;
/* Stop previous announcement in case it's still running */
switch_ivr_stop_displace_session ( member_session , queue - > announce ) ;
/* Play the announcement */
status = switch_ivr_displace_session ( member_session , queue - > announce , 0 , NULL ) ;
if ( status ! = SWITCH_STATUS_SUCCESS ) {
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( member_session ) , SWITCH_LOG_WARNING ,
" Couldn't play announcement '%s' \n " , queue - > announce ) ;
announce_valid = SWITCH_FALSE ;
}
else {
last_announce = time_now ;
}
}
2010-09-08 04:07:19 +00:00
queue_rwunlock ( queue ) ;
switch_yield ( 500000 ) ;
2010-08-13 13:48:37 +00:00
}
2010-09-08 04:07:19 +00:00
2017-07-18 16:25:22 +00:00
switch_core_session_rwunlock ( member_session ) ;
2010-08-13 13:48:37 +00:00
switch_core_destroy_memory_pool ( & m - > pool ) ;
switch_mutex_lock ( globals . mutex ) ;
globals . threads - - ;
switch_mutex_unlock ( globals . mutex ) ;
2016-11-21 22:32:22 +00:00
return NULL ;
2010-08-13 13:48:37 +00:00
}
2011-02-15 03:43:45 +00:00
struct moh_dtmf_helper {
2011-03-31 09:17:04 +00:00
const char * queue_name ;
2011-07-18 02:20:57 +00:00
const char * exit_keys ;
2011-03-31 09:17:04 +00:00
char dtmf ;
2011-02-15 03:43:45 +00:00
} ;
static switch_status_t moh_on_dtmf ( switch_core_session_t * session , void * input , switch_input_type_t itype , void * buf , unsigned int buflen ) {
struct moh_dtmf_helper * h = ( struct moh_dtmf_helper * ) buf ;
switch ( itype ) {
case SWITCH_INPUT_TYPE_DTMF :
2011-07-18 02:20:57 +00:00
if ( h - > exit_keys & & * ( h - > exit_keys ) ) {
2011-02-15 03:43:45 +00:00
switch_dtmf_t * dtmf = ( switch_dtmf_t * ) input ;
2011-07-18 02:20:57 +00:00
if ( strchr ( h - > exit_keys , dtmf - > digit ) ) {
2011-02-15 03:43:45 +00:00
h - > dtmf = dtmf - > digit ;
return SWITCH_STATUS_BREAK ;
}
}
break ;
default :
break ;
}
return SWITCH_STATUS_SUCCESS ;
}
2010-08-13 13:48:37 +00:00
# define CC_DESC "callcenter"
# define CC_USAGE "queue_name"
SWITCH_STANDARD_APP ( callcenter_function )
{
char * argv [ 6 ] = { 0 } ;
char * mydata = NULL ;
cc_queue_t * queue = NULL ;
const char * queue_name = NULL ;
2010-09-08 04:07:19 +00:00
switch_core_session_t * member_session = session ;
switch_channel_t * member_channel = switch_core_session_get_channel ( member_session ) ;
2010-08-13 13:48:37 +00:00
char * sql = NULL ;
2011-03-31 09:17:04 +00:00
char * member_session_uuid = switch_core_session_get_uuid ( member_session ) ;
2010-09-08 04:07:19 +00:00
struct member_thread_helper * h = NULL ;
2010-08-13 13:48:37 +00:00
switch_thread_t * thread ;
switch_threadattr_t * thd_attr = NULL ;
switch_memory_pool_t * pool ;
2010-08-13 23:15:24 +00:00
switch_channel_timetable_t * times = NULL ;
2010-08-13 13:48:37 +00:00
const char * cc_moh_override = switch_channel_get_variable ( member_channel , " cc_moh_override " ) ;
2010-09-08 04:07:19 +00:00
const char * cc_base_score = switch_channel_get_variable ( member_channel , " cc_base_score " ) ;
int cc_base_score_int = 0 ;
2010-08-13 13:48:37 +00:00
const char * cur_moh = NULL ;
2015-05-03 23:25:42 +00:00
char * moh_expanded = NULL ;
2010-08-13 13:48:37 +00:00
char start_epoch [ 64 ] ;
switch_event_t * event ;
2011-04-23 21:01:35 +00:00
switch_time_t t_member_called = local_epoch_time_now ( NULL ) ;
2010-08-26 10:19:58 +00:00
long abandoned_epoch = 0 ;
2011-03-31 09:17:04 +00:00
switch_uuid_t smember_uuid ;
char member_uuid [ SWITCH_UUID_FORMATTED_LENGTH + 1 ] = " " ;
2011-04-22 07:01:08 +00:00
switch_bool_t agent_found = SWITCH_FALSE ;
2011-04-24 02:37:47 +00:00
switch_bool_t moh_valid = SWITCH_TRUE ;
2011-05-03 16:13:52 +00:00
const char * p ;
2010-08-13 13:48:37 +00:00
if ( ! zstr ( data ) ) {
2010-09-08 04:07:19 +00:00
mydata = switch_core_session_strdup ( member_session , data ) ;
2011-04-23 01:55:56 +00:00
switch_separate_string ( mydata , ' ' , argv , ( sizeof ( argv ) / sizeof ( argv [ 0 ] ) ) ) ;
2010-08-13 13:48:37 +00:00
} else {
2011-04-22 06:22:30 +00:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( member_session ) , SWITCH_LOG_WARNING , " No Queue name provided \n " ) ;
2010-08-13 13:48:37 +00:00
goto end ;
}
if ( argv [ 0 ] ) {
2017-07-18 16:25:22 +00:00
queue_name = argv [ 0 ] ;
2010-08-13 13:48:37 +00:00
}
2017-07-18 16:25:22 +00:00
if ( ! queue_name | | ! ( queue = get_queue ( queue_name ) ) ) {
2011-04-22 06:22:30 +00:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( member_session ) , SWITCH_LOG_WARNING , " Queue %s not found \n " , queue_name ) ;
2010-08-13 13:48:37 +00:00
goto end ;
}
2010-08-13 23:15:24 +00:00
/* Make sure we answer the channel before getting the switch_channel_time_table_t answer time */
switch_channel_answer ( member_channel ) ;
2010-08-13 13:48:37 +00:00
/* Grab the start epoch of a channel */
2010-08-13 23:15:24 +00:00
times = switch_channel_get_timetable ( member_channel ) ;
2010-08-27 02:49:02 +00:00
switch_snprintf ( start_epoch , sizeof ( start_epoch ) , " % " SWITCH_TIME_T_FMT , times - > answered / 1000000 ) ;
2010-08-13 13:48:37 +00:00
2010-09-08 04:07:19 +00:00
/* Check if we support and have a queued abandoned member we can resume from */
2010-08-26 10:19:58 +00:00
if ( queue - > abandoned_resume_allowed = = SWITCH_TRUE ) {
char res [ 256 ] ;
/* Check to see if agent already exist */
2011-03-31 09:22:50 +00:00
sql = switch_mprintf ( " SELECT uuid FROM members WHERE queue = '%q' AND cid_number = '%q' AND state = '%q' ORDER BY abandoned_epoch DESC " ,
2010-08-27 02:49:02 +00:00
queue_name , switch_str_nil ( switch_channel_get_variable ( member_channel , " caller_id_number " ) ) , cc_member_state2str ( CC_MEMBER_STATE_ABANDONED ) ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql2str ( NULL , NULL , sql , res , sizeof ( res ) ) ;
2010-08-26 10:19:58 +00:00
switch_safe_free ( sql ) ;
2011-03-31 09:17:04 +00:00
strncpy ( member_uuid , res , sizeof ( member_uuid ) ) ;
2016-11-21 22:32:22 +00:00
2011-03-31 09:17:04 +00:00
if ( ! zstr ( member_uuid ) ) {
sql = switch_mprintf ( " SELECT abandoned_epoch FROM members WHERE uuid = '%q' " , member_uuid ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql2str ( NULL , NULL , sql , res , sizeof ( res ) ) ;
2011-03-31 09:17:04 +00:00
switch_safe_free ( sql ) ;
abandoned_epoch = atol ( res ) ;
}
2010-08-26 10:19:58 +00:00
}
2011-03-31 09:17:04 +00:00
/* If no existing uuid is restored, let create a new one */
if ( abandoned_epoch = = 0 ) {
switch_uuid_get ( & smember_uuid ) ;
switch_uuid_format ( member_uuid , & smember_uuid ) ;
}
switch_channel_set_variable ( member_channel , " cc_side " , " member " ) ;
switch_channel_set_variable ( member_channel , " cc_member_uuid " , member_uuid ) ;
2010-08-13 13:48:37 +00:00
/* Add manually imported score */
if ( cc_base_score ) {
cc_base_score_int + = atoi ( cc_base_score ) ;
}
/* If system, will add the total time the session is up to the base score */
if ( ! switch_strlen_zero ( start_epoch ) & & ! strcasecmp ( " system " , queue - > time_base_score ) ) {
2011-04-23 21:01:35 +00:00
cc_base_score_int + = ( ( long ) local_epoch_time_now ( NULL ) - atol ( start_epoch ) ) ;
2010-08-13 13:48:37 +00:00
}
2016-02-22 12:54:53 +00:00
/* for xml_cdr needs */
switch_channel_set_variable_printf ( member_channel , " cc_queue_joined_epoch " , " % " SWITCH_TIME_T_FMT , local_epoch_time_now ( NULL ) ) ;
switch_channel_set_variable ( member_channel , " cc_queue " , queue_name ) ;
/* We have a previous abandoned user, let's try to recover his place */
if ( abandoned_epoch > 0 ) {
char res [ 256 ] ;
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( member_session ) , SWITCH_LOG_DEBUG , " Member %s <%s> restoring it previous position in queue %s \n " , switch_str_nil ( switch_channel_get_variable ( member_channel , " caller_id_name " ) ) , switch_str_nil ( switch_channel_get_variable ( member_channel , " caller_id_number " ) ) , queue_name ) ;
/* Update abandoned member */
2019-08-14 14:28:37 +00:00
sql = switch_mprintf ( " UPDATE members SET session_uuid = '%q', state = '%q', rejoined_epoch = '% " SWITCH_TIME_T_FMT " ', instance_id = '%q' WHERE uuid = '%q' AND state = '%q' " ,
2017-07-12 16:01:06 +00:00
member_session_uuid , cc_member_state2str ( CC_MEMBER_STATE_WAITING ) , local_epoch_time_now ( NULL ) , globals . cc_instance_id , member_uuid , cc_member_state2str ( CC_MEMBER_STATE_ABANDONED ) ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql ( queue , sql , NULL ) ;
2016-02-22 12:54:53 +00:00
switch_safe_free ( sql ) ;
/* Confirm we took that member in */
sql = switch_mprintf ( " SELECT abandoned_epoch FROM members WHERE uuid = '%q' AND session_uuid = '%q' AND state = '%q' AND queue = '%q' " , member_uuid , member_session_uuid , cc_member_state2str ( CC_MEMBER_STATE_WAITING ) , queue_name ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql2str ( NULL , NULL , sql , res , sizeof ( res ) ) ;
2016-02-22 12:54:53 +00:00
switch_safe_free ( sql ) ;
abandoned_epoch = atol ( res ) ;
if ( abandoned_epoch = = 0 ) {
/* Failed to get the member !!! */
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( member_session ) , SWITCH_LOG_ERROR , " Member %s <%s> restoring action failed in queue %s, joining again \n " , switch_str_nil ( switch_channel_get_variable ( member_channel , " caller_id_name " ) ) , switch_str_nil ( switch_channel_get_variable ( member_channel , " caller_id_number " ) ) , queue_name ) ;
2017-07-18 16:25:22 +00:00
//queue_rwunlock(queue);
2016-02-22 12:54:53 +00:00
} else {
}
}
2010-08-13 13:48:37 +00:00
if ( switch_event_create_subclass ( & event , SWITCH_EVENT_CUSTOM , CALLCENTER_EVENT ) = = SWITCH_STATUS_SUCCESS ) {
switch_channel_event_set_data ( member_channel , event ) ;
2010-09-08 04:07:19 +00:00
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Queue " , queue_name ) ;
2010-08-26 10:19:58 +00:00
switch_event_add_header ( event , SWITCH_STACK_BOTTOM , " CC-Action " , " member-queue-%s " , ( abandoned_epoch = = 0 ? " start " : " resume " ) ) ;
2011-03-31 09:17:04 +00:00
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Member-UUID " , member_uuid ) ;
2011-03-31 09:22:50 +00:00
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Member-Session-UUID " , member_session_uuid ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Member-CID-Name " , switch_str_nil ( switch_channel_get_variable ( member_channel , " caller_id_name " ) ) ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Member-CID-Number " , switch_str_nil ( switch_channel_get_variable ( member_channel , " caller_id_number " ) ) ) ;
2010-08-13 13:48:37 +00:00
switch_event_fire ( & event ) ;
}
2016-02-22 12:54:53 +00:00
2017-07-18 16:25:22 +00:00
2010-08-26 10:19:58 +00:00
if ( abandoned_epoch = = 0 ) {
2016-04-20 08:15:34 +00:00
char * strategy_str = NULL ;
2010-08-26 10:19:58 +00:00
/* Add the caller to the member queue */
2011-04-22 06:22:30 +00:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( member_session ) , SWITCH_LOG_DEBUG , " Member %s <%s> joining queue %s \n " , switch_str_nil ( switch_channel_get_variable ( member_channel , " caller_id_name " ) ) , switch_str_nil ( switch_channel_get_variable ( member_channel , " caller_id_number " ) ) , queue_name ) ;
2016-04-20 08:15:34 +00:00
if ( ! strcasecmp ( queue - > strategy , " ring-all " ) ) {
strategy_str = " ring-all " ;
} else if ( ! strcasecmp ( queue - > strategy , " ring-progressively " ) ) {
strategy_str = " ring-progressively " ;
} else {
strategy_str = " " ;
}
2010-08-26 10:19:58 +00:00
sql = switch_mprintf ( " INSERT INTO members "
2019-08-14 14:28:37 +00:00
" (queue,instance_id,uuid,session_uuid,system_epoch,joined_epoch,base_score,skill_score,cid_number,cid_name,serving_agent,serving_system,state) "
2017-01-05 15:18:28 +00:00
" VALUES('%q','%q','%q','%q','%q','% " SWITCH_TIME_T_FMT " ','%d','%d','%q','%q','%q','','%q') " ,
2010-08-26 10:19:58 +00:00
queue_name ,
2017-07-12 16:01:06 +00:00
globals . cc_instance_id ,
2010-09-08 04:07:19 +00:00
member_uuid ,
2011-03-31 09:17:04 +00:00
member_session_uuid ,
2010-08-26 10:19:58 +00:00
start_epoch ,
2011-04-23 21:01:35 +00:00
local_epoch_time_now ( NULL ) ,
2010-08-26 10:19:58 +00:00
cc_base_score_int ,
0 /*TODO SKILL score*/ ,
switch_str_nil ( switch_channel_get_variable ( member_channel , " caller_id_number " ) ) ,
switch_str_nil ( switch_channel_get_variable ( member_channel , " caller_id_name " ) ) ,
2016-04-20 08:15:34 +00:00
strategy_str ,
2010-08-26 10:19:58 +00:00
cc_member_state2str ( CC_MEMBER_STATE_WAITING ) ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql ( queue , sql , NULL ) ;
2010-08-26 10:19:58 +00:00
switch_safe_free ( sql ) ;
}
2010-08-13 13:48:37 +00:00
/* Send Event with queue count */
2017-07-18 16:25:22 +00:00
cc_queue_count ( queue_name ) ;
cc_send_presence ( queue_name ) ;
2010-08-13 13:48:37 +00:00
/* Start Thread that will playback different prompt to the channel */
switch_core_new_memory_pool ( & pool ) ;
h = switch_core_alloc ( pool , sizeof ( * h ) ) ;
2010-09-08 04:07:19 +00:00
2010-08-13 13:48:37 +00:00
h - > pool = pool ;
2010-09-08 04:07:19 +00:00
h - > member_uuid = switch_core_strdup ( h - > pool , member_uuid ) ;
2011-03-31 09:17:04 +00:00
h - > member_session_uuid = switch_core_strdup ( h - > pool , member_session_uuid ) ;
2011-04-15 00:23:12 +00:00
h - > member_cid_name = switch_core_strdup ( h - > pool , switch_str_nil ( switch_channel_get_variable ( member_channel , " caller_id_name " ) ) ) ;
h - > member_cid_number = switch_core_strdup ( h - > pool , switch_str_nil ( switch_channel_get_variable ( member_channel , " caller_id_number " ) ) ) ;
2010-09-08 04:07:19 +00:00
h - > queue_name = switch_core_strdup ( h - > pool , queue_name ) ;
h - > t_member_called = t_member_called ;
h - > member_cancel_reason = CC_MEMBER_CANCEL_REASON_NONE ;
2010-08-13 13:48:37 +00:00
h - > running = 1 ;
2010-09-08 04:07:19 +00:00
2010-08-13 13:48:37 +00:00
switch_threadattr_create ( & thd_attr , h - > pool ) ;
switch_threadattr_detach_set ( thd_attr , 1 ) ;
switch_threadattr_stacksize_set ( thd_attr , SWITCH_THREAD_STACKSIZE ) ;
switch_thread_create ( & thread , thd_attr , cc_member_thread_run , h , h - > pool ) ;
/* Playback MOH */
if ( cc_moh_override ) {
2010-09-08 04:07:19 +00:00
cur_moh = switch_core_session_strdup ( member_session , cc_moh_override ) ;
2010-08-13 23:15:24 +00:00
} else {
2010-09-08 04:07:19 +00:00
cur_moh = switch_core_session_strdup ( member_session , queue - > moh ) ;
2010-08-13 13:48:37 +00:00
}
2010-08-13 23:15:24 +00:00
queue_rwunlock ( queue ) ;
2015-05-03 23:25:42 +00:00
moh_expanded = switch_channel_expand_variables ( member_channel , cur_moh ) ;
2010-08-13 13:48:37 +00:00
2010-09-08 04:07:19 +00:00
while ( switch_channel_ready ( member_channel ) ) {
switch_input_args_t args = { 0 } ;
2011-02-15 03:43:45 +00:00
struct moh_dtmf_helper ht ;
2011-07-18 02:20:57 +00:00
ht . exit_keys = switch_channel_get_variable ( member_channel , " cc_exit_keys " ) ;
2011-02-15 03:43:45 +00:00
ht . dtmf = ' \0 ' ;
args . input_callback = moh_on_dtmf ;
args . buf = ( void * ) & ht ;
args . buflen = sizeof ( h ) ;
2010-09-08 04:07:19 +00:00
/* An agent was found, time to exit and let the bridge do it job */
2011-04-22 07:01:08 +00:00
if ( ( p = switch_channel_get_variable ( member_channel , " cc_agent_found " ) ) & & ( agent_found = switch_true ( p ) ) ) {
2010-09-08 04:07:19 +00:00
break ;
}
/* If the member thread set a different reason, we monitor it so we can quit the wait */
if ( h - > member_cancel_reason ! = CC_MEMBER_CANCEL_REASON_NONE ) {
break ;
}
switch_core_session_flush_private_events ( member_session ) ;
2015-05-03 23:25:42 +00:00
if ( moh_valid & & moh_expanded ) {
switch_status_t status = switch_ivr_play_file ( member_session , NULL , moh_expanded , & args ) ;
2018-01-25 13:49:53 +00:00
switch_bool_t exiting_with_key = ht . exit_keys & & * ( ht . exit_keys ) & & ! zstr ( & ht . dtmf ) & & strchr ( ht . exit_keys , ht . dtmf ) ;
2016-11-21 22:32:22 +00:00
if ( status = = SWITCH_STATUS_FALSE /* Invalid Recording */ & & SWITCH_READ_ACCEPTABLE ( status ) ) {
2011-05-03 16:13:52 +00:00
/* Sadly, there doesn't seem to be a return to switch_ivr_play_file that tell you the file wasn't found. FALSE also mean that the channel got switch to BRAKE state, so we check for read acceptable */
2011-04-24 02:37:47 +00:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( member_session ) , SWITCH_LOG_WARNING , " Couldn't play file '%s', continuing wait with no audio \n " , cur_moh ) ;
moh_valid = SWITCH_FALSE ;
2018-01-25 13:49:53 +00:00
} else if ( ( status = = SWITCH_STATUS_BREAK ) & & exiting_with_key ) {
2011-07-18 09:39:11 +00:00
char buf [ 2 ] = { ht . dtmf , 0 } ;
switch_channel_set_variable ( member_channel , " cc_exit_key " , buf ) ;
2016-10-05 02:07:42 +00:00
h - > member_cancel_reason = CC_MEMBER_CANCEL_REASON_EXIT_WITH_KEY ;
2011-07-18 02:20:57 +00:00
break ;
2011-04-24 02:37:47 +00:00
} else if ( ! SWITCH_READ_ACCEPTABLE ( status ) ) {
2010-09-08 04:07:19 +00:00
break ;
}
} else {
2018-01-25 13:49:53 +00:00
switch_status_t status = switch_ivr_collect_digits_callback ( member_session , & args , 0 , 0 ) ;
switch_bool_t exiting_with_key = ht . exit_keys & & * ( ht . exit_keys ) & & ! zstr ( & ht . dtmf ) & & strchr ( ht . exit_keys , ht . dtmf ) ;
if ( ( status = = SWITCH_STATUS_BREAK ) & & exiting_with_key ) {
2011-07-18 09:39:11 +00:00
char buf [ 2 ] = { ht . dtmf , 0 } ;
switch_channel_set_variable ( member_channel , " cc_exit_key " , buf ) ;
2016-10-05 02:07:42 +00:00
h - > member_cancel_reason = CC_MEMBER_CANCEL_REASON_EXIT_WITH_KEY ;
2011-07-18 02:20:57 +00:00
break ;
}
2010-09-08 04:07:19 +00:00
}
2011-02-15 03:43:45 +00:00
switch_yield ( 1000 ) ;
2010-08-13 13:48:37 +00:00
}
2015-05-03 23:25:42 +00:00
if ( moh_expanded ! = cur_moh ) {
switch_safe_free ( moh_expanded ) ;
}
2010-08-13 13:48:37 +00:00
2011-06-26 13:30:05 +00:00
/* Make sure an agent was found, as we might break above without setting it */
2011-05-03 16:13:52 +00:00
if ( ! agent_found & & ( p = switch_channel_get_variable ( member_channel , " cc_agent_found " ) ) ) {
agent_found = switch_true ( p ) ;
}
2011-06-26 13:30:05 +00:00
/* Stop member thread */
2010-08-13 13:48:37 +00:00
if ( h ) {
h - > running = 0 ;
}
2011-06-26 13:30:05 +00:00
/* Check if we were removed because FS Core(BREAK) asked us to */
2011-04-22 07:01:08 +00:00
if ( h - > member_cancel_reason = = CC_MEMBER_CANCEL_REASON_NONE & & ! agent_found ) {
2011-03-14 16:31:56 +00:00
h - > member_cancel_reason = CC_MEMBER_CANCEL_REASON_BREAK_OUT ;
}
2011-04-22 07:01:08 +00:00
switch_channel_set_variable ( member_channel , " cc_agent_found " , NULL ) ;
2011-03-14 16:31:56 +00:00
/* Canceled for some reason */
if ( ! switch_channel_up ( member_channel ) | | h - > member_cancel_reason ! = CC_MEMBER_CANCEL_REASON_NONE ) {
2011-04-22 06:22:30 +00:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( member_session ) , SWITCH_LOG_DEBUG , " Member %s <%s> abandoned waiting in queue %s \n " , switch_str_nil ( switch_channel_get_variable ( member_channel , " caller_id_name " ) ) , switch_str_nil ( switch_channel_get_variable ( member_channel , " caller_id_number " ) ) , queue_name ) ;
2011-03-14 16:31:56 +00:00
/* Update member state */
2019-08-14 14:28:37 +00:00
sql = switch_mprintf ( " UPDATE members SET state = '%q', session_uuid = '', abandoned_epoch = '% " SWITCH_TIME_T_FMT " ' WHERE uuid = '%q' AND instance_id = '%q' " ,
2017-07-12 16:01:06 +00:00
cc_member_state2str ( CC_MEMBER_STATE_ABANDONED ) , local_epoch_time_now ( NULL ) , member_uuid , globals . cc_instance_id ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
2011-03-14 16:31:56 +00:00
/* Hangup any callback agents */
2011-04-15 00:23:12 +00:00
switch_core_session_hupall_matching_var ( " cc_member_pre_answer_uuid " , member_uuid , SWITCH_CAUSE_ORIGINATOR_CANCEL ) ;
2011-03-14 16:31:56 +00:00
/* Generate an event */
2010-08-13 13:48:37 +00:00
if ( switch_event_create_subclass ( & event , SWITCH_EVENT_CUSTOM , CALLCENTER_EVENT ) = = SWITCH_STATUS_SUCCESS ) {
switch_channel_event_set_data ( member_channel , event ) ;
2010-09-08 04:07:19 +00:00
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Queue " , queue_name ) ;
2010-08-13 13:48:37 +00:00
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Action " , " member-queue-end " ) ;
2011-04-23 21:01:35 +00:00
switch_event_add_header ( event , SWITCH_STACK_BOTTOM , " CC-Member-Leaving-Time " , " % " SWITCH_TIME_T_FMT , local_epoch_time_now ( NULL ) ) ;
2011-04-23 15:55:56 +00:00
switch_event_add_header ( event , SWITCH_STACK_BOTTOM , " CC-Member-Joined-Time " , " % " SWITCH_TIME_T_FMT , t_member_called ) ;
2011-03-14 16:31:56 +00:00
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Cause " , " Cancel " ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Cancel-Reason " , cc_member_cancel_reason2str ( h - > member_cancel_reason ) ) ;
2011-03-31 09:17:04 +00:00
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Member-UUID " , member_uuid ) ;
2011-03-31 09:22:50 +00:00
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Member-Session-UUID " , member_session_uuid ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Member-CID-Name " , switch_str_nil ( switch_channel_get_variable ( member_channel , " caller_id_name " ) ) ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " CC-Member-CID-Number " , switch_str_nil ( switch_channel_get_variable ( member_channel , " caller_id_number " ) ) ) ;
2010-08-13 13:48:37 +00:00
switch_event_fire ( & event ) ;
}
2010-09-08 04:07:19 +00:00
2011-03-14 16:31:56 +00:00
/* Update some channel variables for xml_cdr needs */
2011-04-23 21:01:35 +00:00
switch_channel_set_variable_printf ( member_channel , " cc_queue_canceled_epoch " , " % " SWITCH_TIME_T_FMT , local_epoch_time_now ( NULL ) ) ;
2011-03-14 16:31:56 +00:00
switch_channel_set_variable_printf ( member_channel , " cc_cause " , " %s " , " cancel " ) ;
switch_channel_set_variable_printf ( member_channel , " cc_cancel_reason " , " %s " , cc_member_cancel_reason2str ( h - > member_cancel_reason ) ) ;
2010-08-13 13:48:37 +00:00
2011-03-14 16:31:56 +00:00
/* Print some debug log information */
2011-04-22 06:22:30 +00:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( member_session ) , SWITCH_LOG_DEBUG , " Member \" %s \" <%s> exit queue %s due to %s \n " ,
2011-03-14 16:31:56 +00:00
switch_str_nil ( switch_channel_get_variable ( member_channel , " caller_id_name " ) ) ,
switch_str_nil ( switch_channel_get_variable ( member_channel , " caller_id_number " ) ) ,
queue_name , cc_member_cancel_reason2str ( h - > member_cancel_reason ) ) ;
2017-07-18 16:25:22 +00:00
if ( ( queue = get_queue ( queue_name ) ) ) {
2016-12-29 15:59:24 +00:00
queue - > calls_abandoned + + ;
queue_rwunlock ( queue ) ;
}
2010-08-13 13:48:37 +00:00
} else {
2011-04-22 06:22:30 +00:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( member_session ) , SWITCH_LOG_DEBUG , " Member %s <%s> is answered by an agent in queue %s \n " , switch_str_nil ( switch_channel_get_variable ( member_channel , " caller_id_name " ) ) , switch_str_nil ( switch_channel_get_variable ( member_channel , " caller_id_number " ) ) , queue_name ) ;
2011-03-14 16:31:56 +00:00
/* Update member state */
2019-08-14 14:28:37 +00:00
sql = switch_mprintf ( " UPDATE members SET state = '%q', bridge_epoch = '% " SWITCH_TIME_T_FMT " ' WHERE uuid = '%q' AND instance_id = '%q' " ,
2017-07-12 16:01:06 +00:00
cc_member_state2str ( CC_MEMBER_STATE_ANSWERED ) , local_epoch_time_now ( NULL ) , member_uuid , globals . cc_instance_id ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
2011-03-14 16:31:56 +00:00
/* Update some channel variables for xml_cdr needs */
switch_channel_set_variable_printf ( member_channel , " cc_cause " , " %s " , " answered " ) ;
2017-07-18 16:25:22 +00:00
if ( ( queue = get_queue ( queue_name ) ) ) {
2016-12-29 15:59:24 +00:00
queue - > calls_answered + + ;
queue_rwunlock ( queue ) ;
}
2010-08-13 13:48:37 +00:00
}
2011-03-14 16:31:56 +00:00
/* Send Event with queue count */
2017-07-18 16:25:22 +00:00
cc_queue_count ( queue_name ) ;
cc_send_presence ( queue_name ) ;
2011-03-14 16:31:56 +00:00
2010-08-13 13:48:37 +00:00
end :
return ;
}
2016-10-05 02:07:42 +00:00
switch_application_interface_t * app_interface = NULL ;
static switch_status_t cc_hook_state_run ( switch_core_session_t * session )
{
switch_channel_t * channel = switch_core_session_get_channel ( session ) ;
switch_channel_state_t state = switch_channel_get_state ( channel ) ;
const char * agent_name = NULL ;
char * sql = NULL ;
agent_name = switch_channel_get_variable ( channel , " cc_tracked_agent " ) ;
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_DEBUG , " Called cc_hook_hanguphook channel %s with state %s " , switch_channel_get_name ( channel ) , switch_channel_state_name ( state ) ) ;
if ( state = = CS_HANGUP ) {
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_DEBUG , " Tracked call for agent %s ended, decreasing external_calls_count " , agent_name ) ;
sql = switch_mprintf ( " UPDATE agents SET external_calls_count = external_calls_count - 1 WHERE name = '%q' " , agent_name ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2016-10-05 02:07:42 +00:00
switch_safe_free ( sql ) ;
switch_core_event_hook_remove_state_run ( session , cc_hook_state_run ) ;
UNPROTECT_INTERFACE ( app_interface ) ;
}
return SWITCH_STATUS_SUCCESS ;
}
SWITCH_STANDARD_APP ( callcenter_track )
{
switch_channel_t * channel = switch_core_session_get_channel ( session ) ;
char agent_status [ 255 ] ;
char * agent_name = NULL ;
char * sql = NULL ;
if ( zstr ( data ) ) {
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_ERROR , " Missing agent name \n " ) ;
return ;
}
2017-07-18 16:25:22 +00:00
if ( cc_agent_get ( " status " , data , agent_status , sizeof ( agent_status ) ) ! = CC_STATUS_SUCCESS ) {
2017-07-18 14:34:07 +00:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_WARNING , " Invalid agent %s " , data ) ;
2017-07-18 16:25:22 +00:00
return ;
2017-07-18 14:34:07 +00:00
}
2016-10-05 02:07:42 +00:00
2017-07-18 16:25:22 +00:00
agent_name = switch_safe_strdup ( data ) ;
2016-10-05 02:07:42 +00:00
switch_channel_set_variable ( channel , " cc_tracked_agent " , agent_name ) ;
2017-07-18 16:09:39 +00:00
2016-10-05 02:07:42 +00:00
sql = switch_mprintf ( " UPDATE agents SET external_calls_count = external_calls_count + 1 WHERE name = '%q' " , agent_name ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql ( NULL , sql , NULL ) ;
2016-10-05 02:07:42 +00:00
switch_safe_free ( sql ) ;
switch_core_event_hook_add_state_run ( session , cc_hook_state_run ) ;
PROTECT_INTERFACE ( app_interface ) ;
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_DEBUG , " Tracking this call for agent %s " , data ) ;
2017-07-18 16:25:22 +00:00
switch_safe_free ( agent_name ) ;
2016-10-05 02:07:42 +00:00
return ;
}
2017-07-18 16:25:22 +00:00
static void cc_send_presence ( const char * queue_name ) {
2016-10-05 02:07:42 +00:00
char * sql ;
char res [ 256 ] = " " ;
int count = 0 ;
switch_event_t * send_event ;
2016-12-09 19:34:05 +00:00
sql = switch_mprintf ( " SELECT COUNT(*) FROM members WHERE queue = '%q' AND state = '%q' " , queue_name , cc_member_state2str ( CC_MEMBER_STATE_WAITING ) ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql2str ( NULL , NULL , sql , res , sizeof ( res ) ) ;
2016-10-05 02:07:42 +00:00
count = atoi ( res ) ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Queue has %d waiting calls. \n " , count ) ;
if ( switch_event_create ( & send_event , SWITCH_EVENT_PRESENCE_IN ) = = SWITCH_STATUS_SUCCESS ) {
switch_event_add_header_string ( send_event , SWITCH_STACK_BOTTOM , " proto " , " callcenter " ) ;
2016-12-09 19:34:05 +00:00
switch_event_add_header ( send_event , SWITCH_STACK_BOTTOM , " login " , " %s " , queue_name ) ;
switch_event_add_header_string ( send_event , SWITCH_STACK_BOTTOM , " from " , queue_name ) ;
2016-10-05 02:07:42 +00:00
if ( count > 0 ) {
switch_event_add_header ( send_event , SWITCH_STACK_BOTTOM , " force-status " , " Active (%d waiting) " , count ) ;
} else {
switch_event_add_header_string ( send_event , SWITCH_STACK_BOTTOM , " force-status " , " Idle " ) ;
}
switch_event_add_header_string ( send_event , SWITCH_STACK_BOTTOM , " rpid " , " unknown " ) ;
switch_event_add_header_string ( send_event , SWITCH_STACK_BOTTOM , " event_type " , " presence " ) ;
switch_event_add_header_string ( send_event , SWITCH_STACK_BOTTOM , " alt_event_type " , " dialog " ) ;
switch_event_add_header ( send_event , SWITCH_STACK_BOTTOM , " event_count " , " %d " , 0 ) ;
switch_event_add_header_string ( send_event , SWITCH_STACK_BOTTOM , " channel-state " , count > 0 ? " CS_ROUTING " : " CS_HANGUP " ) ;
2016-12-09 19:34:05 +00:00
switch_event_add_header_string ( send_event , SWITCH_STACK_BOTTOM , " unique-id " , queue_name ) ;
2016-10-05 02:07:42 +00:00
switch_event_add_header_string ( send_event , SWITCH_STACK_BOTTOM , " answer-state " , count > 0 ? " confirmed " : " terminated " ) ;
switch_event_add_header_string ( send_event , SWITCH_STACK_BOTTOM , " presence-call-direction " , " inbound " ) ;
switch_event_fire ( & send_event ) ;
} else {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Failed to create presence in event \n " ) ;
}
switch_safe_free ( sql ) ;
}
static void cc_presence_event_handler ( switch_event_t * event ) {
char * to = switch_event_get_header ( event , " to " ) ;
2017-07-18 16:25:22 +00:00
char * dup_to = NULL , * queue_name ;
2016-10-05 02:07:42 +00:00
cc_queue_t * queue ;
if ( ! globals . running ) {
2017-07-18 16:25:22 +00:00
return ;
2016-10-05 02:07:42 +00:00
}
// DUMP_EVENT(event);
if ( ! to | | strncasecmp ( to , " callcenter+ " , 11 ) | | ! strchr ( to , ' @ ' ) ) {
2017-07-18 16:25:22 +00:00
return ;
2016-10-05 02:07:42 +00:00
}
2017-07-18 16:25:22 +00:00
dup_to = strdup ( to ) ;
queue_name = dup_to + 11 ;
2016-10-05 02:07:42 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Searching queue %s \n " , queue_name ) ;
2017-07-18 16:25:22 +00:00
queue = get_queue ( queue_name ) ;
2016-10-05 02:07:42 +00:00
if ( ! queue ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Queue not found, exit! \n " ) ;
2017-07-18 16:25:22 +00:00
return ;
2016-10-05 02:07:42 +00:00
}
2017-07-18 16:25:22 +00:00
cc_send_presence ( queue_name ) ;
2016-10-05 02:07:42 +00:00
queue_rwunlock ( queue ) ;
2017-07-18 16:25:22 +00:00
switch_safe_free ( dup_to ) ;
2016-10-05 02:07:42 +00:00
return ;
}
2010-08-13 13:48:37 +00:00
struct list_result {
const char * name ;
const char * format ;
int row_process ;
switch_stream_handle_t * stream ;
} ;
static int list_result_callback ( void * pArg , int argc , char * * argv , char * * columnNames )
{
struct list_result * cbt = ( struct list_result * ) pArg ;
int i = 0 ;
cbt - > row_process + + ;
if ( cbt - > row_process = = 1 ) {
for ( i = 0 ; i < argc ; i + + ) {
cbt - > stream - > write_function ( cbt - > stream , " %s " , columnNames [ i ] ) ;
if ( i < argc - 1 ) {
cbt - > stream - > write_function ( cbt - > stream , " | " ) ;
}
2016-11-21 22:32:22 +00:00
}
2010-08-13 13:48:37 +00:00
cbt - > stream - > write_function ( cbt - > stream , " \n " ) ;
}
for ( i = 0 ; i < argc ; i + + ) {
cbt - > stream - > write_function ( cbt - > stream , " %s " , argv [ i ] ) ;
if ( i < argc - 1 ) {
cbt - > stream - > write_function ( cbt - > stream , " | " ) ;
}
}
cbt - > stream - > write_function ( cbt - > stream , " \n " ) ;
return 0 ;
}
FS-8799 [mod_callcenter] Add JSON API interface for mod_callcenter
Adding a JSON API interface to fetch data in the JSON format. It abides with the JSON command and its implementations by other modules like mod_verto.
It outputs the exactly same column names as the data in CLI.
Various listing of mod_callcenter data such as agents, queues, members, tiers are as follows below.
- Listing of agents
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"agent list"}}`
- Listing of queues
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"queue list"}}`
- Listing of agents for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents", "queue_name":"support@default"}}`
(`queue_name` is a required)
- Listing of members(callers) for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list members", "queue_name":"support@default"}}`
- Listing of tiers for a queue
Command: `json {"command": "callcenter_config","data": {"arguments":"queue list tiers", "queue_name":"support@default"}}`
- Listing of members(callers)
Command: `json {"command": "callcenter_config","data": {"arguments":"member list"}}`
- Listing of tiers
Command: `json {"command": "callcenter_config","data": {"arguments":"tier list"}}`
It handles various errors and also returns back response in JSON as follow
E.g. Send a command without a required field (`queue_name`)
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents"}}`
Output: `{"command":"callcenter_config","data":{"arguments":"queue list agents"},"status":"error","message":"The command returned an error","response":{"error":"Missing data attribute: queue_name"}}`
2016-01-28 08:38:00 +00:00
struct list_result_json {
const char * name ;
const char * format ;
int row_process ;
switch_stream_handle_t * stream ;
cJSON * json_reply ;
} ;
static int list_result_json_callback ( void * pArg , int argc , char * * argv , char * * columnNames )
{
struct list_result_json * cbt = ( struct list_result_json * ) pArg ;
cJSON * o = cJSON_CreateObject ( ) ;
int i = 0 ;
cbt - > row_process + + ;
for ( i = 0 ; i < argc ; i + + ) {
cJSON_AddItemToObject ( o , columnNames [ i ] , cJSON_CreateString ( argv [ i ] ) ) ;
}
cJSON_AddItemToArray ( cbt - > json_reply , o ) ;
return 0 ;
}
2011-07-16 01:53:57 +00:00
# define CC_CONFIG_API_SYNTAX "callcenter_config <target> <args>,\n"\
2017-07-18 16:25:22 +00:00
" \t callcenter_config agent add [name] [type] | \n " \
" \t callcenter_config agent del [name] | \n " \
" \t callcenter_config agent reload [name] | \n " \
" \t callcenter_config agent set status [agent_name] [status] | \n " \
" \t callcenter_config agent set state [agent_name] [state] | \n " \
" \t callcenter_config agent set contact [agent_name] [contact] | \n " \
" \t callcenter_config agent set ready_time [agent_name] [wait till epoch] | \n " \
" \t callcenter_config agent set reject_delay_time [agent_name] [wait second] | \n " \
" \t callcenter_config agent set busy_delay_time [agent_name] [wait second] | \n " \
" \t callcenter_config agent set no_answer_delay_time [agent_name] [wait second] | \n " \
" \t callcenter_config agent get status [agent_name] | \n " \
" \t callcenter_config agent get state [agent_name] | \n " \
" \t callcenter_config agent get uuid [agent_name] | \n " \
" \t callcenter_config agent list [[agent_name]] | \n " \
" \t callcenter_config tier add [queue_name] [agent_name] [[level]] [[position]] | \n " \
" \t callcenter_config tier set state [queue_name] [agent_name] [state] | \n " \
" \t callcenter_config tier set level [queue_name] [agent_name] [level] | \n " \
" \t callcenter_config tier set position [queue_name] [agent_name] [position] | \n " \
" \t callcenter_config tier del [queue_name] [agent_name] | \n " \
" \t callcenter_config tier reload [queue_name] [agent_name] | \n " \
" \t callcenter_config tier list | \n " \
" \t callcenter_config queue load [queue_name] | \n " \
" \t callcenter_config queue unload [queue_name] | \n " \
" \t callcenter_config queue reload [queue_name] | \n " \
" \t callcenter_config queue list | \n " \
" \t callcenter_config queue list agents [queue_name] [status] [state] | \n " \
" \t callcenter_config queue list members [queue_name] | \n " \
" \t callcenter_config queue list tiers [queue_name] | \n " \
" \t callcenter_config queue count | \n " \
" \t callcenter_config queue count agents [queue_name] [status] [state] | \n " \
" \t callcenter_config queue count members [queue_name] | \n " \
" \t callcenter_config queue count tiers [queue_name] "
2010-08-13 13:48:37 +00:00
SWITCH_STANDARD_API ( cc_config_api_function )
{
char * mydata = NULL , * argv [ 8 ] = { 0 } ;
const char * section = NULL ;
const char * action = NULL ;
char * sql ;
int initial_argc = 2 ;
int argc ;
if ( ! globals . running ) {
return SWITCH_STATUS_FALSE ;
}
if ( zstr ( cmd ) ) {
2011-07-16 01:53:57 +00:00
stream - > write_function ( stream , " -USAGE: \n %s \n " , CC_CONFIG_API_SYNTAX ) ;
2010-08-13 13:48:37 +00:00
return SWITCH_STATUS_SUCCESS ;
}
mydata = strdup ( cmd ) ;
switch_assert ( mydata ) ;
argc = switch_separate_string ( mydata , ' ' , argv , ( sizeof ( argv ) / sizeof ( argv [ 0 ] ) ) ) ;
if ( argc < 2 ) {
stream - > write_function ( stream , " %s " , " -ERR Invalid! \n " ) ;
goto done ;
}
2017-07-18 16:25:22 +00:00
section = argv [ 0 ] ;
2010-08-13 13:48:37 +00:00
action = argv [ 1 ] ;
2017-07-18 16:25:22 +00:00
if ( section & & ! strcasecmp ( section , " agent " ) ) {
2010-08-13 13:48:37 +00:00
if ( action & & ! strcasecmp ( action , " add " ) ) {
if ( argc - initial_argc < 2 ) {
stream - > write_function ( stream , " %s " , " -ERR Invalid! \n " ) ;
goto done ;
} else {
const char * name = argv [ 0 + initial_argc ] ;
const char * type = argv [ 1 + initial_argc ] ;
2017-07-18 16:25:22 +00:00
switch ( cc_agent_add ( name , type ) ) {
2010-08-13 13:48:37 +00:00
case CC_STATUS_SUCCESS :
stream - > write_function ( stream , " %s " , " +OK \n " ) ;
break ;
case CC_STATUS_AGENT_ALREADY_EXIST :
stream - > write_function ( stream , " %s " , " -ERR Agent already exist! \n " ) ;
goto done ;
case CC_STATUS_AGENT_INVALID_TYPE :
stream - > write_function ( stream , " %s " , " -ERR Agent type invalid! \n " ) ;
goto done ;
default :
stream - > write_function ( stream , " %s " , " -ERR Unknown Error! \n " ) ;
goto done ;
}
}
} else if ( action & & ! strcasecmp ( action , " del " ) ) {
if ( argc - initial_argc < 1 ) {
stream - > write_function ( stream , " %s " , " -ERR Invalid! \n " ) ;
goto done ;
} else {
const char * agent = argv [ 0 + initial_argc ] ;
2017-07-18 16:25:22 +00:00
switch ( cc_agent_del ( agent ) ) {
2010-08-13 13:48:37 +00:00
case CC_STATUS_SUCCESS :
stream - > write_function ( stream , " %s " , " +OK \n " ) ;
break ;
default :
stream - > write_function ( stream , " %s " , " -ERR Unknown Error! \n " ) ;
goto done ;
}
}
2013-11-28 06:28:00 +00:00
} else if ( action & & ! strcasecmp ( action , " reload " ) ) {
if ( argc - initial_argc < 1 ) {
stream - > write_function ( stream , " %s " , " -ERR Invalid! \n " ) ;
goto done ;
} else {
const char * agent = argv [ 0 + initial_argc ] ;
2017-07-18 16:25:22 +00:00
switch ( load_agent ( agent , NULL , NULL ) ) {
2013-11-28 06:28:00 +00:00
case SWITCH_STATUS_SUCCESS :
stream - > write_function ( stream , " %s " , " +OK \n " ) ;
break ;
default :
stream - > write_function ( stream , " %s " , " -ERR Unknown Error! \n " ) ;
goto done ;
}
}
2010-08-13 13:48:37 +00:00
} else if ( action & & ! strcasecmp ( action , " set " ) ) {
if ( argc - initial_argc < 3 ) {
stream - > write_function ( stream , " %s " , " -ERR Invalid! \n " ) ;
goto done ;
} else {
const char * key = argv [ 0 + initial_argc ] ;
const char * agent = argv [ 1 + initial_argc ] ;
const char * value = argv [ 2 + initial_argc ] ;
2017-07-18 16:25:22 +00:00
switch ( cc_agent_update ( key , value , agent ) ) {
2010-08-13 13:48:37 +00:00
case CC_STATUS_SUCCESS :
stream - > write_function ( stream , " %s " , " +OK \n " ) ;
break ;
case CC_STATUS_AGENT_INVALID_STATUS :
stream - > write_function ( stream , " %s " , " -ERR Invalid Agent Status! \n " ) ;
goto done ;
case CC_STATUS_AGENT_INVALID_STATE :
stream - > write_function ( stream , " %s " , " -ERR Invalid Agent State! \n " ) ;
goto done ;
case CC_STATUS_AGENT_INVALID_TYPE :
stream - > write_function ( stream , " %s " , " -ERR Invalid Agent Type! \n " ) ;
goto done ;
case CC_STATUS_INVALID_KEY :
stream - > write_function ( stream , " %s " , " -ERR Invalid Agent Update KEY! \n " ) ;
goto done ;
2016-11-21 22:32:22 +00:00
case CC_STATUS_AGENT_NOT_FOUND :
2010-08-13 13:48:37 +00:00
stream - > write_function ( stream , " %s " , " -ERR Agent not found! \n " ) ;
goto done ;
default :
stream - > write_function ( stream , " %s " , " -ERR Unknown Error! \n " ) ;
goto done ;
}
}
2011-03-12 16:03:03 +00:00
2010-08-13 13:48:37 +00:00
} else if ( action & & ! strcasecmp ( action , " get " ) ) {
if ( argc - initial_argc < 2 ) {
stream - > write_function ( stream , " %s " , " -ERR Invalid! \n " ) ;
goto done ;
} else {
const char * key = argv [ 0 + initial_argc ] ;
const char * agent = argv [ 1 + initial_argc ] ;
2010-10-30 02:41:14 +00:00
char ret [ 64 ] ;
2017-07-18 16:25:22 +00:00
switch ( cc_agent_get ( key , agent , ret , sizeof ( ret ) ) ) {
2010-08-13 13:48:37 +00:00
case CC_STATUS_SUCCESS :
2010-10-30 02:41:14 +00:00
stream - > write_function ( stream , " %s " , ret ) ;
2010-08-13 13:48:37 +00:00
break ;
case CC_STATUS_INVALID_KEY :
stream - > write_function ( stream , " %s " , " -ERR Invalid Agent Update KEY! \n " ) ;
goto done ;
case CC_STATUS_AGENT_NOT_FOUND :
stream - > write_function ( stream , " %s " , " -ERR Agent not found! \n " ) ;
goto done ;
default :
stream - > write_function ( stream , " %s " , " -ERR Unknown Error! \n " ) ;
goto done ;
}
}
2011-03-12 16:03:03 +00:00
2010-08-13 13:48:37 +00:00
} else if ( action & & ! strcasecmp ( action , " list " ) ) {
struct list_result cbt ;
cbt . row_process = 0 ;
cbt . stream = stream ;
2011-08-16 23:39:06 +00:00
if ( argc - initial_argc > 1 ) {
stream - > write_function ( stream , " %s " , " -ERR Invalid! \n " ) ;
goto done ;
} else if ( argc - initial_argc = = 1 ) {
2017-07-18 16:25:22 +00:00
sql = switch_mprintf ( " SELECT * FROM agents WHERE name='%q' " , argv [ 0 + initial_argc ] ) ;
2011-08-16 23:39:06 +00:00
} else {
sql = switch_mprintf ( " SELECT * FROM agents " ) ;
}
2017-07-18 16:25:22 +00:00
cc_execute_sql_callback ( NULL /* queue */ , NULL /* mutex */ , sql , list_result_callback , & cbt /* Call back variables */ ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
stream - > write_function ( stream , " %s " , " +OK \n " ) ;
}
2016-11-21 22:32:22 +00:00
2010-08-13 13:48:37 +00:00
} else if ( section & & ! strcasecmp ( section , " tier " ) ) {
if ( action & & ! strcasecmp ( action , " add " ) ) {
2015-05-04 19:51:02 +00:00
if ( argc - initial_argc < 2 ) {
2010-08-13 13:48:37 +00:00
stream - > write_function ( stream , " %s " , " -ERR Invalid! \n " ) ;
goto done ;
} else {
2015-05-04 19:51:02 +00:00
int i_level = 1 , i_position = 1 ;
2010-08-13 13:48:37 +00:00
const char * queue_name = argv [ 0 + initial_argc ] ;
const char * agent = argv [ 1 + initial_argc ] ;
const char * level = argv [ 2 + initial_argc ] ;
const char * position = argv [ 3 + initial_argc ] ;
2015-05-04 19:51:02 +00:00
if ( ! zstr ( level ) ) {
i_level = atoi ( level ) ;
}
if ( ! zstr ( position ) ) {
i_position = atoi ( position ) ;
}
2010-08-13 13:48:37 +00:00
2017-07-18 16:25:22 +00:00
switch ( cc_tier_add ( queue_name , agent , cc_tier_state2str ( CC_TIER_STATE_READY ) , i_level , i_position ) ) {
2010-08-13 13:48:37 +00:00
case CC_STATUS_SUCCESS :
stream - > write_function ( stream , " %s " , " +OK \n " ) ;
break ;
case CC_STATUS_QUEUE_NOT_FOUND :
stream - > write_function ( stream , " %s " , " -ERR Queue not found! \n " ) ;
goto done ;
case CC_STATUS_TIER_INVALID_STATE :
stream - > write_function ( stream , " %s " , " -ERR Invalid Tier State! \n " ) ;
goto done ;
case CC_STATUS_AGENT_NOT_FOUND :
stream - > write_function ( stream , " %s " , " -ERR Agent not found! \n " ) ;
goto done ;
case CC_STATUS_TIER_ALREADY_EXIST :
stream - > write_function ( stream , " %s " , " -ERR Tier already exist! \n " ) ;
goto done ;
default :
stream - > write_function ( stream , " %s " , " -ERR Unknown Error! \n " ) ;
goto done ;
}
}
} else if ( action & & ! strcasecmp ( action , " set " ) ) {
if ( argc - initial_argc < 4 ) {
stream - > write_function ( stream , " %s " , " -ERR Invalid! \n " ) ;
goto done ;
} else {
const char * key = argv [ 0 + initial_argc ] ;
const char * queue_name = argv [ 1 + initial_argc ] ;
const char * agent = argv [ 2 + initial_argc ] ;
const char * value = argv [ 3 + initial_argc ] ;
2017-07-18 16:25:22 +00:00
switch ( cc_tier_update ( key , value , queue_name , agent ) ) {
2010-08-13 13:48:37 +00:00
case CC_STATUS_SUCCESS :
stream - > write_function ( stream , " %s " , " +OK \n " ) ;
break ;
case CC_STATUS_AGENT_INVALID_STATUS :
stream - > write_function ( stream , " %s " , " -ERR Invalid Agent Status! \n " ) ;
goto done ;
case CC_STATUS_TIER_INVALID_STATE :
stream - > write_function ( stream , " %s " , " -ERR Invalid Tier State! \n " ) ;
goto done ;
case CC_STATUS_INVALID_KEY :
stream - > write_function ( stream , " %s " , " -ERR Invalid Tier Update KEY! \n " ) ;
goto done ;
case CC_STATUS_AGENT_NOT_FOUND :
stream - > write_function ( stream , " %s " , " -ERR Agent not found! \n " ) ;
goto done ;
case CC_STATUS_QUEUE_NOT_FOUND :
stream - > write_function ( stream , " %s " , " -ERR Agent not found! \n " ) ;
goto done ;
default :
stream - > write_function ( stream , " %s " , " -ERR Unknown Error! \n " ) ;
goto done ;
}
}
2011-03-12 16:03:03 +00:00
2010-08-13 13:48:37 +00:00
} else if ( action & & ! strcasecmp ( action , " del " ) ) {
if ( argc - initial_argc < 2 ) {
stream - > write_function ( stream , " %s " , " -ERR Invalid! \n " ) ;
2016-11-21 22:32:22 +00:00
goto done ;
2010-08-13 13:48:37 +00:00
} else {
const char * queue = argv [ 0 + initial_argc ] ;
const char * agent = argv [ 1 + initial_argc ] ;
2017-07-18 16:25:22 +00:00
switch ( cc_tier_del ( queue , agent ) ) {
2010-08-13 13:48:37 +00:00
case CC_STATUS_SUCCESS :
stream - > write_function ( stream , " %s " , " +OK \n " ) ;
break ;
default :
stream - > write_function ( stream , " %s " , " -ERR Unknown Error! \n " ) ;
goto done ;
}
}
2013-11-28 06:28:00 +00:00
} else if ( action & & ! strcasecmp ( action , " reload " ) ) {
if ( argc - initial_argc < 1 ) {
stream - > write_function ( stream , " %s " , " -ERR Invalid! \n " ) ;
goto done ;
} else {
const char * queue = argv [ 0 + initial_argc ] ;
const char * agent = argv [ 1 + initial_argc ] ;
switch_bool_t load_all = SWITCH_FALSE ;
if ( ! strcasecmp ( queue , " all " ) ) {
load_all = SWITCH_TRUE ;
}
2017-07-18 16:25:22 +00:00
switch ( load_tiers ( load_all , queue , agent , NULL , NULL ) ) {
2013-11-28 06:28:00 +00:00
case SWITCH_STATUS_SUCCESS :
stream - > write_function ( stream , " %s " , " +OK \n " ) ;
break ;
default :
stream - > write_function ( stream , " %s " , " -ERR Unknown Error! \n " ) ;
goto done ;
}
}
2010-08-13 13:48:37 +00:00
} else if ( action & & ! strcasecmp ( action , " list " ) ) {
2011-03-12 16:03:03 +00:00
struct list_result cbt ;
cbt . row_process = 0 ;
cbt . stream = stream ;
sql = switch_mprintf ( " SELECT * FROM tiers ORDER BY level, position " ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql_callback ( NULL /* queue */ , NULL /* mutex */ , sql , list_result_callback , & cbt /* Call back variables */ ) ;
2011-03-12 16:03:03 +00:00
switch_safe_free ( sql ) ;
stream - > write_function ( stream , " %s " , " +OK \n " ) ;
2010-08-13 13:48:37 +00:00
}
} else if ( section & & ! strcasecmp ( section , " queue " ) ) {
2010-08-13 23:15:24 +00:00
if ( action & & ! strcasecmp ( action , " load " ) ) {
if ( argc - initial_argc < 1 ) {
stream - > write_function ( stream , " %s " , " -ERR Invalid! \n " ) ;
goto done ;
} else {
const char * queue_name = argv [ 0 + initial_argc ] ;
cc_queue_t * queue = NULL ;
2017-07-18 16:25:22 +00:00
if ( ( queue = load_queue ( queue_name , SWITCH_TRUE , SWITCH_TRUE , NULL ) ) ) {
2011-01-29 08:09:06 +00:00
stream - > write_function ( stream , " %s " , " +OK \n " ) ;
} else {
stream - > write_function ( stream , " %s " , " -ERR Invalid Queue not found! \n " ) ;
2010-08-13 23:15:24 +00:00
}
}
2011-03-12 16:03:03 +00:00
2010-08-13 23:15:24 +00:00
} else if ( action & & ! strcasecmp ( action , " unload " ) ) {
if ( argc - initial_argc < 1 ) {
stream - > write_function ( stream , " %s " , " -ERR Invalid! \n " ) ;
goto done ;
} else {
const char * queue_name = argv [ 0 + initial_argc ] ;
2017-07-18 16:25:22 +00:00
destroy_queue ( queue_name ) ;
2010-08-13 23:15:24 +00:00
stream - > write_function ( stream , " %s " , " +OK \n " ) ;
}
2011-03-12 16:03:03 +00:00
2010-08-13 23:15:24 +00:00
} else if ( action & & ! strcasecmp ( action , " reload " ) ) {
if ( argc - initial_argc < 1 ) {
stream - > write_function ( stream , " %s " , " -ERR Invalid! \n " ) ;
goto done ;
} else {
const char * queue_name = argv [ 0 + initial_argc ] ;
cc_queue_t * queue = NULL ;
2017-07-18 16:25:22 +00:00
destroy_queue ( queue_name ) ;
if ( ( queue = load_queue ( queue_name , SWITCH_TRUE , SWITCH_TRUE , NULL ) ) ) {
2011-01-29 08:09:06 +00:00
stream - > write_function ( stream , " %s " , " +OK \n " ) ;
} else {
stream - > write_function ( stream , " %s " , " -ERR Invalid Queue not found! \n " ) ;
2010-08-13 23:15:24 +00:00
}
}
2011-03-12 16:03:03 +00:00
2010-08-13 23:15:24 +00:00
} else if ( action & & ! strcasecmp ( action , " list " ) ) {
2011-03-12 16:03:03 +00:00
/* queue list */
2010-08-13 13:48:37 +00:00
if ( argc - initial_argc < 1 ) {
2010-10-13 05:40:18 +00:00
switch_hash_index_t * hi ;
2016-02-07 22:36:47 +00:00
stream - > write_function ( stream , " %s " ,
" name|strategy|moh_sound|time_base_score|tier_rules_apply| " \
" tier_rule_wait_second|tier_rule_wait_multiply_level| " \
" tier_rule_no_agent_no_wait|discard_abandoned_after| " \
" abandoned_resume_allowed|max_wait_time|max_wait_time_with_no_agent| " \
2016-10-05 02:07:42 +00:00
" max_wait_time_with_no_agent_time_reached|record_template|calls_answered|calls_abandoned|ring_progressively_delay|skip_agents_with_external_calls|agent_no_answer_status \n " ) ;
2017-07-18 16:25:22 +00:00
switch_mutex_lock ( globals . mutex ) ;
for ( hi = switch_core_hash_first ( globals . queue_hash ) ; hi ; hi = switch_core_hash_next ( & hi ) ) {
2010-10-13 05:40:18 +00:00
void * val = NULL ;
const void * key ;
switch_ssize_t keylen ;
cc_queue_t * queue ;
2014-03-08 19:37:09 +00:00
switch_core_hash_this ( hi , & key , & keylen , & val ) ;
2010-10-13 05:40:18 +00:00
queue = ( cc_queue_t * ) val ;
2016-10-05 02:07:42 +00:00
stream - > write_function ( stream , " %s|%s|%s|%s|%s|%d|%s|%s|%d|%s|%d|%d|%d|%s|%d|%d|%d|%s|%s \n " ,
2016-02-07 22:36:47 +00:00
queue - > name ,
queue - > strategy ,
queue - > moh ,
queue - > time_base_score ,
( queue - > tier_rules_apply ? " true " : " false " ) ,
queue - > tier_rule_wait_second ,
( queue - > tier_rule_wait_multiply_level ? " true " : " false " ) ,
( queue - > tier_rule_no_agent_no_wait ? " true " : " false " ) ,
queue - > discard_abandoned_after ,
( queue - > abandoned_resume_allowed ? " true " : " false " ) ,
queue - > max_wait_time ,
queue - > max_wait_time_with_no_agent ,
queue - > max_wait_time_with_no_agent_time_reached ,
queue - > record_template ,
queue - > calls_answered ,
2016-04-20 08:15:34 +00:00
queue - > calls_abandoned ,
2016-10-05 02:07:42 +00:00
queue - > ring_progressively_delay ,
( queue - > skip_agents_with_external_calls ? " true " : " false " ) ,
queue - > agent_no_answer_status ) ;
2010-10-13 05:40:18 +00:00
queue = NULL ;
}
2017-07-18 16:25:22 +00:00
switch_mutex_unlock ( globals . mutex ) ;
2010-10-13 05:40:18 +00:00
stream - > write_function ( stream , " %s " , " +OK \n " ) ;
2010-08-13 13:48:37 +00:00
goto done ;
} else {
2011-03-12 16:03:03 +00:00
const char * sub_action = argv [ 0 + initial_argc ] ;
const char * queue_name = argv [ 1 + initial_argc ] ;
const char * status = NULL ;
2014-12-03 01:57:43 +00:00
const char * state = NULL ;
2010-08-13 13:48:37 +00:00
struct list_result cbt ;
2011-03-12 16:03:03 +00:00
/* queue list agents */
if ( sub_action & & ! strcasecmp ( sub_action , " agents " ) ) {
if ( argc - initial_argc > 2 ) {
status = argv [ 2 + initial_argc ] ;
}
2014-12-03 01:57:43 +00:00
if ( argc - initial_argc > 3 ) {
state = argv [ 3 + initial_argc ] ;
}
if ( state ) {
sql = switch_mprintf ( " SELECT agents.* FROM agents,tiers WHERE tiers.agent = agents.name AND tiers.queue = '%q' AND agents.status = '%q' AND agents.state = '%q' " , queue_name , status , state ) ;
}
else if ( status ) {
2011-03-12 16:03:03 +00:00
sql = switch_mprintf ( " SELECT agents.* FROM agents,tiers WHERE tiers.agent = agents.name AND tiers.queue = '%q' AND agents.status = '%q' " , queue_name , status ) ;
} else {
sql = switch_mprintf ( " SELECT agents.* FROM agents,tiers WHERE tiers.agent = agents.name AND tiers.queue = '%q' " , queue_name ) ;
}
/* queue list members */
} else if ( sub_action & & ! strcasecmp ( sub_action , " members " ) ) {
2017-03-06 15:53:48 +00:00
if ( queue_name ) {
sql = switch_mprintf ( " SELECT *,(% " SWITCH_TIME_T_FMT " -joined_epoch)+base_score+skill_score AS score FROM members WHERE queue = '%q' ORDER BY score DESC; " , local_epoch_time_now ( NULL ) , queue_name ) ;
} else {
sql = switch_mprintf ( " SELECT *,(% " SWITCH_TIME_T_FMT " -joined_epoch)+base_score+skill_score AS score FROM members ORDER BY queue, score DESC; " , local_epoch_time_now ( NULL ) ) ;
}
2011-03-12 16:03:03 +00:00
/* queue list tiers */
} else if ( sub_action & & ! strcasecmp ( sub_action , " tiers " ) ) {
sql = switch_mprintf ( " SELECT * FROM tiers WHERE queue = '%q'; " , queue_name ) ;
} else {
stream - > write_function ( stream , " %s " , " -ERR Invalid! \n " ) ;
goto done ;
}
2010-08-13 13:48:37 +00:00
cbt . row_process = 0 ;
cbt . stream = stream ;
2017-07-18 16:25:22 +00:00
cc_execute_sql_callback ( NULL /* queue */ , NULL /* mutex */ , sql , list_result_callback , & cbt /* Call back variables */ ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
stream - > write_function ( stream , " %s " , " +OK \n " ) ;
}
2011-03-12 16:03:03 +00:00
2010-08-13 13:48:37 +00:00
} else if ( action & & ! strcasecmp ( action , " count " ) ) {
2011-03-12 16:03:03 +00:00
/* queue count */
2010-08-13 13:48:37 +00:00
if ( argc - initial_argc < 1 ) {
2011-03-12 16:03:03 +00:00
switch_hash_index_t * hi ;
int queue_count = 0 ;
switch_mutex_lock ( globals . mutex ) ;
2017-07-18 16:25:22 +00:00
for ( hi = switch_core_hash_first ( globals . queue_hash ) ; hi ; hi = switch_core_hash_next ( & hi ) ) {
2011-03-12 16:03:03 +00:00
queue_count + + ;
}
switch_mutex_unlock ( globals . mutex ) ;
stream - > write_function ( stream , " %d \n " , queue_count ) ;
2010-08-13 13:48:37 +00:00
goto done ;
} else {
2011-03-12 16:03:03 +00:00
const char * sub_action = argv [ 0 + initial_argc ] ;
const char * queue_name = argv [ 1 + initial_argc ] ;
const char * status = NULL ;
2014-12-03 01:57:43 +00:00
const char * state = NULL ;
2010-08-13 13:48:37 +00:00
char res [ 256 ] = " " ;
2011-03-12 16:03:03 +00:00
/* queue count agents */
if ( sub_action & & ! strcasecmp ( sub_action , " agents " ) ) {
if ( argc - initial_argc > 2 ) {
status = argv [ 2 + initial_argc ] ;
}
2014-12-03 01:57:43 +00:00
if ( argc - initial_argc > 3 ) {
state = argv [ 3 + initial_argc ] ;
}
if ( state ) {
sql = switch_mprintf ( " SELECT count(*) FROM agents,tiers WHERE tiers.agent = agents.name AND tiers.queue = '%q' AND agents.status = '%q' AND agents.state = '%q' " , queue_name , status , state ) ;
}
else if ( status ) {
2011-03-12 16:03:03 +00:00
sql = switch_mprintf ( " SELECT count(*) FROM agents,tiers WHERE tiers.agent = agents.name AND tiers.queue = '%q' AND agents.status = '%q' " , queue_name , status ) ;
} else {
sql = switch_mprintf ( " SELECT count(*) FROM agents,tiers WHERE tiers.agent = agents.name AND tiers.queue = '%q' " , queue_name ) ;
}
/* queue count members */
} else if ( sub_action & & ! strcasecmp ( sub_action , " members " ) ) {
sql = switch_mprintf ( " SELECT count(*) FROM members WHERE queue = '%q'; " , queue_name ) ;
/* queue count tiers */
} else if ( sub_action & & ! strcasecmp ( sub_action , " tiers " ) ) {
sql = switch_mprintf ( " SELECT count(*) FROM tiers WHERE queue = '%q'; " , queue_name ) ;
} else {
stream - > write_function ( stream , " %s " , " -ERR Invalid! \n " ) ;
goto done ;
}
2017-07-18 16:25:22 +00:00
cc_execute_sql2str ( NULL , NULL , sql , res , sizeof ( res ) ) ;
2010-08-13 13:48:37 +00:00
switch_safe_free ( sql ) ;
stream - > write_function ( stream , " %d \n " , atoi ( res ) ) ;
}
}
}
goto done ;
done :
free ( mydata ) ;
return SWITCH_STATUS_SUCCESS ;
}
2017-09-13 20:39:43 +00:00
SWITCH_STANDARD_API ( cc_break_api_function )
{
char * argv [ 2 ] = { 0 } ;
char * mydata = NULL ;
const char * uuid = NULL ;
switch_core_session_t * break_session = NULL ;
switch_channel_t * channel = NULL ;
switch_bool_t status = SWITCH_STATUS_SUCCESS ;
if ( ! zstr ( cmd ) ) {
mydata = strdup ( cmd ) ;
switch_separate_string ( mydata , ' ' , argv , ( sizeof ( argv ) / sizeof ( argv [ 0 ] ) ) ) ;
} else {
stream - > write_function ( stream , " -ERR Usage: callcenter_break agent <uuid> \n " ) ;
goto end ;
}
uuid = argv [ 1 ] ;
if ( ! uuid ) {
stream - > write_function ( stream , " -ERR Usage: callcenter_break agent <uuid> \n " ) ;
goto end ;
}
break_session = switch_core_session_locate ( uuid ) ;
if ( ! break_session ) {
stream - > write_function ( stream , " -ERR Session not found for uuid %s \n " , uuid ) ;
goto end ;
}
channel = switch_core_session_get_channel ( break_session ) ;
switch_channel_set_app_flag_key ( CC_APP_KEY , channel , CC_APP_SHOULD_BREAK ) ;
end :
if ( break_session ) {
switch_core_session_rwunlock ( break_session ) ;
}
switch_safe_free ( mydata ) ;
return status ;
}
FS-8799 [mod_callcenter] Add JSON API interface for mod_callcenter
Adding a JSON API interface to fetch data in the JSON format. It abides with the JSON command and its implementations by other modules like mod_verto.
It outputs the exactly same column names as the data in CLI.
Various listing of mod_callcenter data such as agents, queues, members, tiers are as follows below.
- Listing of agents
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"agent list"}}`
- Listing of queues
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"queue list"}}`
- Listing of agents for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents", "queue_name":"support@default"}}`
(`queue_name` is a required)
- Listing of members(callers) for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list members", "queue_name":"support@default"}}`
- Listing of tiers for a queue
Command: `json {"command": "callcenter_config","data": {"arguments":"queue list tiers", "queue_name":"support@default"}}`
- Listing of members(callers)
Command: `json {"command": "callcenter_config","data": {"arguments":"member list"}}`
- Listing of tiers
Command: `json {"command": "callcenter_config","data": {"arguments":"tier list"}}`
It handles various errors and also returns back response in JSON as follow
E.g. Send a command without a required field (`queue_name`)
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents"}}`
Output: `{"command":"callcenter_config","data":{"arguments":"queue list agents"},"status":"error","message":"The command returned an error","response":{"error":"Missing data attribute: queue_name"}}`
2016-01-28 08:38:00 +00:00
SWITCH_STANDARD_JSON_API ( json_callcenter_config_function )
{
2016-11-21 22:32:22 +00:00
FS-8799 [mod_callcenter] Add JSON API interface for mod_callcenter
Adding a JSON API interface to fetch data in the JSON format. It abides with the JSON command and its implementations by other modules like mod_verto.
It outputs the exactly same column names as the data in CLI.
Various listing of mod_callcenter data such as agents, queues, members, tiers are as follows below.
- Listing of agents
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"agent list"}}`
- Listing of queues
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"queue list"}}`
- Listing of agents for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents", "queue_name":"support@default"}}`
(`queue_name` is a required)
- Listing of members(callers) for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list members", "queue_name":"support@default"}}`
- Listing of tiers for a queue
Command: `json {"command": "callcenter_config","data": {"arguments":"queue list tiers", "queue_name":"support@default"}}`
- Listing of members(callers)
Command: `json {"command": "callcenter_config","data": {"arguments":"member list"}}`
- Listing of tiers
Command: `json {"command": "callcenter_config","data": {"arguments":"tier list"}}`
It handles various errors and also returns back response in JSON as follow
E.g. Send a command without a required field (`queue_name`)
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents"}}`
Output: `{"command":"callcenter_config","data":{"arguments":"queue list agents"},"status":"error","message":"The command returned an error","response":{"error":"Missing data attribute: queue_name"}}`
2016-01-28 08:38:00 +00:00
cJSON * data = cJSON_GetObjectItem ( json , " data " ) ;
const char * error = NULL ;
2017-07-18 16:25:22 +00:00
const char * arguments = cJSON_GetObjectCstr ( data , " arguments " ) ;
FS-8799 [mod_callcenter] Add JSON API interface for mod_callcenter
Adding a JSON API interface to fetch data in the JSON format. It abides with the JSON command and its implementations by other modules like mod_verto.
It outputs the exactly same column names as the data in CLI.
Various listing of mod_callcenter data such as agents, queues, members, tiers are as follows below.
- Listing of agents
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"agent list"}}`
- Listing of queues
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"queue list"}}`
- Listing of agents for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents", "queue_name":"support@default"}}`
(`queue_name` is a required)
- Listing of members(callers) for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list members", "queue_name":"support@default"}}`
- Listing of tiers for a queue
Command: `json {"command": "callcenter_config","data": {"arguments":"queue list tiers", "queue_name":"support@default"}}`
- Listing of members(callers)
Command: `json {"command": "callcenter_config","data": {"arguments":"member list"}}`
- Listing of tiers
Command: `json {"command": "callcenter_config","data": {"arguments":"tier list"}}`
It handles various errors and also returns back response in JSON as follow
E.g. Send a command without a required field (`queue_name`)
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents"}}`
Output: `{"command":"callcenter_config","data":{"arguments":"queue list agents"},"status":"error","message":"The command returned an error","response":{"error":"Missing data attribute: queue_name"}}`
2016-01-28 08:38:00 +00:00
/* Validate the arguments - try to keep it similar to the CLI api */
2017-07-18 16:25:22 +00:00
if ( zstr ( arguments ) ) {
return SWITCH_STATUS_FALSE ;
FS-8799 [mod_callcenter] Add JSON API interface for mod_callcenter
Adding a JSON API interface to fetch data in the JSON format. It abides with the JSON command and its implementations by other modules like mod_verto.
It outputs the exactly same column names as the data in CLI.
Various listing of mod_callcenter data such as agents, queues, members, tiers are as follows below.
- Listing of agents
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"agent list"}}`
- Listing of queues
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"queue list"}}`
- Listing of agents for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents", "queue_name":"support@default"}}`
(`queue_name` is a required)
- Listing of members(callers) for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list members", "queue_name":"support@default"}}`
- Listing of tiers for a queue
Command: `json {"command": "callcenter_config","data": {"arguments":"queue list tiers", "queue_name":"support@default"}}`
- Listing of members(callers)
Command: `json {"command": "callcenter_config","data": {"arguments":"member list"}}`
- Listing of tiers
Command: `json {"command": "callcenter_config","data": {"arguments":"tier list"}}`
It handles various errors and also returns back response in JSON as follow
E.g. Send a command without a required field (`queue_name`)
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents"}}`
Output: `{"command":"callcenter_config","data":{"arguments":"queue list agents"},"status":"error","message":"The command returned an error","response":{"error":"Missing data attribute: queue_name"}}`
2016-01-28 08:38:00 +00:00
}
/* Prepare the JSON for list of agents */
if ( ! strcasecmp ( arguments , " agent list " ) ) {
struct list_result_json cbt ;
2017-07-18 16:25:22 +00:00
char * sql ;
FS-8799 [mod_callcenter] Add JSON API interface for mod_callcenter
Adding a JSON API interface to fetch data in the JSON format. It abides with the JSON command and its implementations by other modules like mod_verto.
It outputs the exactly same column names as the data in CLI.
Various listing of mod_callcenter data such as agents, queues, members, tiers are as follows below.
- Listing of agents
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"agent list"}}`
- Listing of queues
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"queue list"}}`
- Listing of agents for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents", "queue_name":"support@default"}}`
(`queue_name` is a required)
- Listing of members(callers) for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list members", "queue_name":"support@default"}}`
- Listing of tiers for a queue
Command: `json {"command": "callcenter_config","data": {"arguments":"queue list tiers", "queue_name":"support@default"}}`
- Listing of members(callers)
Command: `json {"command": "callcenter_config","data": {"arguments":"member list"}}`
- Listing of tiers
Command: `json {"command": "callcenter_config","data": {"arguments":"tier list"}}`
It handles various errors and also returns back response in JSON as follow
E.g. Send a command without a required field (`queue_name`)
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents"}}`
Output: `{"command":"callcenter_config","data":{"arguments":"queue list agents"},"status":"error","message":"The command returned an error","response":{"error":"Missing data attribute: queue_name"}}`
2016-01-28 08:38:00 +00:00
cbt . row_process = 0 ;
cbt . json_reply = cJSON_CreateArray ( ) ;
sql = switch_mprintf ( " SELECT * FROM agents " ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql_callback ( NULL /* queue */ , NULL /* mutex */ , sql , list_result_json_callback , & cbt /* Call back variables */ ) ;
switch_safe_free ( sql ) ;
FS-8799 [mod_callcenter] Add JSON API interface for mod_callcenter
Adding a JSON API interface to fetch data in the JSON format. It abides with the JSON command and its implementations by other modules like mod_verto.
It outputs the exactly same column names as the data in CLI.
Various listing of mod_callcenter data such as agents, queues, members, tiers are as follows below.
- Listing of agents
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"agent list"}}`
- Listing of queues
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"queue list"}}`
- Listing of agents for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents", "queue_name":"support@default"}}`
(`queue_name` is a required)
- Listing of members(callers) for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list members", "queue_name":"support@default"}}`
- Listing of tiers for a queue
Command: `json {"command": "callcenter_config","data": {"arguments":"queue list tiers", "queue_name":"support@default"}}`
- Listing of members(callers)
Command: `json {"command": "callcenter_config","data": {"arguments":"member list"}}`
- Listing of tiers
Command: `json {"command": "callcenter_config","data": {"arguments":"tier list"}}`
It handles various errors and also returns back response in JSON as follow
E.g. Send a command without a required field (`queue_name`)
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents"}}`
Output: `{"command":"callcenter_config","data":{"arguments":"queue list agents"},"status":"error","message":"The command returned an error","response":{"error":"Missing data attribute: queue_name"}}`
2016-01-28 08:38:00 +00:00
* json_reply = cbt . json_reply ;
2017-07-18 16:25:22 +00:00
return SWITCH_STATUS_SUCCESS ;
FS-8799 [mod_callcenter] Add JSON API interface for mod_callcenter
Adding a JSON API interface to fetch data in the JSON format. It abides with the JSON command and its implementations by other modules like mod_verto.
It outputs the exactly same column names as the data in CLI.
Various listing of mod_callcenter data such as agents, queues, members, tiers are as follows below.
- Listing of agents
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"agent list"}}`
- Listing of queues
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"queue list"}}`
- Listing of agents for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents", "queue_name":"support@default"}}`
(`queue_name` is a required)
- Listing of members(callers) for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list members", "queue_name":"support@default"}}`
- Listing of tiers for a queue
Command: `json {"command": "callcenter_config","data": {"arguments":"queue list tiers", "queue_name":"support@default"}}`
- Listing of members(callers)
Command: `json {"command": "callcenter_config","data": {"arguments":"member list"}}`
- Listing of tiers
Command: `json {"command": "callcenter_config","data": {"arguments":"tier list"}}`
It handles various errors and also returns back response in JSON as follow
E.g. Send a command without a required field (`queue_name`)
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents"}}`
Output: `{"command":"callcenter_config","data":{"arguments":"queue list agents"},"status":"error","message":"The command returned an error","response":{"error":"Missing data attribute: queue_name"}}`
2016-01-28 08:38:00 +00:00
}
/* Prepare the JSON for list of queues */
if ( ! strcasecmp ( arguments , " queue list " ) ) {
cJSON * reply = cJSON_CreateArray ( ) ;
switch_hash_index_t * hi ;
switch_mutex_lock ( globals . mutex ) ;
2016-11-21 22:32:22 +00:00
2017-07-18 16:25:22 +00:00
for ( hi = switch_core_hash_first ( globals . queue_hash ) ; hi ; hi = switch_core_hash_next ( & hi ) ) {
2016-10-05 02:07:42 +00:00
cJSON * o = cJSON_CreateObject ( ) ;
void * val = NULL ;
const void * key ;
switch_ssize_t keylen ;
cc_queue_t * queue ;
switch_core_hash_this ( hi , & key , & keylen , & val ) ;
queue = ( cc_queue_t * ) val ;
cJSON_AddItemToObject ( o , " name " , cJSON_CreateString ( queue - > name ) ) ;
cJSON_AddItemToObject ( o , " strategy " , cJSON_CreateString ( queue - > strategy ) ) ;
cJSON_AddItemToObject ( o , " moh_sound " , cJSON_CreateString ( queue - > moh ) ) ;
cJSON_AddItemToObject ( o , " time_base_score " , cJSON_CreateString ( queue - > time_base_score ) ) ;
cJSON_AddItemToObject ( o , " tier_rules_apply " , cJSON_CreateString ( queue - > tier_rules_apply ? " true " : " false " ) ) ;
cJSON_AddItemToObject ( o , " tier_rule_wait_second " , cJSON_CreateNumber ( queue - > tier_rule_wait_second ) ) ;
cJSON_AddItemToObject ( o , " tier_rule_wait_multiply_level " , cJSON_CreateString ( queue - > tier_rule_wait_multiply_level ? " true " : " false " ) ) ;
cJSON_AddItemToObject ( o , " tier_rule_no_agent_no_wait " , cJSON_CreateString ( queue - > tier_rule_no_agent_no_wait ? " true " : " false " ) ) ;
cJSON_AddItemToObject ( o , " discard_abandoned_after " , cJSON_CreateNumber ( queue - > discard_abandoned_after ) ) ;
cJSON_AddItemToObject ( o , " abandoned_resume_allowed " , cJSON_CreateString ( queue - > abandoned_resume_allowed ? " true " : " false " ) ) ;
cJSON_AddItemToObject ( o , " max_wait_time " , cJSON_CreateNumber ( queue - > max_wait_time ) ) ;
cJSON_AddItemToObject ( o , " max_wait_time_with_no_agent " , cJSON_CreateNumber ( queue - > max_wait_time_with_no_agent ) ) ;
cJSON_AddItemToObject ( o , " max_wait_time_with_no_agent_time_reached " , cJSON_CreateNumber ( queue - > max_wait_time_with_no_agent_time_reached ) ) ;
cJSON_AddItemToObject ( o , " record_template " , cJSON_CreateString ( queue - > record_template ) ) ;
cJSON_AddItemToObject ( o , " skip_agents_with_external_calls " , cJSON_CreateString ( queue - > skip_agents_with_external_calls ? " true " : " false " ) ) ;
cJSON_AddItemToObject ( o , " agent_no_answer_status " , cJSON_CreateString ( queue - > agent_no_answer_status ) ) ;
cJSON_AddItemToArray ( reply , o ) ;
queue = NULL ;
FS-8799 [mod_callcenter] Add JSON API interface for mod_callcenter
Adding a JSON API interface to fetch data in the JSON format. It abides with the JSON command and its implementations by other modules like mod_verto.
It outputs the exactly same column names as the data in CLI.
Various listing of mod_callcenter data such as agents, queues, members, tiers are as follows below.
- Listing of agents
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"agent list"}}`
- Listing of queues
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"queue list"}}`
- Listing of agents for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents", "queue_name":"support@default"}}`
(`queue_name` is a required)
- Listing of members(callers) for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list members", "queue_name":"support@default"}}`
- Listing of tiers for a queue
Command: `json {"command": "callcenter_config","data": {"arguments":"queue list tiers", "queue_name":"support@default"}}`
- Listing of members(callers)
Command: `json {"command": "callcenter_config","data": {"arguments":"member list"}}`
- Listing of tiers
Command: `json {"command": "callcenter_config","data": {"arguments":"tier list"}}`
It handles various errors and also returns back response in JSON as follow
E.g. Send a command without a required field (`queue_name`)
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents"}}`
Output: `{"command":"callcenter_config","data":{"arguments":"queue list agents"},"status":"error","message":"The command returned an error","response":{"error":"Missing data attribute: queue_name"}}`
2016-01-28 08:38:00 +00:00
}
switch_mutex_unlock ( globals . mutex ) ;
* json_reply = reply ;
2017-07-18 16:25:22 +00:00
return SWITCH_STATUS_SUCCESS ;
FS-8799 [mod_callcenter] Add JSON API interface for mod_callcenter
Adding a JSON API interface to fetch data in the JSON format. It abides with the JSON command and its implementations by other modules like mod_verto.
It outputs the exactly same column names as the data in CLI.
Various listing of mod_callcenter data such as agents, queues, members, tiers are as follows below.
- Listing of agents
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"agent list"}}`
- Listing of queues
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"queue list"}}`
- Listing of agents for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents", "queue_name":"support@default"}}`
(`queue_name` is a required)
- Listing of members(callers) for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list members", "queue_name":"support@default"}}`
- Listing of tiers for a queue
Command: `json {"command": "callcenter_config","data": {"arguments":"queue list tiers", "queue_name":"support@default"}}`
- Listing of members(callers)
Command: `json {"command": "callcenter_config","data": {"arguments":"member list"}}`
- Listing of tiers
Command: `json {"command": "callcenter_config","data": {"arguments":"tier list"}}`
It handles various errors and also returns back response in JSON as follow
E.g. Send a command without a required field (`queue_name`)
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents"}}`
Output: `{"command":"callcenter_config","data":{"arguments":"queue list agents"},"status":"error","message":"The command returned an error","response":{"error":"Missing data attribute: queue_name"}}`
2016-01-28 08:38:00 +00:00
}
/* Prepare the JSON for list of agents for a queue */
if ( ! strcasecmp ( arguments , " queue list agents " ) ) {
struct list_result_json cbt ;
const char * queue_name = cJSON_GetObjectCstr ( data , " queue_name " ) ;
2017-07-18 16:25:22 +00:00
char * sql ;
FS-8799 [mod_callcenter] Add JSON API interface for mod_callcenter
Adding a JSON API interface to fetch data in the JSON format. It abides with the JSON command and its implementations by other modules like mod_verto.
It outputs the exactly same column names as the data in CLI.
Various listing of mod_callcenter data such as agents, queues, members, tiers are as follows below.
- Listing of agents
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"agent list"}}`
- Listing of queues
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"queue list"}}`
- Listing of agents for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents", "queue_name":"support@default"}}`
(`queue_name` is a required)
- Listing of members(callers) for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list members", "queue_name":"support@default"}}`
- Listing of tiers for a queue
Command: `json {"command": "callcenter_config","data": {"arguments":"queue list tiers", "queue_name":"support@default"}}`
- Listing of members(callers)
Command: `json {"command": "callcenter_config","data": {"arguments":"member list"}}`
- Listing of tiers
Command: `json {"command": "callcenter_config","data": {"arguments":"tier list"}}`
It handles various errors and also returns back response in JSON as follow
E.g. Send a command without a required field (`queue_name`)
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents"}}`
Output: `{"command":"callcenter_config","data":{"arguments":"queue list agents"},"status":"error","message":"The command returned an error","response":{"error":"Missing data attribute: queue_name"}}`
2016-01-28 08:38:00 +00:00
cJSON * error_reply = cJSON_CreateObject ( ) ;
if ( zstr ( queue_name ) ) {
error = " Missing data attribute: queue_name " ;
cJSON_AddItemToObject ( error_reply , " error " , cJSON_CreateString ( error ) ) ;
* json_reply = error_reply ;
2017-07-18 16:25:22 +00:00
return SWITCH_STATUS_FALSE ;
FS-8799 [mod_callcenter] Add JSON API interface for mod_callcenter
Adding a JSON API interface to fetch data in the JSON format. It abides with the JSON command and its implementations by other modules like mod_verto.
It outputs the exactly same column names as the data in CLI.
Various listing of mod_callcenter data such as agents, queues, members, tiers are as follows below.
- Listing of agents
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"agent list"}}`
- Listing of queues
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"queue list"}}`
- Listing of agents for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents", "queue_name":"support@default"}}`
(`queue_name` is a required)
- Listing of members(callers) for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list members", "queue_name":"support@default"}}`
- Listing of tiers for a queue
Command: `json {"command": "callcenter_config","data": {"arguments":"queue list tiers", "queue_name":"support@default"}}`
- Listing of members(callers)
Command: `json {"command": "callcenter_config","data": {"arguments":"member list"}}`
- Listing of tiers
Command: `json {"command": "callcenter_config","data": {"arguments":"tier list"}}`
It handles various errors and also returns back response in JSON as follow
E.g. Send a command without a required field (`queue_name`)
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents"}}`
Output: `{"command":"callcenter_config","data":{"arguments":"queue list agents"},"status":"error","message":"The command returned an error","response":{"error":"Missing data attribute: queue_name"}}`
2016-01-28 08:38:00 +00:00
}
cbt . row_process = 0 ;
cbt . json_reply = cJSON_CreateArray ( ) ;
sql = switch_mprintf ( " SELECT agents.* FROM agents,tiers WHERE tiers.agent = agents.name AND tiers.queue = '%q' " , queue_name ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql_callback ( NULL /* queue */ , NULL /* mutex */ , sql , list_result_json_callback , & cbt /* Call back variables */ ) ;
switch_safe_free ( sql ) ;
FS-8799 [mod_callcenter] Add JSON API interface for mod_callcenter
Adding a JSON API interface to fetch data in the JSON format. It abides with the JSON command and its implementations by other modules like mod_verto.
It outputs the exactly same column names as the data in CLI.
Various listing of mod_callcenter data such as agents, queues, members, tiers are as follows below.
- Listing of agents
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"agent list"}}`
- Listing of queues
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"queue list"}}`
- Listing of agents for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents", "queue_name":"support@default"}}`
(`queue_name` is a required)
- Listing of members(callers) for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list members", "queue_name":"support@default"}}`
- Listing of tiers for a queue
Command: `json {"command": "callcenter_config","data": {"arguments":"queue list tiers", "queue_name":"support@default"}}`
- Listing of members(callers)
Command: `json {"command": "callcenter_config","data": {"arguments":"member list"}}`
- Listing of tiers
Command: `json {"command": "callcenter_config","data": {"arguments":"tier list"}}`
It handles various errors and also returns back response in JSON as follow
E.g. Send a command without a required field (`queue_name`)
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents"}}`
Output: `{"command":"callcenter_config","data":{"arguments":"queue list agents"},"status":"error","message":"The command returned an error","response":{"error":"Missing data attribute: queue_name"}}`
2016-01-28 08:38:00 +00:00
* json_reply = cbt . json_reply ;
2017-07-18 16:25:22 +00:00
return SWITCH_STATUS_SUCCESS ;
FS-8799 [mod_callcenter] Add JSON API interface for mod_callcenter
Adding a JSON API interface to fetch data in the JSON format. It abides with the JSON command and its implementations by other modules like mod_verto.
It outputs the exactly same column names as the data in CLI.
Various listing of mod_callcenter data such as agents, queues, members, tiers are as follows below.
- Listing of agents
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"agent list"}}`
- Listing of queues
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"queue list"}}`
- Listing of agents for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents", "queue_name":"support@default"}}`
(`queue_name` is a required)
- Listing of members(callers) for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list members", "queue_name":"support@default"}}`
- Listing of tiers for a queue
Command: `json {"command": "callcenter_config","data": {"arguments":"queue list tiers", "queue_name":"support@default"}}`
- Listing of members(callers)
Command: `json {"command": "callcenter_config","data": {"arguments":"member list"}}`
- Listing of tiers
Command: `json {"command": "callcenter_config","data": {"arguments":"tier list"}}`
It handles various errors and also returns back response in JSON as follow
E.g. Send a command without a required field (`queue_name`)
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents"}}`
Output: `{"command":"callcenter_config","data":{"arguments":"queue list agents"},"status":"error","message":"The command returned an error","response":{"error":"Missing data attribute: queue_name"}}`
2016-01-28 08:38:00 +00:00
}
/* Prepare the JSON for list of callers for a queue */
if ( ! strcasecmp ( arguments , " queue list members " ) ) {
struct list_result_json cbt ;
const char * queue_name = cJSON_GetObjectCstr ( data , " queue_name " ) ;
2017-07-18 16:25:22 +00:00
char * sql ;
FS-8799 [mod_callcenter] Add JSON API interface for mod_callcenter
Adding a JSON API interface to fetch data in the JSON format. It abides with the JSON command and its implementations by other modules like mod_verto.
It outputs the exactly same column names as the data in CLI.
Various listing of mod_callcenter data such as agents, queues, members, tiers are as follows below.
- Listing of agents
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"agent list"}}`
- Listing of queues
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"queue list"}}`
- Listing of agents for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents", "queue_name":"support@default"}}`
(`queue_name` is a required)
- Listing of members(callers) for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list members", "queue_name":"support@default"}}`
- Listing of tiers for a queue
Command: `json {"command": "callcenter_config","data": {"arguments":"queue list tiers", "queue_name":"support@default"}}`
- Listing of members(callers)
Command: `json {"command": "callcenter_config","data": {"arguments":"member list"}}`
- Listing of tiers
Command: `json {"command": "callcenter_config","data": {"arguments":"tier list"}}`
It handles various errors and also returns back response in JSON as follow
E.g. Send a command without a required field (`queue_name`)
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents"}}`
Output: `{"command":"callcenter_config","data":{"arguments":"queue list agents"},"status":"error","message":"The command returned an error","response":{"error":"Missing data attribute: queue_name"}}`
2016-01-28 08:38:00 +00:00
cJSON * error_reply = cJSON_CreateObject ( ) ;
if ( zstr ( queue_name ) ) {
error = " Missing data attribute: queue_name " ;
cJSON_AddItemToObject ( error_reply , " error " , cJSON_CreateString ( error ) ) ;
* json_reply = error_reply ;
2017-07-18 16:25:22 +00:00
return SWITCH_STATUS_FALSE ;
FS-8799 [mod_callcenter] Add JSON API interface for mod_callcenter
Adding a JSON API interface to fetch data in the JSON format. It abides with the JSON command and its implementations by other modules like mod_verto.
It outputs the exactly same column names as the data in CLI.
Various listing of mod_callcenter data such as agents, queues, members, tiers are as follows below.
- Listing of agents
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"agent list"}}`
- Listing of queues
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"queue list"}}`
- Listing of agents for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents", "queue_name":"support@default"}}`
(`queue_name` is a required)
- Listing of members(callers) for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list members", "queue_name":"support@default"}}`
- Listing of tiers for a queue
Command: `json {"command": "callcenter_config","data": {"arguments":"queue list tiers", "queue_name":"support@default"}}`
- Listing of members(callers)
Command: `json {"command": "callcenter_config","data": {"arguments":"member list"}}`
- Listing of tiers
Command: `json {"command": "callcenter_config","data": {"arguments":"tier list"}}`
It handles various errors and also returns back response in JSON as follow
E.g. Send a command without a required field (`queue_name`)
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents"}}`
Output: `{"command":"callcenter_config","data":{"arguments":"queue list agents"},"status":"error","message":"The command returned an error","response":{"error":"Missing data attribute: queue_name"}}`
2016-01-28 08:38:00 +00:00
}
cbt . row_process = 0 ;
cbt . json_reply = cJSON_CreateArray ( ) ;
sql = switch_mprintf ( " SELECT *,(% " SWITCH_TIME_T_FMT " -joined_epoch)+base_score+skill_score AS score FROM members WHERE queue = '%q' ORDER BY score DESC; " , local_epoch_time_now ( NULL ) , queue_name ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql_callback ( NULL /* queue */ , NULL /* mutex */ , sql , list_result_json_callback , & cbt /* Call back variables */ ) ;
switch_safe_free ( sql ) ;
FS-8799 [mod_callcenter] Add JSON API interface for mod_callcenter
Adding a JSON API interface to fetch data in the JSON format. It abides with the JSON command and its implementations by other modules like mod_verto.
It outputs the exactly same column names as the data in CLI.
Various listing of mod_callcenter data such as agents, queues, members, tiers are as follows below.
- Listing of agents
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"agent list"}}`
- Listing of queues
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"queue list"}}`
- Listing of agents for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents", "queue_name":"support@default"}}`
(`queue_name` is a required)
- Listing of members(callers) for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list members", "queue_name":"support@default"}}`
- Listing of tiers for a queue
Command: `json {"command": "callcenter_config","data": {"arguments":"queue list tiers", "queue_name":"support@default"}}`
- Listing of members(callers)
Command: `json {"command": "callcenter_config","data": {"arguments":"member list"}}`
- Listing of tiers
Command: `json {"command": "callcenter_config","data": {"arguments":"tier list"}}`
It handles various errors and also returns back response in JSON as follow
E.g. Send a command without a required field (`queue_name`)
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents"}}`
Output: `{"command":"callcenter_config","data":{"arguments":"queue list agents"},"status":"error","message":"The command returned an error","response":{"error":"Missing data attribute: queue_name"}}`
2016-01-28 08:38:00 +00:00
* json_reply = cbt . json_reply ;
2017-07-18 16:25:22 +00:00
return SWITCH_STATUS_SUCCESS ;
FS-8799 [mod_callcenter] Add JSON API interface for mod_callcenter
Adding a JSON API interface to fetch data in the JSON format. It abides with the JSON command and its implementations by other modules like mod_verto.
It outputs the exactly same column names as the data in CLI.
Various listing of mod_callcenter data such as agents, queues, members, tiers are as follows below.
- Listing of agents
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"agent list"}}`
- Listing of queues
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"queue list"}}`
- Listing of agents for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents", "queue_name":"support@default"}}`
(`queue_name` is a required)
- Listing of members(callers) for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list members", "queue_name":"support@default"}}`
- Listing of tiers for a queue
Command: `json {"command": "callcenter_config","data": {"arguments":"queue list tiers", "queue_name":"support@default"}}`
- Listing of members(callers)
Command: `json {"command": "callcenter_config","data": {"arguments":"member list"}}`
- Listing of tiers
Command: `json {"command": "callcenter_config","data": {"arguments":"tier list"}}`
It handles various errors and also returns back response in JSON as follow
E.g. Send a command without a required field (`queue_name`)
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents"}}`
Output: `{"command":"callcenter_config","data":{"arguments":"queue list agents"},"status":"error","message":"The command returned an error","response":{"error":"Missing data attribute: queue_name"}}`
2016-01-28 08:38:00 +00:00
}
/* Prepare the JSON for list of tiers for a queue */
if ( ! strcasecmp ( arguments , " queue list tiers " ) ) {
struct list_result_json cbt ;
const char * queue_name = cJSON_GetObjectCstr ( data , " queue_name " ) ;
2017-07-18 16:25:22 +00:00
char * sql ;
FS-8799 [mod_callcenter] Add JSON API interface for mod_callcenter
Adding a JSON API interface to fetch data in the JSON format. It abides with the JSON command and its implementations by other modules like mod_verto.
It outputs the exactly same column names as the data in CLI.
Various listing of mod_callcenter data such as agents, queues, members, tiers are as follows below.
- Listing of agents
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"agent list"}}`
- Listing of queues
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"queue list"}}`
- Listing of agents for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents", "queue_name":"support@default"}}`
(`queue_name` is a required)
- Listing of members(callers) for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list members", "queue_name":"support@default"}}`
- Listing of tiers for a queue
Command: `json {"command": "callcenter_config","data": {"arguments":"queue list tiers", "queue_name":"support@default"}}`
- Listing of members(callers)
Command: `json {"command": "callcenter_config","data": {"arguments":"member list"}}`
- Listing of tiers
Command: `json {"command": "callcenter_config","data": {"arguments":"tier list"}}`
It handles various errors and also returns back response in JSON as follow
E.g. Send a command without a required field (`queue_name`)
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents"}}`
Output: `{"command":"callcenter_config","data":{"arguments":"queue list agents"},"status":"error","message":"The command returned an error","response":{"error":"Missing data attribute: queue_name"}}`
2016-01-28 08:38:00 +00:00
cJSON * error_reply = cJSON_CreateObject ( ) ;
if ( zstr ( queue_name ) ) {
error = " Missing data attribute: queue_name " ;
cJSON_AddItemToObject ( error_reply , " error " , cJSON_CreateString ( error ) ) ;
* json_reply = error_reply ;
2017-07-18 16:25:22 +00:00
return SWITCH_STATUS_FALSE ;
FS-8799 [mod_callcenter] Add JSON API interface for mod_callcenter
Adding a JSON API interface to fetch data in the JSON format. It abides with the JSON command and its implementations by other modules like mod_verto.
It outputs the exactly same column names as the data in CLI.
Various listing of mod_callcenter data such as agents, queues, members, tiers are as follows below.
- Listing of agents
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"agent list"}}`
- Listing of queues
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"queue list"}}`
- Listing of agents for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents", "queue_name":"support@default"}}`
(`queue_name` is a required)
- Listing of members(callers) for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list members", "queue_name":"support@default"}}`
- Listing of tiers for a queue
Command: `json {"command": "callcenter_config","data": {"arguments":"queue list tiers", "queue_name":"support@default"}}`
- Listing of members(callers)
Command: `json {"command": "callcenter_config","data": {"arguments":"member list"}}`
- Listing of tiers
Command: `json {"command": "callcenter_config","data": {"arguments":"tier list"}}`
It handles various errors and also returns back response in JSON as follow
E.g. Send a command without a required field (`queue_name`)
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents"}}`
Output: `{"command":"callcenter_config","data":{"arguments":"queue list agents"},"status":"error","message":"The command returned an error","response":{"error":"Missing data attribute: queue_name"}}`
2016-01-28 08:38:00 +00:00
}
cbt . row_process = 0 ;
cbt . json_reply = cJSON_CreateArray ( ) ;
sql = switch_mprintf ( " SELECT * FROM tiers WHERE queue = '%q'; " , queue_name ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql_callback ( NULL /* queue */ , NULL /* mutex */ , sql , list_result_json_callback , & cbt /* Call back variables */ ) ;
switch_safe_free ( sql ) ;
FS-8799 [mod_callcenter] Add JSON API interface for mod_callcenter
Adding a JSON API interface to fetch data in the JSON format. It abides with the JSON command and its implementations by other modules like mod_verto.
It outputs the exactly same column names as the data in CLI.
Various listing of mod_callcenter data such as agents, queues, members, tiers are as follows below.
- Listing of agents
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"agent list"}}`
- Listing of queues
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"queue list"}}`
- Listing of agents for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents", "queue_name":"support@default"}}`
(`queue_name` is a required)
- Listing of members(callers) for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list members", "queue_name":"support@default"}}`
- Listing of tiers for a queue
Command: `json {"command": "callcenter_config","data": {"arguments":"queue list tiers", "queue_name":"support@default"}}`
- Listing of members(callers)
Command: `json {"command": "callcenter_config","data": {"arguments":"member list"}}`
- Listing of tiers
Command: `json {"command": "callcenter_config","data": {"arguments":"tier list"}}`
It handles various errors and also returns back response in JSON as follow
E.g. Send a command without a required field (`queue_name`)
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents"}}`
Output: `{"command":"callcenter_config","data":{"arguments":"queue list agents"},"status":"error","message":"The command returned an error","response":{"error":"Missing data attribute: queue_name"}}`
2016-01-28 08:38:00 +00:00
* json_reply = cbt . json_reply ;
2017-07-18 16:25:22 +00:00
return SWITCH_STATUS_SUCCESS ;
FS-8799 [mod_callcenter] Add JSON API interface for mod_callcenter
Adding a JSON API interface to fetch data in the JSON format. It abides with the JSON command and its implementations by other modules like mod_verto.
It outputs the exactly same column names as the data in CLI.
Various listing of mod_callcenter data such as agents, queues, members, tiers are as follows below.
- Listing of agents
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"agent list"}}`
- Listing of queues
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"queue list"}}`
- Listing of agents for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents", "queue_name":"support@default"}}`
(`queue_name` is a required)
- Listing of members(callers) for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list members", "queue_name":"support@default"}}`
- Listing of tiers for a queue
Command: `json {"command": "callcenter_config","data": {"arguments":"queue list tiers", "queue_name":"support@default"}}`
- Listing of members(callers)
Command: `json {"command": "callcenter_config","data": {"arguments":"member list"}}`
- Listing of tiers
Command: `json {"command": "callcenter_config","data": {"arguments":"tier list"}}`
It handles various errors and also returns back response in JSON as follow
E.g. Send a command without a required field (`queue_name`)
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents"}}`
Output: `{"command":"callcenter_config","data":{"arguments":"queue list agents"},"status":"error","message":"The command returned an error","response":{"error":"Missing data attribute: queue_name"}}`
2016-01-28 08:38:00 +00:00
}
/* Prepare the JSON for list of all callers */
if ( ! strcasecmp ( arguments , " member list " ) ) {
struct list_result_json cbt ;
2017-07-18 16:25:22 +00:00
char * sql ;
FS-8799 [mod_callcenter] Add JSON API interface for mod_callcenter
Adding a JSON API interface to fetch data in the JSON format. It abides with the JSON command and its implementations by other modules like mod_verto.
It outputs the exactly same column names as the data in CLI.
Various listing of mod_callcenter data such as agents, queues, members, tiers are as follows below.
- Listing of agents
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"agent list"}}`
- Listing of queues
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"queue list"}}`
- Listing of agents for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents", "queue_name":"support@default"}}`
(`queue_name` is a required)
- Listing of members(callers) for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list members", "queue_name":"support@default"}}`
- Listing of tiers for a queue
Command: `json {"command": "callcenter_config","data": {"arguments":"queue list tiers", "queue_name":"support@default"}}`
- Listing of members(callers)
Command: `json {"command": "callcenter_config","data": {"arguments":"member list"}}`
- Listing of tiers
Command: `json {"command": "callcenter_config","data": {"arguments":"tier list"}}`
It handles various errors and also returns back response in JSON as follow
E.g. Send a command without a required field (`queue_name`)
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents"}}`
Output: `{"command":"callcenter_config","data":{"arguments":"queue list agents"},"status":"error","message":"The command returned an error","response":{"error":"Missing data attribute: queue_name"}}`
2016-01-28 08:38:00 +00:00
cbt . row_process = 0 ;
cbt . json_reply = cJSON_CreateArray ( ) ;
sql = switch_mprintf ( " SELECT *,(% " SWITCH_TIME_T_FMT " -joined_epoch)+base_score+skill_score AS score FROM members ORDER BY score DESC; " , local_epoch_time_now ( NULL ) ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql_callback ( NULL /* queue */ , NULL /* mutex */ , sql , list_result_json_callback , & cbt /* Call back variables */ ) ;
switch_safe_free ( sql ) ;
FS-8799 [mod_callcenter] Add JSON API interface for mod_callcenter
Adding a JSON API interface to fetch data in the JSON format. It abides with the JSON command and its implementations by other modules like mod_verto.
It outputs the exactly same column names as the data in CLI.
Various listing of mod_callcenter data such as agents, queues, members, tiers are as follows below.
- Listing of agents
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"agent list"}}`
- Listing of queues
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"queue list"}}`
- Listing of agents for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents", "queue_name":"support@default"}}`
(`queue_name` is a required)
- Listing of members(callers) for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list members", "queue_name":"support@default"}}`
- Listing of tiers for a queue
Command: `json {"command": "callcenter_config","data": {"arguments":"queue list tiers", "queue_name":"support@default"}}`
- Listing of members(callers)
Command: `json {"command": "callcenter_config","data": {"arguments":"member list"}}`
- Listing of tiers
Command: `json {"command": "callcenter_config","data": {"arguments":"tier list"}}`
It handles various errors and also returns back response in JSON as follow
E.g. Send a command without a required field (`queue_name`)
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents"}}`
Output: `{"command":"callcenter_config","data":{"arguments":"queue list agents"},"status":"error","message":"The command returned an error","response":{"error":"Missing data attribute: queue_name"}}`
2016-01-28 08:38:00 +00:00
* json_reply = cbt . json_reply ;
2017-07-18 16:25:22 +00:00
return SWITCH_STATUS_SUCCESS ;
FS-8799 [mod_callcenter] Add JSON API interface for mod_callcenter
Adding a JSON API interface to fetch data in the JSON format. It abides with the JSON command and its implementations by other modules like mod_verto.
It outputs the exactly same column names as the data in CLI.
Various listing of mod_callcenter data such as agents, queues, members, tiers are as follows below.
- Listing of agents
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"agent list"}}`
- Listing of queues
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"queue list"}}`
- Listing of agents for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents", "queue_name":"support@default"}}`
(`queue_name` is a required)
- Listing of members(callers) for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list members", "queue_name":"support@default"}}`
- Listing of tiers for a queue
Command: `json {"command": "callcenter_config","data": {"arguments":"queue list tiers", "queue_name":"support@default"}}`
- Listing of members(callers)
Command: `json {"command": "callcenter_config","data": {"arguments":"member list"}}`
- Listing of tiers
Command: `json {"command": "callcenter_config","data": {"arguments":"tier list"}}`
It handles various errors and also returns back response in JSON as follow
E.g. Send a command without a required field (`queue_name`)
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents"}}`
Output: `{"command":"callcenter_config","data":{"arguments":"queue list agents"},"status":"error","message":"The command returned an error","response":{"error":"Missing data attribute: queue_name"}}`
2016-01-28 08:38:00 +00:00
}
/* Prepare the JSON for list of all tiers */
if ( ! strcasecmp ( arguments , " tier list " ) ) {
struct list_result_json cbt ;
2017-07-18 16:25:22 +00:00
char * sql ;
FS-8799 [mod_callcenter] Add JSON API interface for mod_callcenter
Adding a JSON API interface to fetch data in the JSON format. It abides with the JSON command and its implementations by other modules like mod_verto.
It outputs the exactly same column names as the data in CLI.
Various listing of mod_callcenter data such as agents, queues, members, tiers are as follows below.
- Listing of agents
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"agent list"}}`
- Listing of queues
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"queue list"}}`
- Listing of agents for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents", "queue_name":"support@default"}}`
(`queue_name` is a required)
- Listing of members(callers) for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list members", "queue_name":"support@default"}}`
- Listing of tiers for a queue
Command: `json {"command": "callcenter_config","data": {"arguments":"queue list tiers", "queue_name":"support@default"}}`
- Listing of members(callers)
Command: `json {"command": "callcenter_config","data": {"arguments":"member list"}}`
- Listing of tiers
Command: `json {"command": "callcenter_config","data": {"arguments":"tier list"}}`
It handles various errors and also returns back response in JSON as follow
E.g. Send a command without a required field (`queue_name`)
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents"}}`
Output: `{"command":"callcenter_config","data":{"arguments":"queue list agents"},"status":"error","message":"The command returned an error","response":{"error":"Missing data attribute: queue_name"}}`
2016-01-28 08:38:00 +00:00
cbt . row_process = 0 ;
cbt . json_reply = cJSON_CreateArray ( ) ;
sql = switch_mprintf ( " SELECT * FROM tiers ORDER BY level, position " ) ;
2017-07-18 16:25:22 +00:00
cc_execute_sql_callback ( NULL /* queue */ , NULL /* mutex */ , sql , list_result_json_callback , & cbt /* Call back variables */ ) ;
switch_safe_free ( sql ) ;
FS-8799 [mod_callcenter] Add JSON API interface for mod_callcenter
Adding a JSON API interface to fetch data in the JSON format. It abides with the JSON command and its implementations by other modules like mod_verto.
It outputs the exactly same column names as the data in CLI.
Various listing of mod_callcenter data such as agents, queues, members, tiers are as follows below.
- Listing of agents
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"agent list"}}`
- Listing of queues
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"queue list"}}`
- Listing of agents for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents", "queue_name":"support@default"}}`
(`queue_name` is a required)
- Listing of members(callers) for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list members", "queue_name":"support@default"}}`
- Listing of tiers for a queue
Command: `json {"command": "callcenter_config","data": {"arguments":"queue list tiers", "queue_name":"support@default"}}`
- Listing of members(callers)
Command: `json {"command": "callcenter_config","data": {"arguments":"member list"}}`
- Listing of tiers
Command: `json {"command": "callcenter_config","data": {"arguments":"tier list"}}`
It handles various errors and also returns back response in JSON as follow
E.g. Send a command without a required field (`queue_name`)
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents"}}`
Output: `{"command":"callcenter_config","data":{"arguments":"queue list agents"},"status":"error","message":"The command returned an error","response":{"error":"Missing data attribute: queue_name"}}`
2016-01-28 08:38:00 +00:00
* json_reply = cbt . json_reply ;
2017-07-18 16:25:22 +00:00
return SWITCH_STATUS_SUCCESS ;
FS-8799 [mod_callcenter] Add JSON API interface for mod_callcenter
Adding a JSON API interface to fetch data in the JSON format. It abides with the JSON command and its implementations by other modules like mod_verto.
It outputs the exactly same column names as the data in CLI.
Various listing of mod_callcenter data such as agents, queues, members, tiers are as follows below.
- Listing of agents
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"agent list"}}`
- Listing of queues
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"queue list"}}`
- Listing of agents for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents", "queue_name":"support@default"}}`
(`queue_name` is a required)
- Listing of members(callers) for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list members", "queue_name":"support@default"}}`
- Listing of tiers for a queue
Command: `json {"command": "callcenter_config","data": {"arguments":"queue list tiers", "queue_name":"support@default"}}`
- Listing of members(callers)
Command: `json {"command": "callcenter_config","data": {"arguments":"member list"}}`
- Listing of tiers
Command: `json {"command": "callcenter_config","data": {"arguments":"tier list"}}`
It handles various errors and also returns back response in JSON as follow
E.g. Send a command without a required field (`queue_name`)
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents"}}`
Output: `{"command":"callcenter_config","data":{"arguments":"queue list agents"},"status":"error","message":"The command returned an error","response":{"error":"Missing data attribute: queue_name"}}`
2016-01-28 08:38:00 +00:00
}
/* if nothing was executed from above, it should return error */
2017-07-18 16:25:22 +00:00
return SWITCH_STATUS_FALSE ;
2016-11-21 22:32:22 +00:00
FS-8799 [mod_callcenter] Add JSON API interface for mod_callcenter
Adding a JSON API interface to fetch data in the JSON format. It abides with the JSON command and its implementations by other modules like mod_verto.
It outputs the exactly same column names as the data in CLI.
Various listing of mod_callcenter data such as agents, queues, members, tiers are as follows below.
- Listing of agents
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"agent list"}}`
- Listing of queues
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"queue list"}}`
- Listing of agents for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents", "queue_name":"support@default"}}`
(`queue_name` is a required)
- Listing of members(callers) for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list members", "queue_name":"support@default"}}`
- Listing of tiers for a queue
Command: `json {"command": "callcenter_config","data": {"arguments":"queue list tiers", "queue_name":"support@default"}}`
- Listing of members(callers)
Command: `json {"command": "callcenter_config","data": {"arguments":"member list"}}`
- Listing of tiers
Command: `json {"command": "callcenter_config","data": {"arguments":"tier list"}}`
It handles various errors and also returns back response in JSON as follow
E.g. Send a command without a required field (`queue_name`)
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents"}}`
Output: `{"command":"callcenter_config","data":{"arguments":"queue list agents"},"status":"error","message":"The command returned an error","response":{"error":"Missing data attribute: queue_name"}}`
2016-01-28 08:38:00 +00:00
}
2010-08-13 13:48:37 +00:00
/* Macro expands to: switch_status_t mod_callcenter_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool) */
SWITCH_MODULE_LOAD_FUNCTION ( mod_callcenter_load )
{
switch_api_interface_t * api_interface ;
FS-8799 [mod_callcenter] Add JSON API interface for mod_callcenter
Adding a JSON API interface to fetch data in the JSON format. It abides with the JSON command and its implementations by other modules like mod_verto.
It outputs the exactly same column names as the data in CLI.
Various listing of mod_callcenter data such as agents, queues, members, tiers are as follows below.
- Listing of agents
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"agent list"}}`
- Listing of queues
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"queue list"}}`
- Listing of agents for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents", "queue_name":"support@default"}}`
(`queue_name` is a required)
- Listing of members(callers) for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list members", "queue_name":"support@default"}}`
- Listing of tiers for a queue
Command: `json {"command": "callcenter_config","data": {"arguments":"queue list tiers", "queue_name":"support@default"}}`
- Listing of members(callers)
Command: `json {"command": "callcenter_config","data": {"arguments":"member list"}}`
- Listing of tiers
Command: `json {"command": "callcenter_config","data": {"arguments":"tier list"}}`
It handles various errors and also returns back response in JSON as follow
E.g. Send a command without a required field (`queue_name`)
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents"}}`
Output: `{"command":"callcenter_config","data":{"arguments":"queue list agents"},"status":"error","message":"The command returned an error","response":{"error":"Missing data attribute: queue_name"}}`
2016-01-28 08:38:00 +00:00
switch_json_api_interface_t * json_api_interface ;
2010-09-21 16:43:48 +00:00
switch_status_t status ;
2010-08-13 13:48:37 +00:00
2015-09-21 23:00:14 +00:00
if ( switch_event_reserve_subclass ( CALLCENTER_EVENT ) ! = SWITCH_STATUS_SUCCESS ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Couldn't register subclass %s! \n " , CALLCENTER_EVENT ) ;
return SWITCH_STATUS_TERM ;
}
2010-08-13 13:48:37 +00:00
memset ( & globals , 0 , sizeof ( globals ) ) ;
globals . pool = pool ;
2017-07-18 16:25:22 +00:00
switch_core_hash_init ( & globals . queue_hash ) ;
2010-08-13 13:48:37 +00:00
switch_mutex_init ( & globals . mutex , SWITCH_MUTEX_NESTED , globals . pool ) ;
2010-09-21 16:43:48 +00:00
if ( ( status = load_config ( ) ) ! = SWITCH_STATUS_SUCCESS ) {
return status ;
}
2017-07-18 16:25:22 +00:00
2016-10-05 02:07:42 +00:00
if ( switch_event_bind_removable ( modname , SWITCH_EVENT_PRESENCE_PROBE , SWITCH_EVENT_SUBCLASS_ANY ,
2017-07-18 16:25:22 +00:00
cc_presence_event_handler , NULL , & globals . node ) ! = SWITCH_STATUS_SUCCESS ) {
2016-10-05 02:07:42 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Failed to subscribe for presence events! \n " ) ;
return SWITCH_STATUS_GENERR ;
}
2017-07-18 16:25:22 +00:00
switch_mutex_lock ( globals . mutex ) ;
globals . running = 1 ;
switch_mutex_unlock ( globals . mutex ) ;
2010-08-13 13:48:37 +00:00
2010-09-21 16:43:48 +00:00
/* connect my internal structure to the blank pointer passed to me */
* module_interface = switch_loadable_module_create_module_interface ( pool , modname ) ;
2010-08-13 13:48:37 +00:00
2017-07-18 16:25:22 +00:00
if ( ! AGENT_DISPATCH_THREAD_STARTED ) {
cc_agent_dispatch_thread_start ( ) ;
}
2010-08-13 13:48:37 +00:00
SWITCH_ADD_APP ( app_interface , " callcenter " , " CallCenter " , CC_DESC , callcenter_function , CC_USAGE , SAF_NONE ) ;
2017-07-18 16:09:39 +00:00
SWITCH_ADD_APP ( app_interface , " callcenter_track " , " CallCenter Track Call " , " Track external mod_callcenter calls to avoid place new calls " , callcenter_track , CC_USAGE , SAF_NONE ) ;
2010-08-13 13:48:37 +00:00
SWITCH_ADD_API ( api_interface , " callcenter_config " , " Config of callcenter " , cc_config_api_function , CC_CONFIG_API_SYNTAX ) ;
2017-09-13 20:39:43 +00:00
SWITCH_ADD_API ( api_interface , " callcenter_break " , " Stop watching an uuid and release agent " , cc_break_api_function , " callcenter_break agent <uuid> " ) ;
2010-08-13 13:48:37 +00:00
FS-8799 [mod_callcenter] Add JSON API interface for mod_callcenter
Adding a JSON API interface to fetch data in the JSON format. It abides with the JSON command and its implementations by other modules like mod_verto.
It outputs the exactly same column names as the data in CLI.
Various listing of mod_callcenter data such as agents, queues, members, tiers are as follows below.
- Listing of agents
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"agent list"}}`
- Listing of queues
Command: `json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"queue list"}}`
- Listing of agents for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents", "queue_name":"support@default"}}`
(`queue_name` is a required)
- Listing of members(callers) for a queue
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list members", "queue_name":"support@default"}}`
- Listing of tiers for a queue
Command: `json {"command": "callcenter_config","data": {"arguments":"queue list tiers", "queue_name":"support@default"}}`
- Listing of members(callers)
Command: `json {"command": "callcenter_config","data": {"arguments":"member list"}}`
- Listing of tiers
Command: `json {"command": "callcenter_config","data": {"arguments":"tier list"}}`
It handles various errors and also returns back response in JSON as follow
E.g. Send a command without a required field (`queue_name`)
Command: `json {"command": "callcenter_config", "data": {"arguments":"queue list agents"}}`
Output: `{"command":"callcenter_config","data":{"arguments":"queue list agents"},"status":"error","message":"The command returned an error","response":{"error":"Missing data attribute: queue_name"}}`
2016-01-28 08:38:00 +00:00
SWITCH_ADD_JSON_API ( json_api_interface , " callcenter_config " , " JSON Callcenter API " , json_callcenter_config_function , " " ) ;
2010-08-13 13:48:37 +00:00
switch_console_set_complete ( " add callcenter_config agent add " ) ;
switch_console_set_complete ( " add callcenter_config agent del " ) ;
2013-11-28 06:28:00 +00:00
switch_console_set_complete ( " add callcenter_config agent reload " ) ;
2010-08-13 13:48:37 +00:00
switch_console_set_complete ( " add callcenter_config agent set status " ) ;
switch_console_set_complete ( " add callcenter_config agent set state " ) ;
2010-08-26 01:40:33 +00:00
switch_console_set_complete ( " add callcenter_config agent set uuid " ) ;
2010-08-13 13:48:37 +00:00
switch_console_set_complete ( " add callcenter_config agent set contact " ) ;
2010-08-20 01:43:07 +00:00
switch_console_set_complete ( " add callcenter_config agent set ready_time " ) ;
switch_console_set_complete ( " add callcenter_config agent set reject_delay_time " ) ;
switch_console_set_complete ( " add callcenter_config agent set busy_delay_time " ) ;
2011-03-12 16:58:35 +00:00
switch_console_set_complete ( " add callcenter_config agent set no_answer_delay_time " ) ;
2010-08-13 13:48:37 +00:00
switch_console_set_complete ( " add callcenter_config agent get status " ) ;
switch_console_set_complete ( " add callcenter_config agent list " ) ;
switch_console_set_complete ( " add callcenter_config tier add " ) ;
switch_console_set_complete ( " add callcenter_config tier del " ) ;
2013-11-28 06:28:00 +00:00
switch_console_set_complete ( " add callcenter_config tier reload " ) ;
2010-08-13 13:48:37 +00:00
switch_console_set_complete ( " add callcenter_config tier set state " ) ;
switch_console_set_complete ( " add callcenter_config tier set level " ) ;
switch_console_set_complete ( " add callcenter_config tier set position " ) ;
switch_console_set_complete ( " add callcenter_config tier list " ) ;
2010-08-14 08:07:38 +00:00
switch_console_set_complete ( " add callcenter_config queue load " ) ;
switch_console_set_complete ( " add callcenter_config queue unload " ) ;
switch_console_set_complete ( " add callcenter_config queue reload " ) ;
2010-08-13 13:48:37 +00:00
switch_console_set_complete ( " add callcenter_config queue list " ) ;
2011-03-12 16:03:03 +00:00
switch_console_set_complete ( " add callcenter_config queue list agents " ) ;
switch_console_set_complete ( " add callcenter_config queue list members " ) ;
switch_console_set_complete ( " add callcenter_config queue list tiers " ) ;
2010-08-13 13:48:37 +00:00
switch_console_set_complete ( " add callcenter_config queue count " ) ;
2011-03-12 16:03:03 +00:00
switch_console_set_complete ( " add callcenter_config queue count agents " ) ;
switch_console_set_complete ( " add callcenter_config queue count members " ) ;
switch_console_set_complete ( " add callcenter_config queue count tiers " ) ;
2010-08-13 13:48:37 +00:00
2017-09-13 20:39:43 +00:00
switch_console_set_complete ( " add callcenter_break agent " ) ;
2010-08-13 13:48:37 +00:00
/* indicate that the module should continue to be loaded */
return SWITCH_STATUS_SUCCESS ;
}
/*
Called when the system shuts down
Macro expands to : switch_status_t mod_callcenter_shutdown ( ) */
SWITCH_MODULE_SHUTDOWN_FUNCTION ( mod_callcenter_shutdown )
{
2014-04-01 22:21:29 +00:00
switch_hash_index_t * hi = NULL ;
2017-07-18 16:25:22 +00:00
cc_queue_t * queue ;
2010-08-13 13:48:37 +00:00
void * val = NULL ;
const void * key ;
switch_ssize_t keylen ;
int sanity = 0 ;
2015-09-21 23:00:14 +00:00
switch_event_free_subclass ( CALLCENTER_EVENT ) ;
2016-10-05 02:07:42 +00:00
switch_event_unbind ( & globals . node ) ;
2010-08-13 13:48:37 +00:00
switch_mutex_lock ( globals . mutex ) ;
if ( globals . running = = 1 ) {
globals . running = 0 ;
}
2017-07-18 16:25:22 +00:00
switch_mutex_unlock ( globals . mutex ) ;
2010-08-13 13:48:37 +00:00
2017-07-18 16:25:22 +00:00
while ( globals . threads ) {
switch_cond_next ( ) ;
if ( + + sanity > = 60000 ) {
break ;
}
}
2010-08-13 13:48:37 +00:00
2017-07-18 16:25:22 +00:00
switch_mutex_lock ( globals . mutex ) ;
while ( ( hi = switch_core_hash_first_iter ( globals . queue_hash , hi ) ) ) {
2014-03-08 19:37:09 +00:00
switch_core_hash_this ( hi , & key , & keylen , & val ) ;
2017-07-18 16:25:22 +00:00
queue = ( cc_queue_t * ) val ;
switch_core_hash_delete ( globals . queue_hash , queue - > name ) ;
2010-08-13 13:48:37 +00:00
2017-07-18 16:25:22 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Waiting for write lock (queue %s) \n " , queue - > name ) ;
switch_thread_rwlock_wrlock ( queue - > rwlock ) ;
2010-08-13 13:48:37 +00:00
2017-07-18 16:25:22 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Destroying queue %s \n " , queue - > name ) ;
2010-08-13 13:48:37 +00:00
2017-07-18 16:25:22 +00:00
switch_core_destroy_memory_pool ( & queue - > pool ) ;
queue = NULL ;
2010-08-13 13:48:37 +00:00
}
2010-08-14 16:22:11 +00:00
2019-07-24 16:26:32 +00:00
switch_core_hash_destroy ( & globals . queue_hash ) ;
2017-07-18 16:25:22 +00:00
switch_safe_free ( globals . odbc_dsn ) ;
switch_safe_free ( globals . dbname ) ;
2017-07-12 16:01:06 +00:00
switch_safe_free ( globals . cc_instance_id ) ;
2010-08-13 13:48:37 +00:00
switch_mutex_unlock ( globals . mutex ) ;
return SWITCH_STATUS_SUCCESS ;
}
/* For Emacs:
* Local Variables :
* mode : c
* indent - tabs - mode : t
* tab - width : 4
* c - basic - offset : 4
* End :
* For VIM :
2013-06-25 16:50:17 +00:00
* vim : set softtabstop = 4 shiftwidth = 4 tabstop = 4 noet
2010-08-13 13:48:37 +00:00
*/