diff --git a/configs/samples/stasis.conf.sample b/configs/samples/stasis.conf.sample index 508f7de447..0ae7b9d89c 100644 --- a/configs/samples/stasis.conf.sample +++ b/configs/samples/stasis.conf.sample @@ -69,6 +69,7 @@ ; decline=ast_channel_agent_logoff_type ; decline=ast_channel_talking_start ; decline=ast_channel_talking_stop +; decline=ast_channel_tone_detect ; decline=ast_security_event_type ; decline=ast_named_acl_change_type ; decline=ast_local_bridge_type diff --git a/include/asterisk/stasis_channels.h b/include/asterisk/stasis_channels.h index 728e0ff1e3..c5dd9d02f4 100644 --- a/include/asterisk/stasis_channels.h +++ b/include/asterisk/stasis_channels.h @@ -656,6 +656,14 @@ struct stasis_message_type *ast_channel_talking_start(void); */ struct stasis_message_type *ast_channel_talking_stop(void); +/*! + * \since 22.0.0 + * \brief Message type for a channel tone detection + * + * \return A stasis message type + */ +struct stasis_message_type *ast_channel_tone_detect(void); + /*! * \since 12 * \brief Publish in the \ref ast_channel_topic or \ref ast_channel_topic_all diff --git a/main/stasis.c b/main/stasis.c index 05a7a505f7..f3bc0ffdb7 100644 --- a/main/stasis.c +++ b/main/stasis.c @@ -137,6 +137,7 @@ + diff --git a/main/stasis_channels.c b/main/stasis_channels.c index 4b48a9c9be..916f7dacca 100644 --- a/main/stasis_channels.c +++ b/main/stasis_channels.c @@ -1592,6 +1592,13 @@ static struct ast_json *hold_to_json(struct stasis_message *message, "channel", json_channel); } + +static struct ast_json *tone_detect_to_json(struct stasis_message *message, + const struct stasis_message_sanitizer *sanitize) +{ + return channel_blob_to_json(message, "ChannelToneDetected", sanitize); +} + static struct ast_json *unhold_to_json(struct stasis_message *message, const struct stasis_message_sanitizer *sanitize) { @@ -1661,6 +1668,9 @@ STASIS_MESSAGE_TYPE_DEFN(ast_channel_talking_stop, .to_ami = talking_stop_to_ami, .to_json = talking_stop_to_json, ); +STASIS_MESSAGE_TYPE_DEFN(ast_channel_tone_detect, + .to_json = tone_detect_to_json, + ); /*! @} */ @@ -1697,6 +1707,7 @@ static void stasis_channels_cleanup(void) STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_agent_logoff_type); STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_talking_start); STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_talking_stop); + STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_tone_detect); } int ast_stasis_channels_init(void) @@ -1748,6 +1759,7 @@ int ast_stasis_channels_init(void) res |= STASIS_MESSAGE_TYPE_INIT(ast_channel_mixmonitor_mute_type); res |= STASIS_MESSAGE_TYPE_INIT(ast_channel_talking_start); res |= STASIS_MESSAGE_TYPE_INIT(ast_channel_talking_stop); + res |= STASIS_MESSAGE_TYPE_INIT(ast_channel_tone_detect); return res; } diff --git a/res/ari/ari_model_validators.c b/res/ari/ari_model_validators.c index a5ba5cc768..0b20808c7b 100644 --- a/res/ari/ari_model_validators.c +++ b/res/ari/ari_model_validators.c @@ -4906,6 +4906,101 @@ ari_validator ast_ari_validate_channel_talking_started_fn(void) return ast_ari_validate_channel_talking_started; } +int ast_ari_validate_channel_tone_detected(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_type = 0; + int has_application = 0; + int has_timestamp = 0; + int has_channel = 0; + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + if (strcmp("asterisk_id", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelToneDetected field asterisk_id failed validation\n"); + res = 0; + } + } else + if (strcmp("type", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_type = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelToneDetected field type failed validation\n"); + res = 0; + } + } else + if (strcmp("application", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_application = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelToneDetected field application failed validation\n"); + res = 0; + } + } else + if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_timestamp = 1; + prop_is_valid = ast_ari_validate_date( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelToneDetected field timestamp failed validation\n"); + res = 0; + } + } else + if (strcmp("channel", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_channel = 1; + prop_is_valid = ast_ari_validate_channel( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelToneDetected field channel failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI ChannelToneDetected has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_type) { + ast_log(LOG_ERROR, "ARI ChannelToneDetected missing required field type\n"); + res = 0; + } + + if (!has_application) { + ast_log(LOG_ERROR, "ARI ChannelToneDetected missing required field application\n"); + res = 0; + } + + if (!has_timestamp) { + ast_log(LOG_ERROR, "ARI ChannelToneDetected missing required field timestamp\n"); + res = 0; + } + + if (!has_channel) { + ast_log(LOG_ERROR, "ARI ChannelToneDetected missing required field channel\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_channel_tone_detected_fn(void) +{ + return ast_ari_validate_channel_tone_detected; +} + int ast_ari_validate_channel_unhold(struct ast_json *json) { int res = 1; @@ -5867,6 +5962,9 @@ int ast_ari_validate_event(struct ast_json *json) if (strcmp("ChannelTalkingStarted", discriminator) == 0) { return ast_ari_validate_channel_talking_started(json); } else + if (strcmp("ChannelToneDetected", discriminator) == 0) { + return ast_ari_validate_channel_tone_detected(json); + } else if (strcmp("ChannelUnhold", discriminator) == 0) { return ast_ari_validate_channel_unhold(json); } else @@ -6074,6 +6172,9 @@ int ast_ari_validate_message(struct ast_json *json) if (strcmp("ChannelTalkingStarted", discriminator) == 0) { return ast_ari_validate_channel_talking_started(json); } else + if (strcmp("ChannelToneDetected", discriminator) == 0) { + return ast_ari_validate_channel_tone_detected(json); + } else if (strcmp("ChannelUnhold", discriminator) == 0) { return ast_ari_validate_channel_unhold(json); } else diff --git a/res/ari/ari_model_validators.h b/res/ari/ari_model_validators.h index f350c8de33..7289be26ac 100644 --- a/res/ari/ari_model_validators.h +++ b/res/ari/ari_model_validators.h @@ -911,6 +911,22 @@ int ast_ari_validate_channel_talking_started(struct ast_json *json); */ ari_validator ast_ari_validate_channel_talking_started_fn(void); +/*! + * \brief Validator for ChannelToneDetected. + * + * Tone was detected on the channel. + * + * \param json JSON object to validate. + * \retval True (non-zero) if valid. + * \retval False (zero) if invalid. + */ +int ast_ari_validate_channel_tone_detected(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_channel_tone_detected(). + */ +ari_validator ast_ari_validate_channel_tone_detected_fn(void); + /*! * \brief Validator for ChannelUnhold. * @@ -1605,6 +1621,12 @@ ari_validator ast_ari_validate_application_fn(void); * - application: string (required) * - timestamp: Date (required) * - channel: Channel (required) + * ChannelToneDetected + * - asterisk_id: string + * - type: string (required) + * - application: string (required) + * - timestamp: Date (required) + * - channel: Channel (required) * ChannelUnhold * - asterisk_id: string * - type: string (required) diff --git a/res/res_tonedetect.c b/res/res_tonedetect.c index c81e80c370..97ba60cc42 100644 --- a/res/res_tonedetect.c +++ b/res/res_tonedetect.c @@ -39,6 +39,7 @@ #include "asterisk/channel.h" #include "asterisk/dsp.h" #include "asterisk/pbx.h" +#include "asterisk/stasis_channels.h" #include "asterisk/audiohook.h" #include "asterisk/app.h" #include "asterisk/indications.h" @@ -355,6 +356,7 @@ static int detect_callback(struct ast_audiohook *audiohook, struct ast_channel * { struct ast_datastore *datastore = NULL; struct detect_information *di = NULL; + struct stasis_message *message; int match = 0; /* If the audiohook is stopping it means the channel is shutting down.... but we let the datastore destroy take care of it */ @@ -394,6 +396,16 @@ static int detect_callback(struct ast_audiohook *audiohook, struct ast_channel * } ast_debug(1, "TONE_DETECT just got a hit (#%d in this direction, waiting for %d total)\n", now, di->hitsrequired); if (now >= di->hitsrequired) { + message = ast_channel_blob_create_from_cache(ast_channel_uniqueid(chan), ast_channel_tone_detect(), NULL); + + if (!message) { + ast_log(LOG_ERROR, "Unable to publish tone detected event for ARI on channel '%s'", ast_channel_name(chan)); + return 1; + } else { + stasis_publish(ast_channel_topic(chan), message); + ao2_ref(message, -1); + } + if (direction == AST_AUDIOHOOK_DIRECTION_READ && di->gotorx) { ast_async_parseable_goto(chan, di->gotorx); } else if (di->gototx) { diff --git a/rest-api/api-docs/events.json b/rest-api/api-docs/events.json index c9822f6cbb..2bd1eeabc7 100644 --- a/rest-api/api-docs/events.json +++ b/rest-api/api-docs/events.json @@ -178,6 +178,7 @@ "ChannelUserevent", "ChannelHangupRequest", "ChannelVarset", + "ChannelToneDetected", "ChannelTalkingStarted", "ChannelTalkingFinished", "ChannelHold", @@ -759,6 +760,17 @@ } } }, + "ChannelToneDetected": { + "id": "ChannelToneDetected", + "description": "Tone was detected on the channel.", + "properties": { + "channel": { + "required": true, + "type": "Channel", + "description": "The channel the tone was detected on." + } + } + }, "ChannelTalkingStarted": { "id": "ChannelTalkingStarted", "description": "Talking was detected on the channel.",