更新时间:2024-09-24 GMT+08:00
分享

C#

样例

语音回呼场景API获取录音文件下载地址API呼叫状态通知API话单通知API

环境要求

.NET Core 2.0及以上版本或.NET Framework 4.7.1及以上版本。

引用库

Newtonsoft.Json 11.0.2,请参考https://www.newtonsoft.com/json获取。

  • 本文档所述Demo在提供服务的过程中,可能会涉及个人数据的使用,建议您遵从国家的相关法律采取足够的措施,以确保用户的个人数据受到充分的保护。
  • 本文档所述Demo仅用于功能演示,不允许客户直接进行商业使用。
  • 本文档信息仅供参考,不构成任何要约或承诺。

“语音回呼场景API”代码样例

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Cryptography;
using System.Text;

namespace voicecall_csharp_demo_x_aksk_
{
    class VoiceCall
    {
        string base_url = "https://{domain}:{port}"; //APP接入地址,购买服务时下发,请替换为实际值
        string appKey = "***appKey***"; //语音回呼应用的appKey,购买服务时下发,请替换为实际值
        string appSecret = "***appSecret***"; //语音回呼应用的appSecret,购买服务时下发,请替换为实际值

        static void Main(string[] args)
        {
            //主叫号码,被叫号码请替换为实际号码.固话号码从控制台号码管理页获取
            voiceCallAPI("+86531*******4", "+86135*******1", "+86531*******4", "+86135*******2");
        }

        static void voiceCallAPI(string displayNbr, string callerNbr, string displayCalleeNbr, string calleeNbr)
        {
            if (String.IsNullOrEmpty(displayNbr) || String.IsNullOrEmpty(callerNbr) || String.IsNullOrEmpty(displayCalleeNbr) || String.IsNullOrEmpty(calleeNbr))
            {
                return;
            }

            string apiURI = "/rest/httpsessions/click2Call/v2.0"; //接口URI
            string requestUrl = base_url + apiURI;

            try
            {
                //为防止因HTTPS证书认证失败造成API调用失败,需要先忽略证书信任问题
                //.NET Framework 4.7.1及以上版本,请采用如下代码
                var sslHandler = new HttpClientHandler()
                {
                    ServerCertificateCustomValidationCallback = (message, cert, chain, err) => { return true; }
                };
                HttpClient client = new HttpClient(sslHandler, true);
                //低于.NET Framework 4.7.1版本,请采用如下代码
                //HttpClient client = new HttpClient();
                //ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult);

                //请求Headers
                client.DefaultRequestHeaders.Add("Authorization", "AKSK realm=\"SDP\",profile=\"UsernameToken\",type=\"Appkey\"");
                client.DefaultRequestHeaders.Add("X-AKSK", buildAKSKHeader(appKey, appSecret));

                //请求Body
                var body = new Dictionary<string, object>() {
                    /*必填参数*/
                    {"displayNbr", displayNbr},//主叫用户手机终端的来电显示号码。
                    {"callerNbr", callerNbr},//发起呼叫时所使用的主叫号码。
                    {"displayCalleeNbr", displayCalleeNbr},//被叫用户终端的来电显示号码。
                    {"calleeNbr", calleeNbr}//发起呼叫时所拨打的被叫号码。
                    /*选填参数*/

                    //{"maxDuration", 0}, //允许单次通话进行的最长时间
                    //{"lastMinVoice", "lastmin_voice1.wav"}, //最后一分钟放音提示音
                    //{"lastMinToUE", "both"}, //最后一分钟放音的播放对象
                    //{"playPreVoice", "false"}, //设置主叫(callerNbr)应答语音回呼后,呼叫被叫(calleeNbr)前,是否向主叫(callerNbr)播放提示音
                    //{"preVoice", "pre_voice1.wav"}, //设置主叫(callerNbr)应答语音回呼后,呼叫被叫(calleeNbr)前向主叫播放的提示音
                    //{"waitVoice", "wait_voice1.wav"}, //设置主叫应答语音回呼后的等待音

                    //{"calleeMedia", "all"}, //指定被叫的媒体音播放方式
                    //{"statusUrl", ""}, //设置SP接收状态上报的URL,要求使用BASE64编码
                    //{"feeUrl", ""}, //设置SP接收话单上报的URL,要求使用BASE64编码
                    //{"recordFlag", "false"}, //设置语音回呼通话过程是否录音
                    //{"recordHintTone", "recordhint_voice1.wav"}, //设置使用录音功能的提示音
                    //{"partyTypeRequiredInDisconnect", "false"}, //disconnect状态是否需要携带通话主动挂机的用户类型
                    //{"returnIdlePort", "false"}, //指示是否需要返回平台空闲呼叫端口数量
                    //{"userData", "customerId123"} //设置用户的附属信息
                };

                HttpContent content = new StringContent(JsonConvert.SerializeObject(body));
                //请求Headers中的Content-Type参数
                content.Headers.ContentType = new MediaTypeHeaderValue("application/json");

                var response = client.PostAsync(requestUrl, content).Result;
                Console.WriteLine(response.StatusCode);
                var res = response.Content.ReadAsStringAsync().Result;
                Console.WriteLine(res); //打印响应结果
            }
            catch (Exception e)
            {
                Console.WriteLine(e.StackTrace);
                Console.WriteLine(e.Message); //打印错误信息
            }
        }

