Replace chan_agent with app_agent_pool.

The ill conceived chan_agent is no more.  It is now replaced by
app_agent_pool.

Agents login using the AgentLogin() application as before.  The
AgentLogin() application no longer does any authentication.
Authentication is now the responsibility of the dialplan.  (Besides, the
authentication done by chan_agent did not match what the voice prompts
asked for.)

Sample extensions.conf
[login]
; Sample agent 1001 login
; Set COLP for in between calls so the agent does not see the last caller COLP.
exten => 1001,1,Set(CONNECTEDLINE(all)="Agent Waiting" <1001>)
; Give the agent DTMF transfer and disconnect features when connected to a caller.
same => n,Set(CHANNEL(dtmf-features)=TX)
same => n,AgentLogin(1001)
same => n,NoOp(AGENT_STATUS is ${AGENT_STATUS})
same => n,Hangup()

[caller]
; Sample caller direct connect to agent 1001
exten => 800,1,AgentRequest(1001)
same => n,NoOp(AGENT_STATUS is ${AGENT_STATUS})
same => n,Hangup()

; Sample caller going through a Queue to agent 1001
exten => 900,1,Queue(agent_q)
same => n,Hangup()

Sample queues.conf
[agent_q]
member => Local/800@caller,,SuperAgent,Agent:1001

Under the hood operation overview:
1) Logged in agents wait for callers in an agents holding bridge.
2) Caller requests an agent using AgentRequest()
3) A basic bridge is created, the agent is notified, and caller joins the
   basic bridge to wait for the agent.
4) The agent is either automatically connected to the caller or must ack
   the call to connect.
5) The agent is moved from the agents holding bridge to the basic bridge.
6) The agent and caller talk.
7) The connection is ended by either party.
8) The agent goes back to the agents holding bridge.

To avoid some locking issues with the agent holding bridge, I needed to
make some changes to the after bridge callback support.  The after bridge
callback is now a list of requested callbacks with the last to be added
the only active callback.  The after bridge callback for failed callbacks
will always happen in the channel thread when the channel leaves the
bridging system or is destroyed.

(closes issue ASTERISK-21554)
Reported by: Matt Jordan

Review: https://reviewboard.asterisk.org/r/2657/


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@394417 65c4cc65-6c06-0410-ace0-fbb531ad65f3
changes/78/78/1
Richard Mudgett 12 years ago
parent d43bbfb74e
commit d43b17a872

@ -14,10 +14,17 @@
Applications
------------------
AgentLogin
------------------
* The application no longer does agent authentication. The dialplan needs to
perform this function before running AgentLogin. If the agent is already
logged in, dialplan will continue with the AGENT_STATUS channel variable
set to ALREADY_LOGGED_IN.
AgentMonitorOutgoing
------------------
* The 'c' option has been removed. It is not possible to modify the name of a
channel involved in a CDR.
* Application removed. It was a holdover from when AgentCallbackLogin was
removed.
ForkCDR
------------------
@ -260,8 +267,8 @@ AMI (Asterisk Manager Interface)
of "CallerID" and "ConnectedID" to avoid confusion with similarly named
parameters in the channel snapshot.
* The "Agentlogin" and "Agentlogoff" events have been renamed "AgentLogin" and
"AgentLogoff" respectively.
* The AMI events "Agentlogin" and "Agentlogoff" have been renamed
"AgentLogin" and "AgentLogoff" respectively.
* The "Channel" key used in the "AlarmClear", "Alarm", and "DNDState" has been
renamed "DAHDIChannel" since it does not convey an Asterisk channel name.
@ -453,6 +460,21 @@ chan_agent
and pretending otherwise helps no one.
* The AGENTUPDATECDR channel variable has also been removed, for the same
reason as the updatecdr option.
* The driver is no longer a Data retrieval API data provider for the
AMI DataGet action.
* The endcall and enddtmf configuration options are removed. Use the
dialplan function CHANNEL(dtmf-features) to set DTMF features on the agent
channel before calling AgentLogin.
* chan_agent is removed and replaced with AgentLogin and AgentRequest dialplan
applications. Agents are connected with callers using the new AgentRequest
dialplan application. The Agents:<agent-id> device state is available to
monitor the status of an agent. See agents.conf.sample for valid
configuration options.
chan_bridge
------------------
* chan_bridge is removed and its functionality is incorporated into ConfBridge
itself.
chan_local
------------------

