mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-25 23:16:04 +00:00
app_agent_pool: Return to dialplan if the agent fails to ack the call.
Improvements to the agent pool functionality. * AgentRequest no longer hangs up the caller if the agent fails to connect with the caller. It now continues in the dialplan. * AgentRequest returns AGENT_STATUS set to NOT_CONNECTED if the agent failed to connect with the call. Most likely because the agent did not acknowledge the call in time or got disconnected. * The agent alerting play file configured by the agent.conf custom_beep option can now be disabled by setting the option to an empty string. The agent is effectively alerted to a call presence when MOH stops. * Fixed bridge reference leak when the agent connects with a caller. ASTERISK-23499 #close Reported by: Matt Jordan Review: https://reviewboard.asterisk.org/r/3551/ git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/12@414747 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
11
CHANGES
11
CHANGES
@@ -8,6 +8,17 @@
|
|||||||
===
|
===
|
||||||
==============================================================================
|
==============================================================================
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
--- Functionality changes from Asterisk 12.3.0 to Asterisk 12.4.0 ------------
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
AgentRequest
|
||||||
|
------------------
|
||||||
|
* Returns new AGENT_STATUS value "NOT_CONNECTED" if the agent fails to
|
||||||
|
connect with an incoming caller after being alerted to the presence
|
||||||
|
of the incoming caller. The most likely reason this would happen is
|
||||||
|
the agent did not acknowledge the call in time.
|
||||||
|
|
||||||
------------------------------------------------------------------------------
|
------------------------------------------------------------------------------
|
||||||
--- Functionality changes from Asterisk 12.2.0 to Asterisk 12.3.0 ------------
|
--- Functionality changes from Asterisk 12.2.0 to Asterisk 12.3.0 ------------
|
||||||
------------------------------------------------------------------------------
|
------------------------------------------------------------------------------
|
||||||
|
@@ -108,13 +108,16 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||||||
<parameter name="AgentId" required="true" />
|
<parameter name="AgentId" required="true" />
|
||||||
</syntax>
|
</syntax>
|
||||||
<description>
|
<description>
|
||||||
<para>Request an agent to connect with the channel. Failure to find and
|
<para>Request an agent to connect with the channel. Failure to find,
|
||||||
alert an agent will continue in the dialplan with <variable>AGENT_STATUS</variable> set.</para>
|
alert the agent, or acknowledge the call will continue in the dialplan
|
||||||
|
with <variable>AGENT_STATUS</variable> set.</para>
|
||||||
<para><variable>AGENT_STATUS</variable> enumeration values:</para>
|
<para><variable>AGENT_STATUS</variable> enumeration values:</para>
|
||||||
<enumlist>
|
<enumlist>
|
||||||
<enum name = "INVALID"><para>The specified agent is invalid.</para></enum>
|
<enum name = "INVALID"><para>The specified agent is invalid.</para></enum>
|
||||||
<enum name = "NOT_LOGGED_IN"><para>The agent is not available.</para></enum>
|
<enum name = "NOT_LOGGED_IN"><para>The agent is not available.</para></enum>
|
||||||
<enum name = "BUSY"><para>The agent is on another call.</para></enum>
|
<enum name = "BUSY"><para>The agent is on another call.</para></enum>
|
||||||
|
<enum name = "NOT_CONNECTED"><para>The agent did not connect with the
|
||||||
|
call. The agent most likely did not acknowledge the call.</para></enum>
|
||||||
<enum name = "ERROR"><para>Alerting the agent failed.</para></enum>
|
<enum name = "ERROR"><para>Alerting the agent failed.</para></enum>
|
||||||
</enumlist>
|
</enumlist>
|
||||||
</description>
|
</description>
|
||||||
@@ -545,7 +548,7 @@ static int load_config(void)
|
|||||||
aco_option_register(&cfg_info, "wrapuptime", ACO_EXACT, agent_types, "0", OPT_UINT_T, 0, FLDSET(struct agent_cfg, wrapup_time));
|
aco_option_register(&cfg_info, "wrapuptime", ACO_EXACT, agent_types, "0", OPT_UINT_T, 0, FLDSET(struct agent_cfg, wrapup_time));
|
||||||
aco_option_register(&cfg_info, "musiconhold", ACO_EXACT, agent_types, "default", OPT_STRINGFIELD_T, 0, STRFLDSET(struct agent_cfg, moh));
|
aco_option_register(&cfg_info, "musiconhold", ACO_EXACT, agent_types, "default", OPT_STRINGFIELD_T, 0, STRFLDSET(struct agent_cfg, moh));
|
||||||
aco_option_register(&cfg_info, "recordagentcalls", ACO_EXACT, agent_types, "no", OPT_BOOL_T, 1, FLDSET(struct agent_cfg, record_agent_calls));
|
aco_option_register(&cfg_info, "recordagentcalls", ACO_EXACT, agent_types, "no", OPT_BOOL_T, 1, FLDSET(struct agent_cfg, record_agent_calls));
|
||||||
aco_option_register(&cfg_info, "custom_beep", ACO_EXACT, agent_types, "beep", OPT_STRINGFIELD_T, 1, STRFLDSET(struct agent_cfg, beep_sound));
|
aco_option_register(&cfg_info, "custom_beep", ACO_EXACT, agent_types, "beep", OPT_STRINGFIELD_T, 0, STRFLDSET(struct agent_cfg, beep_sound));
|
||||||
aco_option_register(&cfg_info, "fullname", ACO_EXACT, agent_types, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct agent_cfg, full_name));
|
aco_option_register(&cfg_info, "fullname", ACO_EXACT, agent_types, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct agent_cfg, full_name));
|
||||||
|
|
||||||
if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
|
if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
|
||||||
@@ -761,7 +764,7 @@ static void agent_pvt_destructor(void *vdoomed)
|
|||||||
|
|
||||||
ast_party_connected_line_free(&doomed->waiting_colp);
|
ast_party_connected_line_free(&doomed->waiting_colp);
|
||||||
if (doomed->caller_bridge) {
|
if (doomed->caller_bridge) {
|
||||||
ast_bridge_destroy(doomed->caller_bridge, AST_CAUSE_USER_BUSY);
|
ast_bridge_destroy(doomed->caller_bridge, 0);
|
||||||
doomed->caller_bridge = NULL;
|
doomed->caller_bridge = NULL;
|
||||||
}
|
}
|
||||||
if (doomed->logged) {
|
if (doomed->logged) {
|
||||||
@@ -993,6 +996,23 @@ static AO2_GLOBAL_OBJ_STATIC(agent_holding);
|
|||||||
/*! Agent holding bridge deferred creation lock. */
|
/*! Agent holding bridge deferred creation lock. */
|
||||||
AST_MUTEX_DEFINE_STATIC(agent_holding_lock);
|
AST_MUTEX_DEFINE_STATIC(agent_holding_lock);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \internal
|
||||||
|
* \brief Callback to clear AGENT_STATUS on the caller channel.
|
||||||
|
*
|
||||||
|
* \param bridge_channel Which channel to operate on.
|
||||||
|
* \param payload Data to pass to the callback. (NULL if none).
|
||||||
|
* \param payload_size Size of the payload if payload is non-NULL. A number otherwise.
|
||||||
|
*
|
||||||
|
* \note The payload MUST NOT have any resources that need to be freed.
|
||||||
|
*
|
||||||
|
* \return Nothing
|
||||||
|
*/
|
||||||
|
static void clear_agent_status(struct ast_bridge_channel *bridge_channel, const void *payload, size_t payload_size)
|
||||||
|
{
|
||||||
|
pbx_builtin_setvar_helper(bridge_channel->chan, "AGENT_STATUS", NULL);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \internal
|
* \internal
|
||||||
* \brief Connect the agent with the waiting caller.
|
* \brief Connect the agent with the waiting caller.
|
||||||
@@ -1028,12 +1048,18 @@ static void agent_connect_caller(struct ast_bridge_channel *bridge_channel, stru
|
|||||||
NULL, 0);
|
NULL, 0);
|
||||||
if (res) {
|
if (res) {
|
||||||
/* Reset agent. */
|
/* Reset agent. */
|
||||||
ast_bridge_destroy(caller_bridge, AST_CAUSE_USER_BUSY);
|
ast_bridge_destroy(caller_bridge, 0);
|
||||||
ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
|
ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
|
||||||
AST_CAUSE_NORMAL_CLEARING);
|
AST_CAUSE_NORMAL_CLEARING);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_ANSWER, NULL, 0);
|
res = ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_ANSWER, NULL, 0)
|
||||||
|
|| ast_bridge_channel_write_callback(bridge_channel, 0, clear_agent_status, NULL, 0);
|
||||||
|
if (res) {
|
||||||
|
/* Reset agent. */
|
||||||
|
ast_bridge_destroy(caller_bridge, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (record_agent_calls) {
|
if (record_agent_calls) {
|
||||||
struct ast_bridge_features_automixmonitor options = {
|
struct ast_bridge_features_automixmonitor options = {
|
||||||
@@ -1046,6 +1072,8 @@ static void agent_connect_caller(struct ast_bridge_channel *bridge_channel, stru
|
|||||||
*/
|
*/
|
||||||
ast_bridge_features_do(AST_BRIDGE_BUILTIN_AUTOMIXMON, bridge_channel, &options);
|
ast_bridge_features_do(AST_BRIDGE_BUILTIN_AUTOMIXMON, bridge_channel, &options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ao2_t_ref(caller_bridge, -1, "Agent successfully in caller_bridge");
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bridge_agent_hold_ack(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
|
static int bridge_agent_hold_ack(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
|
||||||
@@ -1462,7 +1490,7 @@ static void agent_logout(struct agent_pvt *agent)
|
|||||||
agent_devstate_changed(agent->username);
|
agent_devstate_changed(agent->username);
|
||||||
|
|
||||||
if (caller_bridge) {
|
if (caller_bridge) {
|
||||||
ast_bridge_destroy(caller_bridge, AST_CAUSE_USER_BUSY);
|
ast_bridge_destroy(caller_bridge, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
ast_channel_lock(logged);
|
ast_channel_lock(logged);
|
||||||
@@ -1548,7 +1576,7 @@ static void agent_run(struct agent_pvt *agent, struct ast_channel *logged)
|
|||||||
agent_unlock(agent);
|
agent_unlock(agent);
|
||||||
ao2_ref(cfg_old, -1);
|
ao2_ref(cfg_old, -1);
|
||||||
if (caller_bridge) {
|
if (caller_bridge) {
|
||||||
ast_bridge_destroy(caller_bridge, AST_CAUSE_USER_BUSY);
|
ast_bridge_destroy(caller_bridge, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (agent->state == AGENT_STATE_LOGGING_OUT
|
if (agent->state == AGENT_STATE_LOGGING_OUT
|
||||||
@@ -1674,7 +1702,7 @@ static void caller_abort_agent(struct agent_pvt *agent)
|
|||||||
agent->caller_bridge = NULL;
|
agent->caller_bridge = NULL;
|
||||||
agent_unlock(agent);
|
agent_unlock(agent);
|
||||||
if (caller_bridge) {
|
if (caller_bridge) {
|
||||||
ast_bridge_destroy(caller_bridge, AST_CAUSE_USER_BUSY);
|
ast_bridge_destroy(caller_bridge, 0);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1690,9 +1718,11 @@ static int caller_safety_timeout(struct ast_bridge_channel *bridge_channel, void
|
|||||||
struct agent_pvt *agent = hook_pvt;
|
struct agent_pvt *agent = hook_pvt;
|
||||||
|
|
||||||
if (agent->state == AGENT_STATE_CALL_PRESENT) {
|
if (agent->state == AGENT_STATE_CALL_PRESENT) {
|
||||||
ast_verb(3, "Agent '%s' did not respond. Safety timeout.\n", agent->username);
|
ast_log(LOG_WARNING, "Agent '%s' process did not respond. Safety timeout.\n",
|
||||||
ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
|
agent->username);
|
||||||
AST_CAUSE_USER_BUSY);
|
pbx_builtin_setvar_helper(bridge_channel->chan, "AGENT_STATUS", "ERROR");
|
||||||
|
|
||||||
|
ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, 0);
|
||||||
caller_abort_agent(agent);
|
caller_abort_agent(agent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1769,6 +1799,49 @@ static int send_colp_to_agent(struct ast_bridge_channel *bridge_channel, struct
|
|||||||
AST_CONTROL_CONNECTED_LINE, data, datalen);
|
AST_CONTROL_CONNECTED_LINE, data, datalen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \internal
|
||||||
|
* \brief Caller joined the bridge event callback.
|
||||||
|
*
|
||||||
|
* \param bridge_channel Channel executing the feature
|
||||||
|
* \param hook_pvt Private data passed in when the hook was created
|
||||||
|
*
|
||||||
|
* \retval 0 Keep the callback hook.
|
||||||
|
* \retval -1 Remove the callback hook.
|
||||||
|
*/
|
||||||
|
static int caller_joined_bridge(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
|
||||||
|
{
|
||||||
|
struct agent_pvt *agent = hook_pvt;
|
||||||
|
struct ast_bridge_channel *logged;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
logged = agent_bridge_channel_get_lock(agent);
|
||||||
|
if (!logged) {
|
||||||
|
ast_verb(3, "Agent '%s' not logged in.\n", agent->username);
|
||||||
|
pbx_builtin_setvar_helper(bridge_channel->chan, "AGENT_STATUS", "NOT_LOGGED_IN");
|
||||||
|
|
||||||
|
ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, 0);
|
||||||
|
caller_abort_agent(agent);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = send_alert_to_agent(logged, agent->username);
|
||||||
|
ast_bridge_channel_unlock(logged);
|
||||||
|
ao2_ref(logged, -1);
|
||||||
|
if (res) {
|
||||||
|
ast_verb(3, "Agent '%s': Failed to alert the agent.\n", agent->username);
|
||||||
|
pbx_builtin_setvar_helper(bridge_channel->chan, "AGENT_STATUS", "ERROR");
|
||||||
|
|
||||||
|
ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, 0);
|
||||||
|
caller_abort_agent(agent);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pbx_builtin_setvar_helper(bridge_channel->chan, "AGENT_STATUS", "NOT_CONNECTED");
|
||||||
|
ast_indicate(bridge_channel->chan, AST_CONTROL_RINGING);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Dialplan AgentRequest application to locate an agent to talk with.
|
* \brief Dialplan AgentRequest application to locate an agent to talk with.
|
||||||
*
|
*
|
||||||
@@ -1826,24 +1899,26 @@ static int agent_request_exec(struct ast_channel *chan, const char *data)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Setup the alert agent on caller joining the bridge hook. */
|
||||||
|
ao2_ref(agent, +1);
|
||||||
|
if (ast_bridge_join_hook(&caller_features, caller_joined_bridge, agent,
|
||||||
|
__ao2_cleanup, 0)) {
|
||||||
|
ao2_ref(agent, -1);
|
||||||
|
ast_bridge_features_cleanup(&caller_features);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
caller_bridge = ast_bridge_basic_new();
|
caller_bridge = ast_bridge_basic_new();
|
||||||
if (!caller_bridge) {
|
if (!caller_bridge) {
|
||||||
ast_bridge_features_cleanup(&caller_features);
|
ast_bridge_features_cleanup(&caller_features);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get COLP for agent. */
|
|
||||||
ast_party_connected_line_init(&connected);
|
|
||||||
ast_channel_lock(chan);
|
|
||||||
ast_connected_line_copy_from_caller(&connected, ast_channel_caller(chan));
|
|
||||||
ast_channel_unlock(chan);
|
|
||||||
|
|
||||||
agent_lock(agent);
|
agent_lock(agent);
|
||||||
switch (agent->state) {
|
switch (agent->state) {
|
||||||
case AGENT_STATE_LOGGED_OUT:
|
case AGENT_STATE_LOGGED_OUT:
|
||||||
case AGENT_STATE_LOGGING_OUT:
|
case AGENT_STATE_LOGGING_OUT:
|
||||||
agent_unlock(agent);
|
agent_unlock(agent);
|
||||||
ast_party_connected_line_free(&connected);
|
|
||||||
ast_bridge_destroy(caller_bridge, 0);
|
ast_bridge_destroy(caller_bridge, 0);
|
||||||
ast_bridge_features_cleanup(&caller_features);
|
ast_bridge_features_cleanup(&caller_features);
|
||||||
ast_verb(3, "Agent '%s' not logged in.\n", agent->username);
|
ast_verb(3, "Agent '%s' not logged in.\n", agent->username);
|
||||||
@@ -1857,7 +1932,6 @@ static int agent_request_exec(struct ast_channel *chan, const char *data)
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
agent_unlock(agent);
|
agent_unlock(agent);
|
||||||
ast_party_connected_line_free(&connected);
|
|
||||||
ast_bridge_destroy(caller_bridge, 0);
|
ast_bridge_destroy(caller_bridge, 0);
|
||||||
ast_bridge_features_cleanup(&caller_features);
|
ast_bridge_features_cleanup(&caller_features);
|
||||||
ast_verb(3, "Agent '%s' is busy.\n", agent->username);
|
ast_verb(3, "Agent '%s' is busy.\n", agent->username);
|
||||||
@@ -1867,38 +1941,55 @@ static int agent_request_exec(struct ast_channel *chan, const char *data)
|
|||||||
agent_unlock(agent);
|
agent_unlock(agent);
|
||||||
agent_devstate_changed(agent->username);
|
agent_devstate_changed(agent->username);
|
||||||
|
|
||||||
|
/* Get COLP for agent. */
|
||||||
|
ast_party_connected_line_init(&connected);
|
||||||
|
ast_channel_lock(chan);
|
||||||
|
ast_connected_line_copy_from_caller(&connected, ast_channel_caller(chan));
|
||||||
|
ast_channel_unlock(chan);
|
||||||
|
|
||||||
logged = agent_bridge_channel_get_lock(agent);
|
logged = agent_bridge_channel_get_lock(agent);
|
||||||
if (!logged) {
|
if (!logged) {
|
||||||
ast_party_connected_line_free(&connected);
|
ast_party_connected_line_free(&connected);
|
||||||
|
caller_abort_agent(agent);
|
||||||
ast_bridge_destroy(caller_bridge, 0);
|
ast_bridge_destroy(caller_bridge, 0);
|
||||||
ast_bridge_features_cleanup(&caller_features);
|
ast_bridge_features_cleanup(&caller_features);
|
||||||
ast_verb(3, "Agent '%s' not logged in.\n", agent->username);
|
ast_verb(3, "Agent '%s' not logged in.\n", agent->username);
|
||||||
pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "NOT_LOGGED_IN");
|
pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "NOT_LOGGED_IN");
|
||||||
caller_abort_agent(agent);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
send_colp_to_agent(logged, &connected);
|
send_colp_to_agent(logged, &connected);
|
||||||
ast_party_connected_line_free(&connected);
|
|
||||||
|
|
||||||
res = send_alert_to_agent(logged, agent->username);
|
|
||||||
ast_bridge_channel_unlock(logged);
|
ast_bridge_channel_unlock(logged);
|
||||||
ao2_ref(logged, -1);
|
ao2_ref(logged, -1);
|
||||||
if (res) {
|
ast_party_connected_line_free(&connected);
|
||||||
ast_bridge_destroy(caller_bridge, 0);
|
|
||||||
ast_bridge_features_cleanup(&caller_features);
|
|
||||||
ast_verb(3, "Agent '%s': Failed to alert the agent.\n", agent->username);
|
|
||||||
pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "ERROR");
|
|
||||||
caller_abort_agent(agent);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ast_indicate(chan, AST_CONTROL_RINGING);
|
if (ast_bridge_join(caller_bridge, chan, NULL, &caller_features, NULL,
|
||||||
ast_bridge_join(caller_bridge, chan, NULL, &caller_features, NULL,
|
AST_BRIDGE_JOIN_PASS_REFERENCE)) {
|
||||||
AST_BRIDGE_JOIN_PASS_REFERENCE);
|
caller_abort_agent(agent);
|
||||||
|
ast_verb(3, "Agent '%s': Caller %s failed to join the bridge.\n",
|
||||||
|
agent->username, ast_channel_name(chan));
|
||||||
|
pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "ERROR");
|
||||||
|
}
|
||||||
ast_bridge_features_cleanup(&caller_features);
|
ast_bridge_features_cleanup(&caller_features);
|
||||||
|
|
||||||
return -1;
|
/* Determine if we need to continue in the dialplan after the bridge. */
|
||||||
|
ast_channel_lock(chan);
|
||||||
|
if (ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO) {
|
||||||
|
/*
|
||||||
|
* The bridge was broken for a hangup that isn't real.
|
||||||
|
* Don't run the h extension, because the channel isn't
|
||||||
|
* really hung up. This should really only happen with
|
||||||
|
* AST_SOFTHANGUP_ASYNCGOTO.
|
||||||
|
*/
|
||||||
|
res = 0;
|
||||||
|
} else {
|
||||||
|
res = ast_check_hangup(chan)
|
||||||
|
|| ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE)
|
||||||
|
|| ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENT_STATUS"));
|
||||||
|
}
|
||||||
|
ast_channel_unlock(chan);
|
||||||
|
|
||||||
|
return res ? -1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -2040,9 +2131,8 @@ static int agent_login_exec(struct ast_channel *chan, const char *data)
|
|||||||
|
|
||||||
agent_login_channel_config(agent, chan);
|
agent_login_channel_config(agent, chan);
|
||||||
|
|
||||||
if (!ast_test_flag(&opts, OPT_SILENT)
|
if (!ast_test_flag(&opts, OPT_SILENT)) {
|
||||||
&& !ast_streamfile(chan, "agent-loginok", ast_channel_language(chan))) {
|
ast_stream_and_wait(chan, "agent-loginok", AST_DIGIT_NONE);
|
||||||
ast_waitstream(chan, "");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ast_verb(2, "Agent '%s' logged in (format %s/%s)\n", agent->username,
|
ast_verb(2, "Agent '%s' logged in (format %s/%s)\n", agent->username,
|
||||||
|
Reference in New Issue
Block a user