        static string buildAKSKHeader(string appKey, string appSecret)
        {
            string now = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ"); //Created
            string nonce = Guid.NewGuid().ToString().Replace("-", ""); //Nonce
            String str = nonce + now;

            byte[] keyByte = Encoding.UTF8.GetBytes(appSecret);
            byte[] messageBytes = Encoding.UTF8.GetBytes(str);
            using (var hmacsha256 = new HMACSHA256(keyByte))
            {
                byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
                string base64 = Convert.ToBase64String(hashmessage);
                return String.Format("UsernameToken Username=\"{0}\",PasswordDigest=\"{1}\",Nonce=\"{2}\",Created=\"{3}\"",
                    appKey, base64, nonce, now);
            }
        }

        //低于.NET Framework 4.7.1版本,启用如下方法
        //static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
        //{
        //    return true;
        //}
    }
}

“获取录音文件下载地址API”代码样例

using System;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;

namespace voicecall_csharp_demo_x_aksk_
{
    class GetRecordLink
    {
        string base_url = "https://{domain}:{port}"; //APP接入地址,购买服务时下发,请替换为实际值

        static void Main(string[] args)
        {
            //语音回呼应用的appKey和appSecret,购买服务时下发,请替换为实际值
            string aksk = buildAKSKHeader("***appKey***", "***appSecret***");
            //录音文件名和录音存储服务器域名,从话单通知中获取
            string location = getRecordLinkAPI("1200_366_0_20161228102743.wav", "ostor.huawei.com", aksk);
            //打印录音文件下载地址
            Console.WriteLine($"The record file download link is: {location}");
        }

        static string getRecordLinkAPI(string fileName, string recordDomain, string xaksk)
        {
            if (String.IsNullOrEmpty(fileName) || String.IsNullOrEmpty(recordDomain) || String.IsNullOrEmpty(xaksk))
            {
                return;
            }

            var location;

            string apiURI = "/rest/provision/voice/record/v1.0"; //接口URI

            var keyValues = new NameValueCollection();
            keyValues.Add("fileName", fileName);
            keyValues.Add("recordDomain", recordDomain);

            string requestUrl = base_url + apiURI + "?" + buildQueryString(keyValues);

            try
            {
                //为防止因HTTPS证书认证失败造成API调用失败,需要先忽略证书信任问题
                //.NET Framework 4.7.1及以上版本,请采用如下代码
                var sslHandler = new HttpClientHandler()
                {
                    ServerCertificateCustomValidationCallback = (message, cert, chain, err) => { return true; }
                };
                sslHandler.AllowAutoRedirect = false; //关闭重定向
                HttpClient client = new HttpClient(sslHandler, true);
                //低于.NET Framework 4.7.1版本,请采用如下代码
                //var sslHandler = new HttpClientHandler();
                //sslHandler.AllowAutoRedirect = false;
                //HttpClient client = new HttpClient(sslHandler);
                //ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult);

                //请求Headers
                client.DefaultRequestHeaders.Add("Authorization", "AKSK realm=\"SDP\",profile=\"UsernameToken\",type=\"Appkey\"");
                client.DefaultRequestHeaders.Add("X-AKSK", xaksk);

                var response = client.GetAsync(requestUrl).Result; //GET请求
                if (response.StatusCode.Equals(301))
                {
                    location = Convert.ToString(response.Headers.GetValues("Location"));
                }
                else
                {
                    var res = response.Content.ReadAsStringAsync().Result;
                    Console.WriteLine(response.StatusCode);
                    Console.WriteLine(res); //打印响应结果
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.StackTrace);
                Console.WriteLine(e.Message); //打印错误信息
            }

            return location;
        }

