你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn 。
本文内容
通话自动化使用 REST API 接口来接收操作请求,并提供响应以告知请求是否已成功提交。 由于通话的异步性,大多数操作将在操作成功完成或失败时触发相应的事件。 本指南介绍在通话期间可供开发人员执行的操作,例如发送 DTMF 和连续 DTMF 识别。 操作随附有关如何调用上述操作的示例代码。
通话自动化支持使用其他各种操作来管理本指南中未提到的通话和录音。
注意
通话自动化目前不能与 Microsoft Teams 互操作。 不支持使用通话自动化呼叫 Teams 用户,向其重定向通话或向 Teams 用户播放音频等操作。
作为先决条件,建议你先阅读以下文章以充分理解本指南:
介绍操作事件编程模型和事件回调的通话自动化概念指南 。
了解本指南中使用的用户标识符 ,例如 CommunicationUserIdentifier 和 PhoneNumberIdentifier。
详细了解如何通过通话自动化控制和引导通话 ,这将教会你处理通话的基本知识。
对于所有代码示例,client
是可以按示例所示创建的 CallAutomationClient 对象,callConnection
是从 Answer 或 CreateCall 响应中获取的 CallConnection 对象。 也可以从应用程序收到的回调事件中获取该对象。
var callAutomationClient = new CallAutomationClient("<Azure Communication Services connection string>");
CallAutomationClient callAutomationClient = new CallAutomationClientBuilder()
.connectionString("<Azure Communication Services connection string>")
.buildClient();
callAutomationClient = new CallAutomationClient(("<Azure Communication Services connection string>");
call_automation_client = CallAutomationClient.from_connection_string((("<Azure Communication Services connection string>")
发送 DTMF
可将 DTMF 音调发送给外部参与者,当你已经在通话且需要邀请另一个拥有分机号码或 IVR 导航菜单的参与者时,这可能很有用。
注意
此功能仅支持外部 PSTN 参与者,且此功能支持一次发送最多 18 个音调。
SendDtmfAsync 方法
向外部参与者发送 DTMF 音调列表。
var tones = new DtmfTone[] { DtmfTone.One, DtmfTone.Two, DtmfTone.Three, DtmfTone.Pound };
var sendDtmfTonesOptions = new SendDtmfTonesOptions(tones, new PhoneNumberIdentifier(calleePhonenumber))
{
OperationContext = "dtmfs-to-ivr"
};
var sendDtmfAsyncResult = await callAutomationClient.GetCallConnection(callConnectionId)
.GetCallMedia()
.SendDtmfTonesAsync(sendDtmfTonesOptions);
List<DtmfTone> tones = Arrays.asList(DtmfTone.ONE, DtmfTone.TWO, DtmfTone.THREE, DtmfTone.POUND);
SendDtmfTonesOptions options = new SendDtmfTonesOptions(tones, new PhoneNumberIdentifier(c2Target));
options.setOperationContext("dtmfs-to-ivr");
callAutomationClient.getCallConnectionAsync(callConnectionId)
.getCallMediaAsync()
.sendDtmfTonesWithResponse(options)
.block();
const tones = [DtmfTone.One, DtmfTone.Two, DtmfTone.Three];
const sendDtmfTonesOptions: SendDtmfTonesOptions = {
operationContext: "dtmfs-to-ivr"
};
const result: SendDtmfTonesResult = await callAutomationClient.getCallConnection(callConnectionId)
.getCallMedia()
.sendDtmfTones(tones, {
phoneNumber: c2Target
}, sendDtmfTonesOptions);
console.log("sendDtmfTones, result=%s", result);
tones = [DtmfTone.ONE, DtmfTone.TWO, DtmfTone.THREE]
result = call_automation_client.get_call_connection(call_connection_id).send_dtmf_tones(
tones = tones,
target_participant = PhoneNumberIdentifier(c2_target),
operation_context = "dtmfs-to-ivr")
app.logger.info("Send dtmf, result=%s", result)
当应用程序发送这些 DTMF 音调时,你会收到事件更新。 可使用 SendDtmfTonesCompleted
和 SendDtmfTonesFailed
事件在应用程序中创建业务逻辑,以确定后续步骤。
SendDtmfTonesCompleted 事件的示例
if (acsEvent is SendDtmfTonesCompleted sendDtmfCompleted)
{
logger.LogInformation("Send DTMF succeeded, context={context}", sendDtmfCompleted.OperationContext);
}
if (acsEvent instanceof SendDtmfTonesCompleted) {
SendDtmfTonesCompleted event = (SendDtmfTonesCompleted) acsEvent;
log.info("Send dtmf succeeded: context=" + event.getOperationContext());
}
if (event.type === "Microsoft.Communication.SendDtmfTonesCompleted") {
console.log("Send dtmf succeeded: context=%s", eventData.operationContext);
}
if event.type == "Microsoft.Communication.SendDtmfTonesCompleted":
app.logger.info("Send dtmf succeeded: context=%s", event.data['operationContext']);
SendDtmfTonesFailed 的示例
if (acsEvent is SendDtmfTonesFailed sendDtmfFailed)
{
logger.LogInformation("Send dtmf failed: result={result}, context={context}",
sendDtmfFailed.ResultInformation?.Message, sendDtmfFailed.OperationContext);
}
if (acsEvent instanceof SendDtmfTonesFailed) {
SendDtmfTonesFailed event = (SendDtmfTonesFailed) acsEvent;
log.info("Send dtmf failed: result=" + event.getResultInformation().getMessage() + ", context="
+ event.getOperationContext());
}
if (event.type === "Microsoft.Communication.SendDtmfTonesFailed") {
console.log("sendDtmfTones failed: result=%s, context=%s",
eventData.resultInformation.message,
eventData.operationContext);
}
if event.type == "Microsoft.Communication.SendDtmfTonesFailed":
app.logger.info("Send dtmf failed: result=%s, context=%s", event.data['resultInformation']['message'], event.data['operationContext'])
连续 DTMF 识别
可在整个通话中订阅接收连续 DTMF 音调。 当目标参与者按下键盘上的某个键时,应用程序会收到 DTMF 音调。 当参与者按下这些音调时,它们会一个接一个地发送到应用程序。
StartContinuousDtmfRecognitionAsync 方法
开始检测参与者发送的 DTMF 音调。
await callAutomationClient.GetCallConnection(callConnectionId)
.GetCallMedia()
.StartContinuousDtmfRecognitionAsync(new PhoneNumberIdentifier(c2Target), "dtmf-reco-on-c2");
ContinuousDtmfRecognitionOptions options = new ContinuousDtmfRecognitionOptions(new PhoneNumberIdentifier(c2Target));
options.setOperationContext("dtmf-reco-on-c2");
callAutomationClient.getCallConnectionAsync(callConnectionId)
.getCallMediaAsync()
.startContinuousDtmfRecognitionWithResponse(options)
.block();
const continuousDtmfRecognitionOptions: ContinuousDtmfRecognitionOptions = {
operationContext: "dtmf-reco-on-c2"
};
await callAutomationclient.getCallConnection(callConnectionId)
.getCallMedia()
.startContinuousDtmfRecognition({
phoneNumber: c2Target
}, continuousDtmfRecognitionOptions);
call_automation_client.get_call_connection(
call_connection_id
).start_continuous_dtmf_recognition(
target_participant=PhoneNumberIdentifier(c2_target),
operation_context="dtmf-reco-on-c2",
)
app.logger.info("Started continuous DTMF recognition")
当应用程序不再希望从参与者处接收 DTMF 音调时,可使用 StopContinuousDtmfRecognitionAsync
方法让 Azure 通信服务知道停止检测 DTMF 音调。
StopContinuousDtmfRecognitionAsync
停止检测参与者发送的 DTMF 音调。
var continuousDtmfRecognitionOptions = new ContinuousDtmfRecognitionOptions(new PhoneNumberIdentifier(callerPhonenumber))
{
OperationContext = "dtmf-reco-on-c2"
};
var startContinuousDtmfRecognitionAsyncResult = await callAutomationClient.GetCallConnection(callConnectionId)
.GetCallMedia()
.StartContinuousDtmfRecognitionAsync(continuousDtmfRecognitionOptions);
ContinuousDtmfRecognitionOptions options = new ContinuousDtmfRecognitionOptions(new PhoneNumberIdentifier(c2Target));
options.setOperationContext("dtmf-reco-on-c2");
callAutomationClient.getCallConnectionAsync(callConnectionId)
.getCallMediaAsync()
.stopContinuousDtmfRecognitionWithResponse(options)
.block();
const continuousDtmfRecognitionOptions: ContinuousDtmfRecognitionOptions = {
operationContext: "dtmf-reco-on-c2"
};
await callAutomationclient.getCallConnection(callConnectionId)
.getCallMedia()
.stopContinuousDtmfRecognition({
phoneNumber: c2Target
}, continuousDtmfRecognitionOptions);
call_automation_client.get_call_connection(call_connection_id).stop_continuous_dtmf_recognition(
target_participant=PhoneNumberIdentifier(c2_target),
operation_context="dtmf-reco-on-c2")
app.logger.info("Stopped continuous DTMF recognition")
当这些操作成功或失败时,应用程序会收到事件更新。 可使用这些事件来生成自定义业务逻辑,以配置应用程序在收到这些事件更新时需要执行的下一步骤。
ContinuousDtmfRecognitionToneReceived 事件
如何处理成功检测到的 DTMF 音调的示例。
if (acsEvent is ContinuousDtmfRecognitionToneReceived continuousDtmfRecognitionToneReceived)
{
logger.LogInformation("Tone detected: sequenceId={sequenceId}, tone={tone}",
continuousDtmfRecognitionToneReceived.SequenceId,
continuousDtmfRecognitionToneReceived.Tone);
}
if (acsEvent instanceof ContinuousDtmfRecognitionToneReceived) {
ContinuousDtmfRecognitionToneReceived event = (ContinuousDtmfRecognitionToneReceived) acsEvent;
log.info("Tone detected: sequenceId=" + event.getSequenceId()
+ ", tone=" + event.getTone().convertToString()
+ ", context=" + event.getOperationContext());
}
if (event.type === "Microsoft.Communication.ContinuousDtmfRecognitionToneReceived") {
console.log("Tone detected: sequenceId=%s, tone=%s, context=%s",
eventData.sequenceId,
eventData.tone,
eventData.operationContext);
}
if event.type == "Microsoft.Communication.ContinuousDtmfRecognitionToneReceived":
app.logger.info("Tone detected: sequenceId=%s, tone=%s, context=%s",
event.data['sequenceId'],
event.data['tone'],
event.data['operationContext'])
Azure 通信服务提供 SequenceId
作为 ContinuousDtmfRecognitionToneReceived
事件的一部分,而应用程序可使用它重构参与者输入 DTMF 音调的顺序。
ContinuousDtmfRecognitionFailed 事件
如何在 DTMF 音调检测失败时进行处理的示例。
if (acsEvent is ContinuousDtmfRecognitionToneFailed continuousDtmfRecognitionToneFailed)
{
logger.LogInformation("Start continuous DTMF recognition failed, result={result}, context={context}",
continuousDtmfRecognitionToneFailed.ResultInformation?.Message,
continuousDtmfRecognitionToneFailed.OperationContext);
}
if (acsEvent instanceof ContinuousDtmfRecognitionToneFailed) {
ContinuousDtmfRecognitionToneFailed event = (ContinuousDtmfRecognitionToneFailed) acsEvent;
log.info("Tone failed: result="+ event.getResultInformation().getMessage()
+ ", context=" + event.getOperationContext());
}
if (event.type === "Microsoft.Communication.ContinuousDtmfRecognitionToneFailed") {
console.log("Tone failed: result=%s, context=%s", eventData.resultInformation.message, eventData.operationContext);
}
if event.type == "Microsoft.Communication.ContinuousDtmfRecognitionToneFailed":
app.logger.info(
"Tone failed: result=%s, context=%s",
event.data["resultInformation"]["message"],
event.data["operationContext"],
)
ContinuousDtmfRecogntionStopped 事件
如何在连续 DTMF 识别停止(这可能是因为应用程序调用了 StopContinuousDtmfRecognitionAsync
事件或通话已结束)时进行处理的示例。
if (acsEvent is ContinuousDtmfRecognitionStopped continuousDtmfRecognitionStopped)
{
logger.LogInformation("Continuous DTMF recognition stopped, context={context}", continuousDtmfRecognitionStopped.OperationContext);
}
if (acsEvent instanceof ContinuousDtmfRecognitionStopped) {
ContinuousDtmfRecognitionStopped event = (ContinuousDtmfRecognitionStopped) acsEvent;
log.info("Tone stopped, context=" + event.getOperationContext());
}
if (event.type === "Microsoft.Communication.ContinuousDtmfRecognitionStopped") {
console.log("Tone stopped: context=%s", eventData.operationContext);
}
if event.type == "Microsoft.Communication.ContinuousDtmfRecognitionStopped":
app.logger.info("Tone stoped: context=%s", event.data["operationContext"])
Hold
通过“保持”操作,开发人员可暂时暂停参与者与系统或代理之间的对话。 这在以下情况下很有用:参与者需要被转接至另一个代理或部门,或者代理需要在后台咨询主管才能继续对话。 在此期间,可选择向处于保持状态的参与者播放音频。
// Option 1: Hold without additional options
await callAutomationClient.GetCallConnection(callConnectionId)
.GetCallMedia().HoldAsync(c2Target);
/*
// Option 2: Hold with play source
PlaySource playSource = /* initialize playSource */;
await callAutomationClient.GetCallConnection(callConnectionId)
.GetCallMedia().HoldAsync(c2Target, playSource);
// Option 3: Hold with options
var holdOptions = new HoldOptions(target)
{
OperationCallbackUri = new Uri(""),
OperationContext = "holdcontext"
};
await callMedia.HoldAsync(holdOptions);
*/
// Option 1: Hold with options
PlaySource playSource = /* initialize playSource */;
HoldOptions holdOptions = new HoldOptions(target)
.setOperationCallbackUrl(appConfig.getBasecallbackuri())
.setPlaySource(playSource)
.setOperationContext("holdPstnParticipant");
client.getCallConnection(callConnectionId).getCallMedia().holdWithResponse(holdOptions, Context.NONE);
/*
// Option 2: Hold without additional options
client.getCallConnection(callConnectionId).getCallMedia().hold(target);
*/
// Option 1: Hold with options
const options = {
playSource: playSource,
operationContext: "holdUserContext",
operationCallbackUrl: "URL" // replace with actual callback URL
};
await callMedia.hold(targetuser, options);
/*
// Option 2: Hold without additional options
await callMedia.hold(targetuser);
*/
# Option 1: Hold without additional options
call_connection_client.hold(target_participant=PhoneNumberIdentifier(TARGET_PHONE_NUMBER))
'''
# Option 2: Hold with options
call_connection_client.hold(
target_participant=PhoneNumberIdentifier(TARGET_PHONE_NUMBER),
play_source=play_source,
operation_context="holdUserContext",
operation_callback_url="URL" # replace with actual callback URL
)
'''
取消保持
通过“取消保持”操作,开发人员可恢复之前暂停的参与者与系统或代理之间的对话。 当参与者被取消保持后,他们将能够再次听到系统或代理的声音。
var unHoldOptions = new UnholdOptions(target)
{
OperationContext = "UnHoldPstnParticipant"
};
// Option 1
var UnHoldParticipant = await callMedia.UnholdAsync(unHoldOptions);
/*
// Option 2
var UnHoldParticipant = await callMedia.UnholdAsync(target);
*/
// Option 1
client.getCallConnection(callConnectionId).getCallMedia().unholdWithResponse(target, "unholdPstnParticipant", Context.NONE);
/*
// Option 2
client.getCallConnection(callConnectionId).getCallMedia().unhold(target);
*/
const unholdOptions = {
operationContext: "unholdUserContext"
};
// Option 1
await callMedia.unhold(target);
/*
// Option 2
await callMedia.unhold(target, unholdOptions);
*/
# Option 1
call_connection_client.unhold(target_participant=PhoneNumberIdentifier(TARGET_PHONE_NUMBER))
'''
# Option 2
call_connection_client.unhold(target_participant=PhoneNumberIdentifier(TARGET_PHONE_NUMBER), operation_context="holdUserContext")
'''
音频流式处理(公共预览版)
使用音频流式处理可以订阅正在进行的通话的实时音频流。 有关如何开始使用音频流式处理的更详细指南以及有关音频流式处理回调事件的信息,请参阅本页 。
实时听录(公共预览版)
使用实时听录可以访问正在进行的通话的音频的实时听录。 有关如何开始使用实时听录的更详细指南以及有关实时听录回调事件的信息,请参阅本页 。
下表说明了在前一个操作仍在运行/排队时允许哪些媒体操作运行/排队。
现有操作
通话支线
允许
已禁止
PlayToAll
主要
PlayToAll、Recognize(Non-Group Call)、PlayTo、Recognize(Group Call)、SendDTMF、StartContinuousDtmfRecognition
无
Recognize(Non-Group Call)
主要
PlayToAll、Recognize(Non-Group Call)、PlayTo、Recognize(Group Call)、SendDTMF、StartContinuousDtmfRecognition
无
PlayTo
Sub
PlayToAll、Recognize(Non-Group Call)
PlayTo、Recognize(Group Call)、SendDTMF、StartContinuousDtmfRecognition
Recognize(Group Call)
Sub
PlayToAll、Recognize(Non-Group Call)
PlayTo、Recognize(Group Call)、SendDTMF、StartContinuousDtmfRecognition
SendDTMF
Sub
PlayToAll、Recognize(Non-Group Call)
PlayTo、Recognize(Group Call)、SendDTMF、StartContinuousDtmfRecognition
StartContinuousDtmfRecognition
Sub
PlayToAll、Recognize(Non-Group Call)、PlayTo、Recognize(Group Call)、SendDTMF、StartContinuousDtmfRecognition
无