From 1ce87dfca17a53b81cb87a761b385ab48371782f Mon Sep 17 00:00:00 2001 From: Marc Olivier Chouinard Date: Tue, 18 Jul 2017 11:14:43 -0400 Subject: [PATCH] FS-10511: Set status as External call when using callcenter_track --- .../mod_callcenter/mod_callcenter.c | 99 +++++++++++++++---- 1 file changed, 78 insertions(+), 21 deletions(-) diff --git a/src/mod/applications/mod_callcenter/mod_callcenter.c b/src/mod/applications/mod_callcenter/mod_callcenter.c index 4e4fef89db..ffd08b9654 100644 --- a/src/mod/applications/mod_callcenter/mod_callcenter.c +++ b/src/mod/applications/mod_callcenter/mod_callcenter.c @@ -101,7 +101,8 @@ typedef enum { CC_AGENT_STATUS_LOGGED_OUT = 1, CC_AGENT_STATUS_AVAILABLE = 2, CC_AGENT_STATUS_AVAILABLE_ON_DEMAND = 3, - CC_AGENT_STATUS_ON_BREAK = 4 + CC_AGENT_STATUS_ON_BREAK = 4, + CC_AGENT_STATUS_ON_EXTERNAL_CALL = 5 } cc_agent_status_t; static struct cc_status_table AGENT_STATUS_CHART[] = { @@ -110,6 +111,7 @@ static struct cc_status_table AGENT_STATUS_CHART[] = { {"Available", CC_AGENT_STATUS_AVAILABLE}, {"Available (On Demand)", CC_AGENT_STATUS_AVAILABLE_ON_DEMAND}, {"On Break", CC_AGENT_STATUS_ON_BREAK}, + {"On External Call", CC_AGENT_STATUS_ON_EXTERNAL_CALL}, {NULL, 0} }; @@ -208,6 +210,7 @@ static char agents_sql[] = /*User Personal Status Available On Break + On External Call Logged Out */ " state VARCHAR(255),\n" @@ -225,12 +228,14 @@ static char agents_sql[] = " last_bridge_start INTEGER NOT NULL DEFAULT 0,\n" " last_bridge_end INTEGER NOT NULL DEFAULT 0,\n" " last_offered_call INTEGER NOT NULL DEFAULT 0,\n" +" last_waiting_state INTEGER NOT NULL DEFAULT 0,\n" " 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" " talk_time INTEGER NOT NULL DEFAULT 0,\n" " ready_time INTEGER NOT NULL DEFAULT 0,\n" -" external_calls_count INTEGER NOT NULL DEFAULT 0\n" +" external_calls_count INTEGER NOT NULL DEFAULT 0,\n" +" status_before_external_calls VARCHAR(255) DEFAULT ''\n" ");\n"; static char tiers_sql[] = @@ -892,6 +897,7 @@ static cc_profile_t *load_profile(const char *profile_name) { "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); + switch_cache_db_test_reactive(dbh, "select status_before_external_calls from agents", NULL, "alter table agents add status_before_external_calls VARCHAR(255);"); switch_cache_db_test_reactive(dbh, "select external_calls_count from agents", NULL, "alter table agents add external_calls_count integer not null default 0;"); switch_cache_db_test_reactive(dbh, "select count(queue) from tiers", "drop table tiers" , tiers_sql); switch_cache_db_test_reactive(dbh, "select count(last_waiting_state) FROM agents", NULL, "alter table agents add last_waiting_state integer not null default 0;"); @@ -903,8 +909,11 @@ static cc_profile_t *load_profile(const char *profile_name) { sql = switch_mprintf("update agents set state = 'Waiting', uuid = '' where system = 'single_box';" "update tiers set state = 'Ready' where agent IN (select name from agents where system = 'single_box');" "update members set state = '%q', session_uuid = '' where system = '%q';" - "update agents set external_calls_count = 0 where system = 'single_box';", - cc_member_state2str(CC_MEMBER_STATE_ABANDONED), globals.core_uuid); + "update agents set external_calls_count = 0 where system = 'single_box';" + "update agents set status_before_external_calls = '' where system = 'single_box';" + "update agents set status = '%q' where status = '%q' and system = 'single_box'", /* This is not perfect... but better than having a call stuck on external call after a crash */ + cc_member_state2str(CC_MEMBER_STATE_ABANDONED), globals.core_uuid, cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE), cc_agent_status2str(CC_AGENT_STATUS_ON_EXTERNAL_CALL)); + cc_execute_sql(profile, NULL, sql, NULL); switch_safe_free(sql); @@ -1648,7 +1657,6 @@ static switch_status_t load_agent(cc_profile_t *profile, const char *agent_name, } } - if (!(x_agents = switch_xml_child(x_profile, "agents"))) { goto end; } @@ -2511,6 +2519,9 @@ static int agents_callback(void *pArg, int argc, char **argv, char **columnNames if (! (strcasecmp(agent_status, cc_agent_status2str(CC_AGENT_STATUS_ON_BREAK)))) { contact_agent = SWITCH_FALSE; } + if (! (strcasecmp(agent_status, cc_agent_status2str(CC_AGENT_STATUS_ON_EXTERNAL_CALL)))) { + contact_agent = SWITCH_FALSE; + } /* 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; @@ -2689,7 +2700,7 @@ static int members_callback(void *pArg, int argc, char **argv, char **columnName if (!cbt.queue_name || !(queue = get_queue(profile, cbt.queue_name))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Queue %s not found locally, delete this member\n", cbt.queue_name); sql = switch_mprintf("DELETE FROM members WHERE uuid = '%q' AND system = '%q'", cbt.member_uuid, cbt.member_system); - cc_execute_sql(NULL, sql, NULL); + cc_execute_sql(profile, NULL, sql, NULL); switch_safe_free(sql); goto end; } else { @@ -2816,38 +2827,39 @@ static int members_callback(void *pArg, int argc, char **argv, char **columnName sql = switch_mprintf("SELECT system, 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)" " WHERE tiers.queue = '%q'" - " AND (agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q')" + " AND (agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q')" " AND tiers.position > %d" " AND tiers.level = %d" " UNION " "SELECT system, 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)" " WHERE tiers.queue = '%q'" - " AND (agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q')" + " AND (agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q')" " ORDER BY dyn_order asc, tiers_level, tiers_position, agents_last_offered_call", queue_name, - cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE), cc_agent_status2str(CC_AGENT_STATUS_ON_BREAK), cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE_ON_DEMAND), + cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE), cc_agent_status2str(CC_AGENT_STATUS_ON_BREAK), cc_agent_status2str(CC_AGENT_STATUS_ON_EXTERNAL_CALL), cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE_ON_DEMAND), position, level, queue_name, - cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE), cc_agent_status2str(CC_AGENT_STATUS_ON_BREAK), cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE_ON_DEMAND) + cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE), cc_agent_status2str(CC_AGENT_STATUS_ON_BREAK), cc_agent_status2str(CC_AGENT_STATUS_ON_EXTERNAL_CALL), cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE_ON_DEMAND), + level ); } else if (!strcasecmp(queue_strategy, "round-robin")) { sql = switch_mprintf("SELECT system, 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)" " WHERE tiers.queue = '%q'" - " AND (agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q')" + " AND (agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q')" " 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)" " UNION " "SELECT system, 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)" " WHERE tiers.queue = '%q'" - " AND (agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q')" + " AND (agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q')" " ORDER BY dyn_order asc, tiers_level, tiers_position, agents_last_offered_call", queue_name, - cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE), cc_agent_status2str(CC_AGENT_STATUS_ON_BREAK), cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE_ON_DEMAND), + cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE), cc_agent_status2str(CC_AGENT_STATUS_ON_BREAK), cc_agent_status2str(CC_AGENT_STATUS_ON_EXTERNAL_CALL), cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE_ON_DEMAND), queue_name, queue_name, queue_name, - cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE), cc_agent_status2str(CC_AGENT_STATUS_ON_BREAK), cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE_ON_DEMAND) + cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE), cc_agent_status2str(CC_AGENT_STATUS_ON_BREAK), cc_agent_status2str(CC_AGENT_STATUS_ON_EXTERNAL_CALL), cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE_ON_DEMAND) ); } else { @@ -2875,10 +2887,10 @@ static int members_callback(void *pArg, int argc, char **argv, char **columnName sql = switch_mprintf("SELECT system, 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)" " WHERE tiers.queue = '%q'" - " AND (agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q')" + " AND (agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q')" " ORDER BY %q", queue_name, - cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE), cc_agent_status2str(CC_AGENT_STATUS_ON_BREAK), cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE_ON_DEMAND), + cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE), cc_agent_status2str(CC_AGENT_STATUS_ON_BREAK), cc_agent_status2str(CC_AGENT_STATUS_ON_EXTERNAL_CALL), cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE_ON_DEMAND), sql_order_by); switch_safe_free(sql_order_by); @@ -3574,6 +3586,7 @@ static switch_status_t cc_hook_state_run(switch_core_session_t *session) const char *profile_name = NULL; char *sql = NULL; cc_profile_t *profile = NULL; + char agent_status[255]; profile_name = switch_channel_get_variable(channel, "cc_profile"); @@ -3581,7 +3594,7 @@ static switch_status_t cc_hook_state_run(switch_core_session_t *session) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Profile %s not found\n", profile_name); switch_core_event_hook_remove_state_run(session, cc_hook_state_run); UNPROTECT_INTERFACE(app_interface); - return SWITCH_STATUS_SUCCESS; + goto end; } @@ -3589,20 +3602,48 @@ static switch_status_t cc_hook_state_run(switch_core_session_t *session) 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 (cc_agent_get(profile, "status", agent_name, agent_status, sizeof(agent_status)) != CC_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Invalid agent %s", agent_name); + goto end; + } + + if (state == CS_HANGUP) { + char agent_status_changed[255]; + switch_event_t *event; + 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); cc_execute_sql(profile, NULL, sql, NULL); switch_safe_free(sql); + sql = switch_mprintf("UPDATE agents SET status = status_before_external_calls, status_before_external_calls = '' WHERE name = '%q' AND external_calls_count = 0 AND status = '%q'", agent_name, cc_agent_status2str(CC_AGENT_STATUS_ON_EXTERNAL_CALL)); + cc_execute_sql(profile, NULL, sql, NULL); + switch_safe_free(sql); + + if (cc_agent_get(profile, "status", agent_name, agent_status_changed, sizeof(agent_status_changed)) != CC_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Invalid agent %s", agent_name); + } else if (strcasecmp(agent_status, agent_status_changed) && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CALLCENTER_EVENT) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Profile", profile_name); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Agent", agent_name); + 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", agent_status_changed); + switch_event_fire(&event); + } + switch_core_event_hook_remove_state_run(session, cc_hook_state_run); UNPROTECT_INTERFACE(app_interface); } +end: + profile_rwunlock(profile); profile_rwunlock(profile); return SWITCH_STATUS_SUCCESS; } +#define CC_TRACK_DESC "Track external mod_callcenter calls to avoid place new calls" +#define CC_TRACK_USAGE "[profile/]queue_name" + SWITCH_STANDARD_APP(callcenter_track) { switch_channel_t *channel = switch_core_session_get_channel(session); @@ -3629,22 +3670,38 @@ SWITCH_STANDARD_APP(callcenter_track) if (!profile_name || !(profile = get_profile(profile_name))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Profile %s not found\n", profile_name); goto end; - } switch_channel_set_variable(channel, "cc_profile", profile_name); - if (cc_agent_get(profile, "status", data, agent_status, sizeof(agent_status)) != CC_STATUS_SUCCESS) { + if (cc_agent_get(profile, "status", agent_name, agent_status, sizeof(agent_status)) != CC_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Invalid agent %s", data); goto end; } switch_channel_set_variable(channel, "cc_tracked_agent", agent_name); - sql = switch_mprintf("UPDATE agents SET external_calls_count = external_calls_count + 1 WHERE name = '%q'", agent_name); cc_execute_sql(profile, NULL, sql, NULL); switch_safe_free(sql); + if (switch_true(switch_channel_get_variable(channel, "cc_set_external_call_status"))) { + switch_event_t *event; + char agent_status_changed[255]; + + sql = switch_mprintf("UPDATE agents SET status_before_external_calls = status, status = '%q' WHERE name = '%q' AND external_calls_count > 0 AND status != '%q'", cc_agent_status2str(CC_AGENT_STATUS_ON_EXTERNAL_CALL), agent_name, cc_agent_status2str(CC_AGENT_STATUS_ON_EXTERNAL_CALL)); + cc_execute_sql(profile, NULL, sql, NULL); + switch_safe_free(sql); + + if (cc_agent_get(profile, "status", agent_name, agent_status_changed, sizeof(agent_status_changed)) != CC_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Invalid agent %s", agent_name); + } else if (strcasecmp(agent_status, agent_status_changed) && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CALLCENTER_EVENT) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Profile", profile_name); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Agent", agent_name); + 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", agent_status_changed); + switch_event_fire(&event); + } + } 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); @@ -4647,7 +4704,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_callcenter_load) *module_interface = switch_loadable_module_create_module_interface(pool, modname); SWITCH_ADD_APP(app_interface, "callcenter", "CallCenter", CC_DESC, callcenter_function, CC_USAGE, SAF_NONE); - 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); + SWITCH_ADD_APP(app_interface, "callcenter_track", "CallCenter Track Call", CC_TRACK_DESC, callcenter_track, CC_TRACK_USAGE, SAF_NONE); SWITCH_ADD_API(api_interface, "callcenter_config", "Config of callcenter", cc_config_api_function, CC_CONFIG_API_SYNTAX); SWITCH_ADD_JSON_API(json_api_interface, "callcenter_config", "JSON Callcenter API", json_callcenter_config_function, "");