最近公司要上线语音通知功能,需求如下:

场景:发生报警时,自动通知到指定的手机号,同时,提供几个按键选项,例如,语音通知如下:

“您好,XXX小区发生XXXX报警,按1确认报警,按2忽略报警,按3屏蔽报警,暂不处理请挂机”

->用户按1,播放:您的确认请求已提交,处理结果稍候将以短信形式通知您 ->通过呼叫状态API识别按键1 ->执行操作A

->用户按2,播放:您的忽略请求已提交,处理结果稍候将以短信形式通知您 ->通过呼叫状态API识别按键2 ->执行操作B

->用户按3,播放:您的屏蔽请求已提交,处理结果稍候将以短信形式通知您 ->通过呼叫状态API识别按键3 ->执行操作C

其实主要就是要识别用户的按键,有的供应商叫 DTMF,有的叫交互式语音通知,有的叫IvrCall,就这个功能点,调查了大大小小几家供应商,要么嫌弃我们发送量太少,要么不提供报警类的语音通知,要么对接的技术人员不专业,最后还是,选择了华为云,理由如下:

  1. 相信华为多年来在通信领域的能力,在语音/短信等方面应该是有优势的;

  2. 华为云语音通知可提供96开头的号码,让我们的业务更正规;

  3. 华为云语音通知VoiceCall对接的技术人员是最专业的;

在官网提交了申请之后,一个工作日内,工作人员就联系了我,确认了相关需求可实现,同时详细介绍了开发流程和费用等问题,之后半小时之内就收到了测试环境邮件。

根据我们的需求,主要使用了 :

大客户简单授权API:https://support.huaweicloud.com/api-VoiceCall/rtc_05_0002.html

语音通知API:https://support.huaweicloud.com/api-VoiceCall/rtc_05_0013.html

呼叫状态和话单API:https://support.huaweicloud.com/api-VoiceCall/rtc_05_0014.html

其中在处理呼叫状态和话单API时,遇到了些问题,没想到华为云技术支持人员在晚上9点多依然回复了邮件,要知道,这只是调试阶段,能这么及时的相应,也是要点赞的。

经过两天的调试,我们基本上已经完成了开发,准备提交商用了,稍稍总结下,到目前为止,选择华为云VoiceCall是正确的选择。

最后,因为我们是.NET CORE环境开发,华为云官网并未提供DEMO,因此我会把我们的代码整理下,发出来供.NET 的客户参考。

发送测试语音:

        /// <summary>
/// 发送测试语音短信,test_template01_kuaidi,您有$[NUM_2]件快递请到$[TXT_32]领取
/// </summary>
/// <param name="PhoneNum">逗号分隔的电话号码</param>
/// <returns></returns>
[HttpPost]
[ActionName("SendVoiceCall")]
public OperatedResult<List<SendVoiceCallResult>> SendNormalVoiceCall(string PhoneNum)
{
try
{
List<SendVoiceCallResult> listResult = new List<SendVoiceCallResult>();
int successCount = ; string numstr = PhoneNum;
List<string> ListPhones = new List<string>();
var nums = numstr.Split(',').ToList();
nums.ForEach(x =>
{
//固定电话 以 区号开头的 例如:+8602165522102,需要改为 +862165522102
if (x.StartsWith("+860"))
{
x = "+86" + x.Remove(, );
}
//固定电话 以区号开头,去掉0
if (x.StartsWith(""))
{
x = "+86" + x.Remove(, );
}
//11位手机号
if (x.Length == && x.StartsWith(""))
{
x = "+86" + x;
}
ListPhones.Add(x);
}); ListPhones.ForEach(x =>
{ string Url = $"{_appSettings.Value.BaseUrl}:{_appSettings.Value.Port}{_appSettings.Value.SendVoiceCallUrl}?app_key={_appSettings.Value.AppKey}&access_token={SpTokenHelper.HuaWeiSpTokenInfo.AccessToken}&format=json";
Dictionary<string, string> header = new Dictionary<string, string>();
header.Add("ContentType", "application/json; charset=UTF-8");
VoiceCallRequestBody body = new VoiceCallRequestBody();
body.bindNbr = _appSettings.Value.BindNbr;
body.displayNbr = _appSettings.Value.DisplayNbr;
body.calleeNbr = x;
//您有$[3]件快递请到$[人民公园]领取,按9重听。
body.playInfoList = new PlayContentInfo[] { new PlayContentInfo() { templateParas = new string[] {"","人民公园" }, templateId = "test_template01_kuaidi", collectInd = , collectContentTriggerReplaying = "", replayAfterCollection = "true" } };
body.returnIdlePort = "true";
body.userData =Guid.NewGuid().ToString();
body.statusUrl = _appSettings.Value.StatusUrl.ToBase64Str();
body.feeUrl = _appSettings.Value.FeeUrl.ToBase64Str();
var response = HttpHelper.HttpPostAsync(Url, JsonConvert.SerializeObject(body), header, null, "application/json").Result; SendVoiceCallResponse re = JsonConvert.DeserializeObject<SendVoiceCallResponse>(response);
_logger.LogInformation($"PhoneNum:{x},,ResultCode:{re.resultcode},ResultDesc{re.resultdesc},SessionID:{re.sessionid},IdlePort:{re.idleport}");
listResult.Add(new SendVoiceCallResult() { Phone = x, Response = re, AlarmContent = JsonConvert.SerializeObject(body) });
if (re.resultcode == "")
{
successCount++;
} }); return OperatedResult<List<SendVoiceCallResult>>.Success($"共{ListPhones.Count}条呼叫,发送成功{successCount}条", listResult);
}
catch (Exception ex)
{ _logger.LogError("SendNormalVoiceCall失败:" + ex.Message);
return OperatedResult<List<SendVoiceCallResult>>.Fail($"发起语音呼叫失败");
} }

