基于华为云语音通知 VoiceCall 的应用上线记录并分享.NET CORE DEMO
最近公司要上线语音通知功能,需求如下:
场景:发生报警时,自动通知到指定的手机号,同时,提供几个按键选项,例如,语音通知如下:
“您好,XXX小区发生XXXX报警,按1确认报警,按2忽略报警,按3屏蔽报警,暂不处理请挂机”
->用户按1,播放:您的确认请求已提交,处理结果稍候将以短信形式通知您 ->通过呼叫状态API识别按键1 ->执行操作A
->用户按2,播放:您的忽略请求已提交,处理结果稍候将以短信形式通知您 ->通过呼叫状态API识别按键2 ->执行操作B
->用户按3,播放:您的屏蔽请求已提交,处理结果稍候将以短信形式通知您 ->通过呼叫状态API识别按键3 ->执行操作C
其实主要就是要识别用户的按键,有的供应商叫 DTMF,有的叫交互式语音通知,有的叫IvrCall,就这个功能点,调查了大大小小几家供应商,要么嫌弃我们发送量太少,要么不提供报警类的语音通知,要么对接的技术人员不专业,最后还是,选择了华为云,理由如下:
相信华为多年来在通信领域的能力,在语音/短信等方面应该是有优势的;
华为云语音通知可提供96开头的号码,让我们的业务更正规;
华为云语音通知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的更多相关文章
- 基于华为云IOT及无线RFID技术的智慧仓储解决方案最佳实践系列一
[摘要]仓储管理存在四大细分场景:出入库管理.盘点.分拣和货物跟踪.本系列将介绍利用华为云IOT全栈云服务,端侧采用华为收发分离式RFID解决方案,打造端到端到IOT智慧仓储解决方案的最佳实践. 仓储 ...
- 基于华为云IoT Studio自助生成10万行代码的奥秘
华为IoT小助手们搬好板凳.备好笔记本.听了HDC.Cloud的几场华为云技术架构师的直播讲课,感觉获益匪浅却又似懂非懂,直后悔自己没有好好打下基础.为了避免再次出现这样的情况,小助手偷偷跑去找了华为 ...
- 基于华为云服务器的FTP站点搭建
前言 主要介绍了华为云上如何使用弹性云服务器的Linux实例使用vsftpd软件搭建FTP站点.vsftpd全称是"very secure FTP daemon",是一款在Linu ...
- 基于华为云CSE微服务接口兼容常见问题
微服务接口兼容常见问题 在进行微服务持续迭代开发的过程中,由于新特性在不停的加入,一些过时的特性在不停的修改,接口兼容问题面临巨大的挑战,特别是在运行环境多版本共存(灰度发布)的情况下.本章节主要描述 ...
- 重磅!普惠AI--华为云语音语义万次调用1元购,有奖问答@评论区等你来!【华为云技术分享】
活动快速入口:https://activity.huaweicloud.com/language_speech_promotion0.html 语音交互与自然语言处理 语音交互是一种人机交互方式,以开 ...
- 聊聊如何在华为云IoT平台进行产品开发
摘要:华为云物联网平台承载着南北向数据互通的功能职责. 本文分享自华为云社区<如何基于华为云IoT物联网平台进行产品开发>,作者: Super.雯 . 华为云物联网平台承载着南北向数据互通 ...
- 性能达到原生 MySQL 七倍,华为云 Taurus 技术解读【华为云技术分享】
版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/devcloud/article/detai ...
- 走近深度学习,认识MoXing:初识华为云ModelArts的王牌利器 — MoXing
[摘要] 本文为MoXing系列文章第一篇,主要介绍什么是MoXing,MoXing API的优势以及MoXing程序的基本结构. MoXing的概念 MoXing是华为云深度学习服务提供的网络模型开 ...
- 华为云Volcano:让企业AI算力像火山一样爆发
欢迎添加华为云小助手微信(微信号:HWCloud002 或 HWCloud003),输入关键字"加群",加入华为云线上技术讨论群:输入关键字"最新活动",获取华 ...
随机推荐
- Android 启动过程介绍【转】
本文转载自:http://blog.csdn.net/yangwen123/article/details/8023654 一般开机过程大致可以分为三个大阶段: 1. OS级别,由bootloader ...
- charles抓取线上接口数据替换为本地json格式数据
最近要做下拉刷新,无奈测试服务器的测试数据太少,没有足够的数据做下拉刷新,所以用charles抓取了测试服务器的接口,然后在伪造了很多数据返回到我的电脑上,下面来说说使用方法: 第一步: 安装FQ软件 ...
- VK Cup 2015 - Round 2 E. Correcting Mistakes —— 字符串
题目链接:http://codeforces.com/contest/533/problem/E E. Correcting Mistakes time limit per test 2 second ...
- TP框架中的多种方法代码(C,G,L,T,I,N,D,M,A,R,B,U,W,S,F,E)
C方法 function C($name=null, $value=null,$default=null) { static $_config = array(); // 无参数时获取所有 if (e ...
- cassandra 存储list数组
demo如下: CREATE TABLE users3 ( user_id text PRIMARY KEY, first_name text, last_name text, emails list ...
- hdu 6058
\(f(l,r,k)=\)区间[\(l\),\(r\)]的第k大. \(\sum_{l=1}^{n}{\sum_{r=l}^{n}{f(l,r,k)}}\) 参考题解,claris大佬题解.赛后AC. ...
- 使用grunt中遇到的问题
1.使用jshint进行代码检查时,grunt命令后报错: 因为出现了乱码,我猜测是因为编码原因导致的.遂在webstorm的setting中修改了编码为utf-8,问题解决.
- [C++]变量存储类别,指针和引用,类与对象,继承与派生的一些摘要
C++中共有四种存储类别标识符:auto/static/register/extern 1.auto 函数或分程序内定义的变量(包括形参)可以定义为auto(自动变量).如果不指定存储类别,则隐式定义 ...
- vue-router 安装
如果在一个模块化工程中使用它,需要通过Vue.use() 明确的安装路由功能,如果使用全局的script标签,则不需要手动安装. Vue Router是Vue.js官方的路由管理器.它和Vue.js的 ...
- hibernate学习三 精解Hibernate之核心文件
一 hibernate.cfg.xml详解 1 JDBC连接: 2 配置C3P0连接池: 3 配置JNDI数据源: 4 可选的配置属性: 5 hibernate二级缓存属性 6 hibernate事务 ...