        static string buildAKSKHeader(string appKey, string appSecret)
        {
            string now = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ"); //Created
            string nonce = Guid.NewGuid().ToString().Replace("-", ""); //Nonce
            String str = nonce + now;

            byte[] keyByte = Encoding.UTF8.GetBytes(appSecret);
            byte[] messageBytes = Encoding.UTF8.GetBytes(str);
            using (var hmacsha256 = new HMACSHA256(keyByte))
            {
                byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
                string base64 = Convert.ToBase64String(hashmessage);
                return String.Format("UsernameToken Username=\"{0}\",PasswordDigest=\"{1}\",Nonce=\"{2}\",Created=\"{3}\"",
                    appKey, base64, nonce, now);
            }
        }

        static string buildQueryString(NameValueCollection keyValues)
        {
            StringBuilder temp = new StringBuilder();
            foreach (string item in keyValues.Keys)
            {
                temp.Append(item).Append("=").Append(WebUtility.UrlEncode(keyValues.Get(item))).Append("&");
            }
            return temp.Remove(temp.Length - 1, 1).ToString();
        }

        //低于.NET Framework 4.7.1版本,启用如下方法
        //static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
        //{
        //    return true;
        //}
    }
}

“呼叫状态通知API”代码样例

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;

namespace voicecall_csharp_demo_x_aksk_
{
    class CallEventImpl
    {
        static void Main(string[] args)
        {
            //呼叫事件通知样例
            string jsonBody = JsonConvert.SerializeObject(new Dictionary<string, object>(){
                {"eventType", "callout"},
                {"statusInfo", new Dictionary<string, object>(){
                        {"sessionId", "1201_612_4294967295_20190124030424@callenabler245.huaweicaas.com"},
                        {"timestamp", "2019-01-24 03:04:24"},
                        {"caller", "+86138*******2"},
                        {"called", "+86138*******1"},
                    }
                }
            });

            Console.WriteLine("jsonBody:" + jsonBody);

            OnCallEvent(jsonBody); //呼叫事件处理
        }