回调函数:

        /// <summary>
/// 华为云VoiceCall呼叫状态回复
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[HttpPost]
[ActionName("StatusCallBack")]
public IActionResult StatusCallBack(StatusRequestCallBackInfo request)
{
_logger.LogDebug(JsonConvert.SerializeObject(request)); return Ok(new StatusResponse() {resultdesc="",resultcode="" });
}

大客户认证:

        /// <summary>
/// 从华为云获取Token
/// </summary>
/// <returns></returns>
public OperatedResult<HuaWeiSpTokenInfo> GetSpToken()
{
string Url = $"{_appSettings.Value.BaseUrl}:{_appSettings.Value.Port}{_appSettings.Value.GetTokenUrl}?app_key={_appSettings.Value.AppKey}&username={_appSettings.Value.UserName}&format=json"; Dictionary<string, string> header = new Dictionary<string, string>();
header.Add("ContentType", "application/x-www-form-urlencoded; charset=UTF-8");
header.Add("Accept", "*/*");
// header.Add("Authorization", _appSettings.Value.UserPsw); var response = HttpHelper.HttpPostAsync(Url, null, header,_appSettings.Value.UserPsw).Result; SpGetTokenResponse re = JsonConvert.DeserializeObject<SpGetTokenResponse>(response);
if (re.resultcode == "")
{
//获取成功
HuaWeiSpTokenInfo.AccessToken = re.access_token;
HuaWeiSpTokenInfo.RefreshToken = re.refresh_token;
HuaWeiSpTokenInfo.ExpiresIn = re.expires_in; HuaWeiSpTokenInfo.TokenStartTime = DateTime.Now;
HuaWeiSpTokenInfo.TokenEndTime = DateTime.Now.AddSeconds( Convert.ToInt32( re.expires_in)); return OperatedResult<HuaWeiSpTokenInfo>.Success(HuaWeiSpTokenInfo);
}
else
{
SpGetTokenResponseCode code = new SpGetTokenResponseCode();
code.list.Where(x => x.Code == re.resultcode).FirstOrDefault(); return OperatedResult<HuaWeiSpTokenInfo>.Fail($"执行失败:ResultCode:{re.resultcode},ResultDesc:{re.resultdesc}");
}
}

刷新Token:

        /// <summary>
/// 从华为云刷新Token
/// </summary>
/// <returns></returns>
public OperatedResult<HuaWeiSpTokenInfo> RefreshSpToken()
{
string Url = $"{_appSettings.Value.BaseUrl}:{_appSettings.Value.Port}{_appSettings.Value.RefreshTokenUrl}"; Dictionary<string, string> header = new Dictionary<string, string>();
//header.Add("ContentType", "application/x-www-form-urlencoded; charset=UTF-8");
header.Add("Accept", "*/*"); string PostData = $"grant_type=refresh_token&refresh_token={HuaWeiSpTokenInfo.RefreshToken}&app_key={_appSettings.Value.AppKey}&app_secret={_appSettings.Value.AppSecret}"; var response = HttpHelper.HttpPostAsync(Url, PostData, header,null, "application/x-www-form-urlencoded").Result; SpRefreshTokenResponse re = JsonConvert.DeserializeObject<SpRefreshTokenResponse>(response);
if (re.resultcode == "")
{
//获取成功
HuaWeiSpTokenInfo.AccessToken = re.access_token;
HuaWeiSpTokenInfo.RefreshToken = re.refresh_token;
HuaWeiSpTokenInfo.ExpiresIn = re.expires_in; HuaWeiSpTokenInfo.TokenStartTime = DateTime.Now;
HuaWeiSpTokenInfo.TokenEndTime = DateTime.Now.AddSeconds(Convert.ToInt32(re.expires_in)); return OperatedResult<HuaWeiSpTokenInfo>.Success(HuaWeiSpTokenInfo);
}
else
{
//获取失败
SpRefreshTokenResponseCode code = new SpRefreshTokenResponseCode();
code.list.Where(x => x.Code == re.resultcode).FirstOrDefault(); return OperatedResult<HuaWeiSpTokenInfo>.Fail($"执行失败:ResultCode:{re.resultcode},ResultDesc:{re.resultdesc}");
}
}

