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

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

“您好,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. 整合kxmovie

    下载后发现kxmovie 直接运行是不通的,上面我们已经处理了ffmpeg部分的内容.当然了先处理ffmpeg部分.然后再复制kxmovie/kxmovie到项目中导入相应的文件. 编译报错:UIim ...

  2. 渗透模型 Percolation Models(一个物理模型的广泛应用)

    转自:http://mooc.guokr.com/note/15357/ http://mooc.guokr.com/user/0298406005/note/ 模型思想: 有水流下来,是否会渗入地面 ...

  3. 动态inventory脚本的python实现

    #!/usr/bin/env python # -*- coding: utf-8 -*- ''' 基于python的动态inventory脚本实例 ''' import os import sys ...

  4. IPFS中文简介

    ipfs是什么? 它是一个协议也是一个网络,已经运行了2年半,并非虚无缥缈的空气. 它像比特币网络一样,并没有发明什么新技术,他只是将很多种技术(P2P网络技术,bt传输技术,Git版本控制,自证明文 ...

  5. codeforces 465C.No to Palindromes! 解题报告

    题目链接:http://codeforces.com/problemset/problem/464/A 题目意思:给出一个长度为 n 且是 tolerable 的字符串s,需要求出字典序最小的长度也为 ...

  6. HDU2147 kiki's game (SG表找规律)

    Recently kiki has nothing to do. While she is bored, an idea appears in his mind, she just playes th ...

  7. shell---rpm

    [root@master src]# rpm -qpl epel-release-latest-6.noarch.rpm         ##查询该rpm包安装了什么warning: epel-rel ...

  8. [POI 2000] 病毒

    [题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=2938 [算法] 首先建出给定字符串集的AC自动机 存在无限长的代码当且仅当 : AC ...

  9. Tutte矩阵求一般图最大匹配

    [集训队2017论文集] 一张无向图的Tutte矩阵为 其中xi,j为一个random的值. Tutte矩阵的秩(一定为偶数)/2 就是这张图的最大匹配. 原理大概就是: 一个图有完美匹配,则det( ...

  10. eclipse 查找controller

    一.打开eclipse: 二.同时按住Ctrl + Shift + R ; 弹出框如下: 在红色输入框内输入controller 名字即可. 查找控制器里面的方法:Ctrl + O