        /// <summary>
        /// 呼叫事件通知,详细内容以接口文档为准
        /// </summary>
        /// <param name="jsonBody"></param>
        static void OnCallEvent(string jsonBody)
        {
            JObject jsonObj = (JObject)JsonConvert.DeserializeObject(jsonBody); //将通知消息解析为jsonObj
            string eventType = jsonObj["eventType"].ToString(); //通知事件类型

            if ("fee".Equals(eventType))
            {
                Console.WriteLine("EventType error:" + eventType);
                return;
            }

            if (!jsonObj.ContainsKey("statusInfo"))
            {
                Console.WriteLine("param error: no statusInfo.");
                return;
            }
            JObject statusInfo = (JObject)jsonObj["statusInfo"]; //呼叫状态事件信息

            Console.WriteLine("eventType:" + eventType); //打印通知事件类型

            //callout:呼出事件
            if ("callout".Equals(eventType))
            {
                /**
                 * Example: 此处以解析sessionId为例,请按需解析所需参数并自行实现相关处理
                 *
                 * 'timestamp': 该呼叫事件发生时语音通话平台的UNIX时间戳
                 * 'userData': 用户附属信息
                 * 'sessionId': 通话链路的标识ID
                 * 'caller': 主叫号码
                 * 'called': 被叫号码
                 */
                if (statusInfo.ContainsKey("sessionId"))
                {
                    Console.WriteLine("sessionId:" + statusInfo["sessionId"]);
                }
                return;
            }
            //alerting:振铃事件
            if ("alerting".Equals(eventType))
            {
                /**
                 * Example: 此处以解析sessionId为例,请按需解析所需参数并自行实现相关处理
                 *
                 * 'timestamp': 该呼叫事件发生时语音通话平台的UNIX时间戳
                 * 'userData': 用户附属信息
                 * 'sessionId': 通话链路的标识ID
                 * 'caller': 主叫号码
                 * 'called': 被叫号码
                 */
                if (statusInfo.ContainsKey("sessionId"))
                {
                    Console.WriteLine("sessionId:" + statusInfo["sessionId"]);
                }
                return;
            }
            //answer:应答事件
            if ("answer".Equals(eventType))
            {
                /**
                 * Example: 此处以解析sessionId为例,请按需解析所需参数并自行实现相关处理
                 *
                 * 'timestamp': 该呼叫事件发生时语音通话平台的UNIX时间戳
                 * 'userData': 用户附属信息
                 * 'sessionId': 通话链路的标识ID
                 * 'caller': 主叫号码
                 * 'called': 被叫号码
                 */
                if (statusInfo.ContainsKey("sessionId"))
                {
                    Console.WriteLine("sessionId:" + statusInfo["sessionId"]);
                }
                return;
            }
            //collectInfo:放音收号结果事件,仅应用于语音通知场景
            if ("collectInfo".Equals(eventType))
            {
                /**
                 * Example: 此处以解析digitInfo为例,请按需解析所需参数并自行实现相关处理
                 *
                 * 'timestamp': 该呼叫事件发生时语音通话平台的UNIX时间戳
                 * 'sessionId': 通话链路的标识ID
                 * 'digitInfo': 放音收号场景中,语音通话平台对开发者进行放音收号操作的结果描述
                 */
                if (statusInfo.ContainsKey("digitInfo"))
                {
                    Console.WriteLine("digitInfo:" + statusInfo["digitInfo"]);
                }
                return;
            }
            //disconnect:挂机事件
            if ("disconnect".Equals(eventType))
            {
                /**
                 * Example: 此处以解析sessionId为例,请按需解析所需参数并自行实现相关处理
                 *
                 * 'timestamp': 该呼叫事件发生时语音通话平台的UNIX时间戳
                 * 'userData': 用户附属信息
                 * 'sessionId': 通话链路的标识ID
                 * 'caller': 主叫号码
                 * 'called': 被叫号码
                 * 'partyType': 挂机的用户类型,仅在语音回呼场景携带
                 * 'stateCode': 通话挂机的原因值
                 * 'stateDesc': 通话挂机的原因值的描述
                 */
                if (statusInfo.ContainsKey("sessionId"))
                {
                    Console.WriteLine("sessionId:" + statusInfo["sessionId"]);
                }
                return;
            }
        }
    }
}

“话单通知API”代码样例

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;