注销Token:

        /// <summary>
/// 从华为云注销Token
/// </summary>
/// <returns></returns>
public OperatedResult<bool> CancleSpToken()
{
string Url = $"{_appSettings.Value.BaseUrl}:{_appSettings.Value.Port}{_appSettings.Value.CancleTokenUrl}?app_key={_appSettings.Value.AppKey}&access_token={HuaWeiSpTokenInfo.AccessToken}"; Dictionary<string, string> header = new Dictionary<string, string>();
header.Add("ContentType", "application/x-www-form-urlencoded; charset=UTF-8");
header.Add("Accept", "*/*"); var response = HttpHelper.HttpPostAsync(Url, null, header).Result; SpCancleTokenResponse re = JsonConvert.DeserializeObject<SpCancleTokenResponse>(response);
if (re.resultcode == "")
{
//获取成功
HuaWeiSpTokenInfo.AccessToken = string.Empty;
HuaWeiSpTokenInfo.RefreshToken = string.Empty;
HuaWeiSpTokenInfo.ExpiresIn = ""; HuaWeiSpTokenInfo.TokenStartTime = DateTime.MinValue;
HuaWeiSpTokenInfo.TokenEndTime = DateTime.MinValue; return OperatedResult<bool>.Success();
}
else
{
//失败
SpCancleTokenResponseCode code = new SpCancleTokenResponseCode();
code.list.Where(x => x.Code == re.resultcode).FirstOrDefault(); return OperatedResult<bool>.Fail($"执行失败:ResultCode:{re.resultcode},ResultDesc:{re.resultdesc}");
}
}

Post请求:

        /// <summary>
/// 异步POST请求
/// </summary>
/// <param name="url"></param>
/// <param name="postData"></param>
/// <param name="headers"></param>
/// <param name="contentType"></param>
/// <param name="customToken"></param>
/// <param name="timeout">请求响应超时时间,单位/s(默认100秒)</param>
/// <param name="encoding">默认UTF8</param>
/// <returns></returns>
public static async Task<string> HttpPostAsync(string url, string postData, Dictionary<string, string> headers = null,string customToken=null, string contentType = null, int timeout = , Encoding encoding = null)
{
using (HttpClient client = new HttpClient(new HttpClientHandler() { ServerCertificateCustomValidationCallback = (a, b, c, d) => true }))
{
if (headers != null)
{
foreach (KeyValuePair<string, string> header in headers)
{
client.DefaultRequestHeaders.Add(header.Key, header.Value); }
}
if(customToken!=null)
{
client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", customToken);
} if (timeout > )
{
client.Timeout = new TimeSpan(, , timeout);
} using (HttpContent content = new StringContent(postData ?? "", encoding ?? Encoding.UTF8))
{
if (contentType != null)
{
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(contentType);
}
using (HttpResponseMessage responseMessage = await client.PostAsync(url, content))
{
Byte[] resultBytes = await responseMessage.Content.ReadAsByteArrayAsync();
return Encoding.UTF8.GetString(resultBytes);
}
}
}
}
}