@ -22,8 +22,8 @@
===========================================================
AgentMonitorOutgoing
- The 'c' option has been removed. It is not possible to modify the name of a
channel involved in a CDR.
- Application removed. It was a holdover from when AgentCallbackLogin was
removed.
NoCDR:
- This application is deprecated. Please use the CDR_PROP function instead.
@ -140,6 +140,15 @@ chan_agent:
and pretending otherwise helps no one.
- The AGENTUPDATECDR channel variable has also been removed, for the same
reason as the updatecdr option.
- chan_agent is removed and replaced with AgentLogin and AgentRequest dialplan
applications. Agents are connected with callers using the new AgentRequest
dialplan application. The Agents:<agent-id> device state is available to
monitor the status of an agent. See agents.conf.sample for valid
configuration options.
chan_bridge
- chan_bridge is removed and its functionality is incorporated into ConfBridge
itself.
chan_dahdi:
- Analog port dialing and deferred DTMF dialing for PRI now distinguishes

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -1,102 +1,70 @@
;
; Agent configuration
; Agent pool configuration
;
[general]
; The general section of this config is not currently used, but reserved
; for future use.
[agents]
;
; Define maxlogintries to allow agent to try max logins before
; failed.
; default to 3
;
;maxlogintries=5
;
;
; Define autologoff times if appropriate. This is how long
; the phone has to ring with no answer before the agent is
; automatically logged off (in seconds)
;
;autologoff=15
;
; Define autologoffunavail to have agents automatically logged
; out when the extension that they are at returns a CHANUNAVAIL
; status when a call is attempted to be sent there.
;[agent-id]
; Define ackcall to require the agent to give a DTMF acknowledgement
; when the agent receives a call.
; The channel variable AGENTACKCALL overrides on agent login.
; Default is "no".
;
;autologoffunavail=yes
;
; Define ackcall to require a DTMF acknowledgement when
; a logged-in agent receives a call. Default is "no".
; Use the acceptdtmf option to configure what DTMF key
; press should be used to acknowledge the call. The
; default is '#'.
;
;ackcall=no
;acceptdtmf=#
;
; Define endcall to allow an agent to hangup a call with a
; DTMF keypress. Default is "yes". Use the enddtmf option to
; configure which DTMF key will end a call. The default is
; '*'.
;
;endcall=yes
;enddtmf=*
;
; Define wrapuptime. This is the minimum amount of time when
; after disconnecting before the caller can receive a new call
; note this is in milliseconds.
; Set what DTMF key sequence the agent should use to acknowledge a call.
; The channel variable AGENTACCEPTDTMF overrides on agent login.
; This option is ignored unless ackcall is enabled.
; Default is "#".
;acceptdtmf=##
;
; Set how many seconds a call for the agent has to wait for the agent to
; acknowledge the call before the agent is automatically logged off. If
; set to zero then the call will wait forever for the agent to acknowledge.
; The channel variable AGENTAUTOLOGOFF overrides on agent login.
; This option is ignored unless ackcall is enabled.
; Default is 0.
;autologoff=15
;
; Set the minimum amount of time after disconnecting a call before
; the agent can receive a new call in milliseconds.
; The channel variable AGENTWRAPUPTIME overrides on agent login.
; Default is 0.
;wrapuptime=5000
;
; Define the default musiconhold for agents
; musiconhold => music_class
;
;musiconhold => default
;
; Define the default good bye sound file for agents
; default to vm-goodbye
; Set the musiconhold class for the agent.
; Default is "default".
;musiconhold=default
;
;goodbye => goodbye_file
;
; Define updatecdr. This is whether or not to change the source
; channel in the CDR record for this call to agent/agent_id so
; that we know which agent generates the call
;
;updatecdr=no
;
; Group memberships for agents (may change in mid-file)
;
;group=3
;group=1,2
;group=
;
; --------------------------------------------------
; This section is devoted to recording agent's calls
; The keywords are global to the chan_agent channel driver
;
; Enable recording calls addressed to agents. It's turned off by default.
; Enable recording calls the agent takes automatically by invoking the
; DTMF automixmon feature when the agent connects to a caller.
; See features.conf.sample for information about the automixmon feature.
; Default is "no".
;recordagentcalls=yes
;
; The format to be used to record the calls: wav, gsm, wav49.
; By default its "wav".
;recordformat=gsm
;
; The text to be added to the name of the recording. Allows forming a url link.
;urlprefix=http://localhost/calls/
;
; The optional directory to save the conversations in. The default is
; /var/spool/asterisk/monitor
;savecallsin=/var/calls
;
; An optional custom beep sound file to play to always-connected agents.
; The sound file played to alert the agent when a call is present.
; Default is "beep".
;custom_beep=beep
;
; A friendly name for the agent used in log messages.
; Default is "".
;fullname=Mark Spencer
;
; --------------------------------------------------
;
; This section contains the agent definitions, in the form:
; This section contains example agent definitions:
;
; Define a template called my-agents:
;[my-agents](!)
;autologoff=15
;ackcall=yes
;acceptdtmf=##
;
; agent => agentid,agentpassword,name
; Define agent 1001 using the my-agents template:
;[1001](my-agents)
;fullname=Mark Spencer
;
;agent => 1001,4321,Mark Spencer
;agent => 1002,4321,Will Meadows
; Define agent 1002 using the my-agents template:
;[1002](my-agents)
;fullname=Will Meadows