namespace voicecall_csharp_demo_x_aksk_
{
    class FeeImpl
    {
        static void Main(string[] args)
        {
            //话单通知样例
            string jsonBody = JsonConvert.SerializeObject(new Dictionary<string, object>(){
                {"eventType", "fee"},
                {"feeLst",  new object[] {
                    new Dictionary<string, object>(){
                            {"direction", 0}, //通话的呼叫方向,以语音通话平台为基准
                            {"spId", "CaaS_Test_01"}, //客户的云服务账号
                            {"appKey", "ka4k*****Em2"}, //应用的app_key,购买服务时下发,请替换为实际值
                            {"icid", "CAE-20190124110424-12019775"}, //呼叫记录的唯一标识
                            {"bindNum", "+8675512****78"}, //发起此次呼叫的CallEnabler业务号码,即绑定号码
                            {"sessionId", "1201_612_4294967295_20190124030424@callenabler245.huaweicaas.com"}, //通话链路的唯一标识
                            {"callerNum", "+86138****0022"}, //主叫号码
                            {"calleeNum", "+86138****0021"}, //被叫号码
                            {"fwdDisplayNum", "+86138****0022"}, //转接呼叫时的显示号码(仅语音回呼场景携带)
                            {"fwdDstNum", "+86138****7021"}, //转接呼叫时的转接号码(仅语音回呼场景携带)

                            {"fwdStartTime", "2019-01-24 03:04:31"}, //转接呼叫操作的开始时间(仅语音回呼场景携带)
                            {"fwdAlertingTime", "2019-01-24 03:04:36"}, //转接呼叫操作后的振铃时间(仅语音回呼场景携带)
                            {"fwdAnswerTime", "2019-01-24 03:04:38"}, //转接呼叫操作后的应答时间(仅语音回呼场景携带)
                            {"callEndTime", "2019-01-24 03:04:49"}, //呼叫结束时间
                            {"fwdUnaswRsn", 0}, //转接呼叫操作失败的Q850原因值
                            {"failTime", ""}, //呼入,呼出的失败时间
                            {"ulFailReason", 0}, //通话失败的拆线点
                            {"sipStatusCode", 0}, //呼入,呼出的失败SIP状态码
                            {"callOutStartTime", "2019-01-24 03:04:24"}, //Initcall的呼出开始时间
                            {"callOutAlertingTime", "2019-01-24 03:04:27"}, //Initcall的呼出振铃时间
                            {"callOutAnswerTime", "2019-01-24 03:04:31"}, //Initcall的呼出应答时间
                            {"callOutUnaswRsn", 0}, //Initcall的呼出失败的Q850原因值
                            {"dynIVRStartTime", ""}, //自定义动态IVR开始时间(仅语音通知场景携带)
                            {"dynIVRPath", ""}, //自定义动态IVR按键路径(仅语音通知场景携带)
                            {"recordFlag", 0}, //录音标识
                            {"recordStartTime", ""}, //录音开始时间(仅语音回呼场景携带)
                            {"recordObjectName", ""}, //录音文件名(仅语音回呼场景携带)
                            {"recordBucketName", ""}, //录音文件所在的目录名(仅语音回呼场景携带)
                            {"recordDomain", ""}, //存放录音文件的域名(仅语音回呼场景携带)
                            {"recordFileDownloadUrl", ""}, //录音文件下载地址(仅语音回呼场景携带)
                            {"ttsPlayTimes", 0}, //应用TTS功能时,使用TTS的总次数
                            {"ttsTransDuration", 0}, //应用TTS功能时,TTS Server进行TTS转换的总时长(单位为秒)
                            {"serviceType", "002"}, //携带呼叫的业务类型信息
                            {"hostName", "callenabler245.huaweicaas.com"}, //话单生成的服务器设备对应的主机名
                            {"userData", "customerId123"} //用户附属信息
                        }
                    }
                }
            });

            Console.WriteLine(jsonBody);

            OnFeeEvent(jsonBody); //话单处理
        }

        /// <summary>
        /// 话单通知,详细内容以接口文档为准
        /// </summary>
        /// <param name="jsonBody"></param>
        static void OnFeeEvent(string jsonBody)
        {
            JObject jsonObj = (JObject)JsonConvert.DeserializeObject(jsonBody); //将通知消息解析为jsonObj
            string eventType = jsonObj["eventType"].ToString(); //通知事件类型

            if (!"fee".Equals(eventType))
            {
                Console.WriteLine("EventType error:" + eventType);
                return;
            }

            if (!jsonObj.ContainsKey("feeLst"))
            {
                Console.WriteLine("param error: no feeLst.");
                return;
            }
            JObject[] feeLst = jsonObj["feeLst"].ToObject<JObject[]>(); //呼叫话单事件信息

            //Example: 此处以解析sessionId为例,请按需解析所需参数并自行实现相关处理
            //短时间内有多个通话结束时语音通话平台会将话单合并推送,每条消息最多携带50个话单
            if (feeLst.Length > 1)
            {
                foreach (JObject loop in feeLst)
                {
                    if (loop.ContainsKey("sessionId"))
                    {
                        Console.WriteLine("sessionId:" + loop["sessionId"]);
                    }
                }
            }
            else if (feeLst.Length == 1)
            {
                if (feeLst[0].ContainsKey("sessionId"))
                {
                    Console.WriteLine("sessionId:" + feeLst[0]["sessionId"]);
                }
            }
            else
            {
                Console.WriteLine("feeLst error: no element.");
            }
        }
    }
}

相关文档