基于华为云语音通知 VoiceCall 的应用上线记录并分享.NET CORE DEMO的更多相关文章

  1. 基于华为云IOT及无线RFID技术的智慧仓储解决方案最佳实践系列一

    [摘要]仓储管理存在四大细分场景:出入库管理.盘点.分拣和货物跟踪.本系列将介绍利用华为云IOT全栈云服务,端侧采用华为收发分离式RFID解决方案,打造端到端到IOT智慧仓储解决方案的最佳实践. 仓储 ...

  2. 基于华为云IoT Studio自助生成10万行代码的奥秘

    华为IoT小助手们搬好板凳.备好笔记本.听了HDC.Cloud的几场华为云技术架构师的直播讲课,感觉获益匪浅却又似懂非懂,直后悔自己没有好好打下基础.为了避免再次出现这样的情况,小助手偷偷跑去找了华为 ...

  3. 基于华为云服务器的FTP站点搭建

    前言 主要介绍了华为云上如何使用弹性云服务器的Linux实例使用vsftpd软件搭建FTP站点.vsftpd全称是"very secure FTP daemon",是一款在Linu ...

  4. 基于华为云CSE微服务接口兼容常见问题

    微服务接口兼容常见问题 在进行微服务持续迭代开发的过程中,由于新特性在不停的加入,一些过时的特性在不停的修改,接口兼容问题面临巨大的挑战,特别是在运行环境多版本共存(灰度发布)的情况下.本章节主要描述 ...

  5. 重磅!普惠AI--华为云语音语义万次调用1元购,有奖问答@评论区等你来!【华为云技术分享】

    活动快速入口:https://activity.huaweicloud.com/language_speech_promotion0.html 语音交互与自然语言处理 语音交互是一种人机交互方式,以开 ...

  6. 聊聊如何在华为云IoT平台进行产品开发

    摘要:华为云物联网平台承载着南北向数据互通的功能职责. 本文分享自华为云社区<如何基于华为云IoT物联网平台进行产品开发>,作者: Super.雯 . 华为云物联网平台承载着南北向数据互通 ...

  7. 性能达到原生 MySQL 七倍,华为云 Taurus 技术解读【华为云技术分享】

    版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/devcloud/article/detai ...

  8. 走近深度学习,认识MoXing:初识华为云ModelArts的王牌利器 — MoXing

    [摘要] 本文为MoXing系列文章第一篇,主要介绍什么是MoXing,MoXing API的优势以及MoXing程序的基本结构. MoXing的概念 MoXing是华为云深度学习服务提供的网络模型开 ...

  9. 华为云Volcano:让企业AI算力像火山一样爆发

    欢迎添加华为云小助手微信(微信号:HWCloud002 或 HWCloud003),输入关键字"加群",加入华为云线上技术讨论群:输入关键字"最新活动",获取华 ...

随机推荐

  1. Android 如何永久性开启adb 的root权限【转】

    本文转载自:https://www.2cto.com/kf/201702/593999.html adb 的root 权限是在system/core/adb/adb.c 中控制.主要根据ro.secu ...

  2. “cannot be resolved to a type” 错误解决方法

    (1)jdk不匹配(或不存在) 项目指定的jdk为“jdk1.6.0_18”,而当前eclipse使用的是“jdk1.6.0_22”.需要在BuildPath | Libraries,中做简单调整. ...

  3. 基于BASYS2的VHDL程序——交通灯(状态机版)

    请尊重作者版权,转载注明源地址:http://www.cnblogs.com/connorzx/p/3694618.html 使用了状态机,增加了可读性和用户体验. library IEEE; use ...

  4. MYSQL进阶学习笔记十三:MySQL 内存优化!(视频序号:进阶_31)

    知识点十四:MySQL 内存的优化(31) 一.优化MySQL SERVER 7组后台进程: masterthread:主要负责将脏缓存页刷新到数据文件,执行purge操作,触发检查点,合并插入缓冲区 ...

  5. php-循环普通数组和关联数组

    <?php //循环普通数组 $arr=array("杭州","成都","拉萨"); $arrlength=count($arr); ...

  6. HDU3666 THE MATRIX PROBLEM (差分约束+取对数去系数)(对退出情况存疑)

    You have been given a matrix C N*M, each element E of C N*M is positive and no more than 1000, The p ...

  7. hadoop2.X集群安装与应用

    可参考此文档:hadoop(2.x)以hadoop2.2为例完全分布式最新高可靠安装文档(非常详细)http://www.aboutyun.com/thread-7684-1-1.html 步骤一:下 ...

  8. Win32编程点滴5 - 响应ActiveX控件的事件

    在最近的一篇文章中说到了,如何创建ActiveX,这次我们来响应事件.这次,我们将创建一个类:CGeneralEventSink,它能够响应任何Dispatch事件(事件的接口继承与IDispatch ...

  9. HDU1150(最小顶点覆盖)

    Machine Schedule Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) ...

  10. 你忘记的java的数据类型信息

    java有8种基本数据类型 int long short byte float double char boolean: 三种情况造成数据溢出 无穷大,无穷小, NAN: 常量 声明为final的变量 ...