@ -543,18 +543,7 @@ monitor-type = MixMonitor
;member => DAHDI/1
;member => DAHDI/2,10
;member => DAHDI/3,10,Bob Johnson
;member => Agent/1001
;member => Agent/1002
;member => Local/1001@agents,0,May Flowers,Agent:1001
;member => Local/1002@agents,0,John Doe,Agent:1002
;member => Local/1000@default,0,John Smith,SIP/1000
;member => Local/2000@default,0,Lorem Ipsum,SIP/2000,no
;
; Note that using agent groups is probably not what you want. Strategies do
; not propagate down to the Agent system so if you want round robin, least
; recent, etc, you should list all the agents in this file individually and not
; use agent groups.
;
;member => Agent/@1 ; Any agent in group 1
;member => Agent/:1,1 ; Any agent in group 1, wait for first
; available, but consider with penalty

@ -1651,7 +1651,7 @@ void ast_after_bridge_goto_read(struct ast_channel *chan, char *buffer, size_t b
/*! Reason the the after bridge callback will not be called. */
enum ast_after_bridge_cb_reason {
/*! The datastore is being destroyed. Likely due to hangup. */
/*! The datastore is being destroyed. Likely due to hangup. (Enum value must be zero.) */
AST_AFTER_BRIDGE_CB_REASON_DESTROY,
/*! Something else replaced the callback with another. */
AST_AFTER_BRIDGE_CB_REASON_REPLACED,
@ -1670,6 +1670,9 @@ enum ast_after_bridge_cb_reason {
* \param reason Reason callback is failing.
* \param data Extra data what setup the callback wanted to pass.
*
* \note Called when the channel leaves the bridging system or
* is destroyed.
*
* \return Nothing
*/
typedef void (*ast_after_bridge_cb_failed)(enum ast_after_bridge_cb_reason reason, void *data);
@ -1709,6 +1712,9 @@ void ast_after_bridge_callback_discard(struct ast_channel *chan, enum ast_after_
*
* \note chan is locked by this function.
*
* \note failed is called when the channel leaves the bridging
* system or is destroyed.
*
* \retval 0 on success.
* \retval -1 on error.
*/

@ -461,7 +461,7 @@ enum aco_process_status {
/*! \brief Process a config info via the options registered with an aco_info
*
* \param info The config_options_info to be used for handling the config
* \param reload Whether or not this is a reload
* \param reload Non-zero if this is for a reload.
*
* \retval ACO_PROCESS_OK Success
* \retval ACO_PROCESS_ERROR Failure

@ -435,6 +435,22 @@ struct stasis_message_type *ast_channel_monitor_start_type(void);
*/
struct stasis_message_type *ast_channel_monitor_stop_type(void);
/*!
* \since 12.0.0
* \brief Message type for agent login on a channel
*
* \retval A stasis message type
*/
struct stasis_message_type *ast_channel_agent_login_type(void);
/*!
* \since 12.0.0
* \brief Message type for agent logoff on a channel
*
* \retval A stasis message type
*/
struct stasis_message_type *ast_channel_agent_logoff_type(void);
/*!
* \since 12
* \brief Message type for starting music on hold on a channel

@ -3242,15 +3242,70 @@ static struct ast_bridge_channel *bridge_channel_alloc(struct ast_bridge *bridge
return bridge_channel;
}
struct after_bridge_cb_ds {
struct after_bridge_cb_node {
/*! Next list node. */
AST_LIST_ENTRY(after_bridge_cb_node) list;
/*! Desired callback function. */
ast_after_bridge_cb callback;
/*! After bridge callback will not be called and destroy any resources data may contain. */
ast_after_bridge_cb_failed failed;
/*! Extra data to pass to the callback. */
void *data;
/*! Reason the after bridge callback failed. */
enum ast_after_bridge_cb_reason reason;
};
struct after_bridge_cb_ds {
/*! After bridge callbacks container. */
AST_LIST_HEAD(, after_bridge_cb_node) callbacks;
};
/*!
* \internal
* \brief Indicate after bridge callback failed.
* \since 12.0.0
*
* \param node After bridge callback node.
*
* \return Nothing
*/
static void after_bridge_cb_failed(struct after_bridge_cb_node *node)
{
if (node->failed) {
node->failed(node->reason, node->data);
node->failed = NULL;
}
}
/*!
* \internal
* \brief Run discarding any after bridge callbacks.
* \since 12.0.0
*
* \param after_bridge After bridge callback container process.
* \param reason Why are we doing this.
*
* \return Nothing
*/
static void after_bridge_cb_run_discard(struct after_bridge_cb_ds *after_bridge, enum ast_after_bridge_cb_reason reason)
{
struct after_bridge_cb_node *node;
for (;;) {
AST_LIST_LOCK(&after_bridge->callbacks);
node = AST_LIST_REMOVE_HEAD(&after_bridge->callbacks, list);
AST_LIST_UNLOCK(&after_bridge->callbacks);
if (!node) {
break;
}
if (!node->reason) {
node->reason = reason;
}
after_bridge_cb_failed(node);
ast_free(node);
}
}
/*!
* \internal
* \brief Destroy the after bridge callback datastore.
@ -3264,10 +3319,9 @@ static void after_bridge_cb_destroy(void *data)
{
struct after_bridge_cb_ds *after_bridge = data;
if (after_bridge->failed) {
after_bridge->failed(AST_AFTER_BRIDGE_CB_REASON_DESTROY, after_bridge->data);
after_bridge->failed = NULL;
}
after_bridge_cb_run_discard(after_bridge, AST_AFTER_BRIDGE_CB_REASON_DESTROY);
AST_LIST_HEAD_DESTROY(&after_bridge->callbacks);
ast_free(after_bridge);
}
@ -3284,7 +3338,6 @@ static void after_bridge_cb_destroy(void *data)
*/
static void after_bridge_cb_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
{
/* There can be only one. Discard any already on the new channel. */
ast_after_bridge_callback_discard(new_chan, AST_AFTER_BRIDGE_CB_REASON_MASQUERADE);
}
@ -3296,47 +3349,67 @@ static const struct ast_datastore_info after_bridge_cb_info = {
/*!
* \internal
* \brief Remove channel after the bridge callback and return it.
* \brief Setup/create an after bridge callback datastore container.
* \since 12.0.0
*
* \param chan Channel to remove after bridge callback.
* \param chan Channel to setup/create the after bridge callback container on.
*
* \retval datastore on success.
* \retval NULL on error or not found.
* \retval after_bridge datastore container on success.
* \retval NULL on error.
*/
static struct ast_datastore *after_bridge_cb_remove(struct ast_channel *chan)
static struct after_bridge_cb_ds *after_bridge_cb_setup(struct ast_channel *chan)
{
struct ast_datastore *datastore;
struct after_bridge_cb_ds *after_bridge;
SCOPED_CHANNELLOCK(lock, chan);
ast_channel_lock(chan);
datastore = ast_channel_datastore_find(chan, &after_bridge_cb_info, NULL);
if (datastore && ast_channel_datastore_remove(chan, datastore)) {
datastore = NULL;
if (datastore) {
return datastore->data;
}
ast_channel_unlock(chan);
return datastore;
/* Create a new datastore. */
datastore = ast_datastore_alloc(&after_bridge_cb_info, NULL);
if (!datastore) {
return NULL;
}
after_bridge = ast_calloc(1, sizeof(*after_bridge));
if (!after_bridge) {
ast_datastore_free(datastore);
return NULL;
}
AST_LIST_HEAD_INIT(&after_bridge->callbacks);
datastore->data = after_bridge;
ast_channel_datastore_add(chan, datastore);
return datastore->data;
}
void ast_after_bridge_callback_discard(struct ast_channel *chan, enum ast_after_bridge_cb_reason reason)
/*!
* \internal
* \brief Find an after bridge callback datastore container.
* \since 12.0.0
*
* \param chan Channel to find the after bridge callback container on.
*
* \retval after_bridge datastore container on success.
* \retval NULL on error.
*/
static struct after_bridge_cb_ds *after_bridge_cb_find(struct ast_channel *chan)
{
struct ast_datastore *datastore;
SCOPED_CHANNELLOCK(lock, chan);
datastore = after_bridge_cb_remove(chan);
if (datastore) {
struct after_bridge_cb_ds *after_bridge = datastore->data;
if (after_bridge && after_bridge->failed) {
after_bridge->failed(reason, after_bridge->data);
after_bridge->failed = NULL;
}
ast_datastore_free(datastore);
datastore = ast_channel_datastore_find(chan, &after_bridge_cb_info, NULL);
if (!datastore) {
return NULL;
}
return datastore->data;
}
/*!
* \internal
* \brief Run any after bridge callback if possible.
* \brief Run any after bridge callback.
* \since 12.0.0
*
* \param chan Channel to run after bridge callback.
@ -3345,33 +3418,75 @@ void ast_after_bridge_callback_discard(struct ast_channel *chan, enum ast_after_
*/
static void after_bridge_callback_run(struct ast_channel *chan)
{
struct ast_datastore *datastore;
struct after_bridge_cb_ds *after_bridge;
struct after_bridge_cb_node *node;
if (ast_check_hangup(chan)) {
after_bridge = after_bridge_cb_find(chan);
if (!after_bridge) {
return;
}
/* Get after bridge goto datastore. */
datastore = after_bridge_cb_remove(chan);
if (!datastore) {
for (;;) {
AST_LIST_LOCK(&after_bridge->callbacks);
node = AST_LIST_REMOVE_HEAD(&after_bridge->callbacks, list);
AST_LIST_UNLOCK(&after_bridge->callbacks);
if (!node) {
break;
}
if (node->reason) {
after_bridge_cb_failed(node);
} else {
node->failed = NULL;
node->callback(chan, node->data);
}
ast_free(node);
}
}
/*!
* \internal
* \brief Run discarding any after bridge callbacks.
* \since 12.0.0
*
* \param chan Channel to run after bridge callback.
*
* \return Nothing
*/
static void after_bridge_callback_run_discard(struct ast_channel *chan, enum ast_after_bridge_cb_reason reason)
{
struct after_bridge_cb_ds *after_bridge;
after_bridge = after_bridge_cb_find(chan);
if (!after_bridge) {
return;
}
after_bridge = datastore->data;
if (after_bridge) {
after_bridge->failed = NULL;
after_bridge->callback(chan, after_bridge->data);
after_bridge_cb_run_discard(after_bridge, reason);
}
void ast_after_bridge_callback_discard(struct ast_channel *chan, enum ast_after_bridge_cb_reason reason)
{
struct after_bridge_cb_ds *after_bridge;
struct after_bridge_cb_node *node;
after_bridge = after_bridge_cb_find(chan);
if (!after_bridge) {
return;
}
/* Discard after bridge callback datastore. */
ast_datastore_free(datastore);
AST_LIST_LOCK(&after_bridge->callbacks);
node = AST_LIST_LAST(&after_bridge->callbacks);
if (node && !node->reason) {
node->reason = reason;
}
AST_LIST_UNLOCK(&after_bridge->callbacks);
}
int ast_after_bridge_callback_set(struct ast_channel *chan, ast_after_bridge_cb callback, ast_after_bridge_cb_failed failed, void *data)
{
struct ast_datastore *datastore;
struct after_bridge_cb_ds *after_bridge;
struct after_bridge_cb_node *new_node;
struct after_bridge_cb_node *last_node;
/* Sanity checks. */
ast_assert(chan != NULL);
@ -3379,29 +3494,28 @@ int ast_after_bridge_callback_set(struct ast_channel *chan, ast_after_bridge_cb
return -1;
}
/* Create a new datastore. */
datastore = ast_datastore_alloc(&after_bridge_cb_info, NULL);
if (!datastore) {
return -1;
}
after_bridge = ast_calloc(1, sizeof(*after_bridge));
after_bridge = after_bridge_cb_setup(chan);
if (!after_bridge) {
ast_datastore_free(datastore);
return -1;
}
/* Initialize it. */
after_bridge->callback = callback;
after_bridge->failed = failed;
after_bridge->data = data;
datastore->data = after_bridge;
/* Put it on the channel replacing any existing one. */
ast_channel_lock(chan);
ast_after_bridge_callback_discard(chan, AST_AFTER_BRIDGE_CB_REASON_REPLACED);
ast_channel_datastore_add(chan, datastore);
ast_channel_unlock(chan);
/* Create a new callback node. */
new_node = ast_calloc(1, sizeof(*new_node));
if (!new_node) {
return -1;
}
new_node->callback = callback;
new_node->failed = failed;
new_node->data = data;
/* Put it in the container disabling any previously active one. */
AST_LIST_LOCK(&after_bridge->callbacks);
last_node = AST_LIST_LAST(&after_bridge->callbacks);
if (last_node && !last_node->reason) {
last_node->reason = AST_AFTER_BRIDGE_CB_REASON_REPLACED;
}
AST_LIST_INSERT_TAIL(&after_bridge->callbacks, new_node, list);
AST_LIST_UNLOCK(&after_bridge->callbacks);
return 0;
}
@ -3866,7 +3980,7 @@ static void *bridge_channel_depart_thread(void *data)
ast_bridge_features_destroy(bridge_channel->features);
bridge_channel->features = NULL;
ast_after_bridge_callback_discard(bridge_channel->chan, AST_AFTER_BRIDGE_CB_REASON_DEPART);
after_bridge_callback_run_discard(bridge_channel->chan, AST_AFTER_BRIDGE_CB_REASON_DEPART);
ast_after_bridge_goto_discard(bridge_channel->chan);
return NULL;

@ -55,6 +55,35 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</syntax>
</managerEventInstance>
</managerEvent>
<managerEvent language="en_US" name="AgentLogin">
<managerEventInstance class="EVENT_FLAG_AGENT">
<synopsis>Raised when an Agent has logged in.</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" />
<parameter name="Agent">
<para>Agent ID of the agent.</para>
</parameter>
</syntax>
<see-also>
<ref type="application">AgentLogin</ref>
<ref type="managerEvent">AgentLogoff</ref>
</see-also>
</managerEventInstance>
</managerEvent>
<managerEvent language="en_US" name="AgentLogoff">
<managerEventInstance class="EVENT_FLAG_AGENT">
<synopsis>Raised when an Agent has logged off.</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/managerEvent[@name='AgentLogin']/managerEventInstance/syntax/parameter)" />
<parameter name="Logintime">
<para>The number of seconds the agent was logged in.</para>
</parameter>
</syntax>
<see-also>
<ref type="managerEvent">AgentLogin</ref>
</see-also>
</managerEventInstance>
</managerEvent>
***/
#define NUM_MULTI_CHANNEL_BLOB_BUCKETS 7
@ -627,6 +656,44 @@ static struct ast_manager_event_blob *varset_to_ami(struct stasis_message *msg)
ast_str_buffer(channel_event_string), variable, value);
}
static struct ast_manager_event_blob *agent_login_to_ami(struct stasis_message *msg)
{
RAII_VAR(struct ast_str *, channel_string, NULL, ast_free);
RAII_VAR(struct ast_str *, party_string, ast_str_create(256), ast_free);
struct ast_channel_blob *obj = stasis_message_data(msg);
const char *agent = ast_json_string_get(ast_json_object_get(obj->blob, "agent"));
channel_string = ast_manager_build_channel_state_string(obj->snapshot);
if (!channel_string) {
return NULL;
}
return ast_manager_event_blob_create(EVENT_FLAG_AGENT, "AgentLogin",
"%s"
"Agent: %s\r\n",
ast_str_buffer(channel_string), agent);
}
static struct ast_manager_event_blob *agent_logoff_to_ami(struct stasis_message *msg)
{
RAII_VAR(struct ast_str *, channel_string, NULL, ast_free);
RAII_VAR(struct ast_str *, party_string, ast_str_create(256), ast_free);
struct ast_channel_blob *obj = stasis_message_data(msg);
const char *agent = ast_json_string_get(ast_json_object_get(obj->blob, "agent"));
long logintime = ast_json_integer_get(ast_json_object_get(obj->blob, "logintime"));
channel_string = ast_manager_build_channel_state_string(obj->snapshot);
if (!channel_string) {
return NULL;
}
return ast_manager_event_blob_create(EVENT_FLAG_AGENT, "AgentLogoff",
"%s"
"Agent: %s\r\n"
"Logintime: %ld\r\n",
ast_str_buffer(channel_string), agent, logintime);
}
void ast_publish_channel_state(struct ast_channel *chan)
{
RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
@ -827,6 +894,12 @@ STASIS_MESSAGE_TYPE_DEFN(ast_channel_moh_start_type);
STASIS_MESSAGE_TYPE_DEFN(ast_channel_moh_stop_type);
STASIS_MESSAGE_TYPE_DEFN(ast_channel_monitor_start_type);
STASIS_MESSAGE_TYPE_DEFN(ast_channel_monitor_stop_type);
STASIS_MESSAGE_TYPE_DEFN(ast_channel_agent_login_type,
.to_ami = agent_login_to_ami,
);
STASIS_MESSAGE_TYPE_DEFN(ast_channel_agent_logoff_type,
.to_ami = agent_logoff_to_ami,
);
/*! @} */
@ -853,6 +926,8 @@ static void stasis_channels_cleanup(void)
STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_moh_stop_type);
STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_monitor_start_type);
STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_monitor_stop_type);
STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_agent_login_type);
STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_agent_logoff_type);
}
void ast_stasis_channels_init(void)
@ -876,6 +951,8 @@ void ast_stasis_channels_init(void)
STASIS_MESSAGE_TYPE_INIT(ast_channel_moh_stop_type);
STASIS_MESSAGE_TYPE_INIT(ast_channel_monitor_start_type);
STASIS_MESSAGE_TYPE_INIT(ast_channel_monitor_stop_type);
STASIS_MESSAGE_TYPE_INIT(ast_channel_agent_login_type);
STASIS_MESSAGE_TYPE_INIT(ast_channel_agent_logoff_type);
channel_topic_all = stasis_topic_create("ast_channel_topic_all");
channel_topic_all_cached = stasis_caching_topic_create(channel_topic_all, channel_snapshot_get_id);

Loading…
Cancel
Save