在之前介绍的火车票查询工具中,利用邮件和短信将查询结果推送给用户。免费短信的条数只有5条,用完之后只能单独使用邮件提醒。
最近发现腾讯云的福利,简单的介绍一下用法。
腾讯云-》产品-》通信服务-》短信-》开通服务-》添加应用-》创建签名和模板-》等待审核通过-》按照Demo测试
在整个流程中,最耗时的就是创建签名和模板,对个人来说只能选择app、公众号或小程序的方式进行申请,多亏之前折腾过一点小程序,从而闯关成功。
接下来查看官方Demo https://github.com/qcloudsms/qcloudsms/tree/master/demo/csharp
整理后的测试Demo
调用代码:
1 using QcloudSms; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace ConsoleTest 9 { 10 class SmsSDKDemo 11 { 12 /// <summary> 13 /// appId 14 /// </summary> 15 private static int appId = 140xxxxx; 16 /// <summary> 17 /// appKey 18 /// </summary> 19 private static string appKey = "xxxxxxxxxxxxxxx"; 20 /// <summary> 21 /// 接收手机号 22 /// </summary> 23 private static string phoneNumber = "xxxxxxxx"; 24 /// <summary> 25 /// 短信模板ID 26 /// </summary> 27 private static int tmplateId = xxxxxxx; 28 29 static void Main(string[] args) 30 { 31 try 32 { 33 SmsSingleSenderResult singleResult; 34 SmsSingleSender singleSender = new SmsSingleSender(appId, appKey); 35 36 singleResult = singleSender.Send(SmsType.普通短信, phoneNumber, ""); 37 Console.WriteLine(singleResult); 38 39 List<string> templParams = new List<string>(); 40 templParams.Add("G2619"); 41 templParams.Add("二等座:23 无座:128"); 42 singleResult = singleSender.SendWithParam(phoneNumber, tmplateId, templParams); 43 Console.WriteLine(singleResult); 44 } 45 catch (Exception e) 46 { 47 Console.WriteLine(e); 48 } 49 Console.Read(); 50 } 51 } 52 }
1 namespace QcloudSms 2 { 3 #region 短信类型枚举 4 /// <summary> 5 /// 短信类型枚举 6 /// </summary> 7 public enum SmsType 8 { 9 普通短信 = 0, 10 营销短信 = 1 11 } 12 #endregion 13 14 #region 单发 15 /// <summary> 16 /// 单发 17 /// </summary> 18 class SmsSingleSender 19 { 20 #region 变量 21 /// <summary> 22 /// appId 23 /// </summary> 24 private int appId; 25 /// <summary> 26 /// appkey 27 /// </summary> 28 private string appkey; 29 /// <summary> 30 /// url 31 /// </summary> 32 private string url = "https://yun.tim.qq.com/v5/tlssmssvr/sendsms"; 33 /// <summary> 34 /// util 35 /// </summary> 36 private SmsSenderUtil util = new SmsSenderUtil(); 37 #endregion 38 39 #region 构造 40 /// <summary> 41 /// 构造函数 42 /// </summary> 43 /// <param name="sdkappid"></param> 44 /// <param name="appkey"></param> 45 public SmsSingleSender(int sdkappid, string appkey) 46 { 47 this.appId = sdkappid; 48 this.appkey = appkey; 49 } 50 #endregion 51 52 #region 普通单发短信接口 53 /// <summary> 54 /// 普通单发短信接口,明确指定内容,如果有多个签名,请在内容中以【】的方式添加到信息内容中,否则系统将使用默认签名 55 /// </summary> 56 /// <param name="type">短信类型,0 为普通短信,1 营销短信</param> 57 /// <param name="phoneNumber">不带国家码的手机号</param> 58 /// <param name="msg">信息内容,必须与申请的模板格式一致,否则将返回错误</param> 59 /// <param name="extend">短信码号扩展号,格式为纯数字串,其他格式无效。默认没有开通</param> 60 /// <param name="ext">服务端原样返回的参数,可填空</param> 61 /// <returns>SmsSingleSenderResult</returns> 62 public SmsSingleSenderResult Send(SmsType type, string phoneNumber, string msg, string extend = "", string ext = "") 63 { 64 long random = util.GetRandom(); 65 long curTime = util.GetCurTime(); 66 67 // 按照协议组织 post 请求包体 68 JObject tel = new JObject(); 69 tel.Add("nationcode", SmsSenderUtil.nationCode); 70 tel.Add("mobile", phoneNumber); 71 JObject data = new JObject(); 72 data.Add("tel", tel); 73 data.Add("msg", msg); 74 data.Add("type", (int)type); 75 data.Add("sig", util.StrToHash(string.Format("appkey={0}&random={1}&time={2}&mobile={3}", appkey, random, curTime, phoneNumber))); 76 data.Add("time", curTime); 77 data.Add("extend", extend); 78 data.Add("ext", ext); 79 80 string wholeUrl = url + "?sdkappid=" + appId + "&random=" + random; 81 HttpWebRequest request = util.GetPostHttpConn(wholeUrl); 82 byte[] requestData = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(data)); 83 request.ContentLength = requestData.Length; 84 Stream requestStream = request.GetRequestStream(); 85 requestStream.Write(requestData, 0, requestData.Length); 86 requestStream.Close(); 87 88 // 接收返回包 89 HttpWebResponse response = (HttpWebResponse)request.GetResponse(); 90 Stream responseStream = response.GetResponseStream(); 91 StreamReader streamReader = new StreamReader(responseStream, Encoding.GetEncoding("utf-8")); 92 string responseStr = streamReader.ReadToEnd(); 93 streamReader.Close(); 94 responseStream.Close(); 95 SmsSingleSenderResult result; 96 if (HttpStatusCode.OK == response.StatusCode) 97 { 98 result = util.ResponseStrToSingleSenderResult(responseStr); 99 } 100 else 101 { 102 result = new SmsSingleSenderResult(); 103 result.result = -1; 104 result.errmsg = "http error " + response.StatusCode + " " + responseStr; 105 } 106 return result; 107 } 108 #endregion 109 110 #region 指定模板单发 111 /// <summary> 112 /// 指定模板单发 113 /// </summary> 114 /// <param name="phoneNumber">不带国家码的手机号</param> 115 /// <param name="templId">模板 id</param> 116 /// <param name="templParams">模板参数列表,如模板 {1}...{2}...{3},那么需要带三个参数</param> 117 /// <param name="sign">短信签名,如果使用默认签名,该字段可缺省</param> 118 /// <param name="extend">扩展码,可填空</param> 119 /// <param name="ext">服务端原样返回的参数,可填空</param> 120 /// <returns>SmsSingleSenderResult</returns> 121 public SmsSingleSenderResult SendWithParam(string phoneNumber, int templId, List<string> templParams, string sign = "", string extend = "", string ext = "") 122 { 123 long random = util.GetRandom(); 124 long curTime = util.GetCurTime(); 125 126 // 按照协议组织 post 请求包体 127 JObject tel = new JObject(); 128 tel.Add("nationcode", SmsSenderUtil.nationCode); 129 tel.Add("mobile", phoneNumber); 130 JObject data = new JObject(); 131 data.Add("tel", tel); 132 data.Add("sig", util.CalculateSigForTempl(appkey, random, curTime, phoneNumber)); 133 data.Add("tpl_id", templId); 134 data.Add("params", util.SmsParamsToJSONArray(templParams)); 135 data.Add("sign", sign); 136 data.Add("time", curTime); 137 data.Add("extend", extend); 138 data.Add("ext", ext); 139 140 string wholeUrl = url + "?sdkappid=" + appId + "&random=" + random; 141 HttpWebRequest request = util.GetPostHttpConn(wholeUrl); 142 byte[] requestData = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(data)); 143 request.ContentLength = requestData.Length; 144 Stream requestStream = request.GetRequestStream(); 145 requestStream.Write(requestData, 0, requestData.Length); 146 requestStream.Close(); 147 148 // 接收返回包 149 HttpWebResponse response = (HttpWebResponse)request.GetResponse(); 150 Stream responseStream = response.GetResponseStream(); 151 StreamReader streamReader = new StreamReader(responseStream, Encoding.GetEncoding("utf-8")); 152 string responseStr = streamReader.ReadToEnd(); 153 streamReader.Close(); 154 responseStream.Close(); 155 SmsSingleSenderResult result; 156 if (HttpStatusCode.OK == response.StatusCode) 157 { 158 result = util.ResponseStrToSingleSenderResult(responseStr); 159 } 160 else 161 { 162 result = new SmsSingleSenderResult(); 163 result.result = -1; 164 result.errmsg = "http error " + response.StatusCode + " " + responseStr; 165 } 166 return result; 167 } 168 #endregion 169 } 170 #endregion 171 172 #region 单发结果 173 /// <summary> 174 /// 单发结果 175 /// </summary> 176 class SmsSingleSenderResult 177 { 178 /// <summary> 179 /// 错误码,0 表示成功(计费依据),非 0 表示失败 180 /// </summary> 181 public int result { set; get; } 182 /// <summary> 183 /// 错误消息,result 非 0 时的具体错误信息 184 /// </summary> 185 public string errmsg { set; get; } 186 /// <summary> 187 /// 用户的 session 内容,腾讯 server 回包中会原样返回 188 /// </summary> 189 public string ext { set; get; } 190 /// <summary> 191 /// 本次发送标识 id,标识一次短信下发记录 192 /// </summary> 193 public string sid { set; get; } 194 /// <summary> 195 /// 短信计费的条数 196 /// </summary> 197 public int fee { set; get; } 198 /// <summary> 199 /// ToString() 200 /// </summary> 201 /// <returns></returns> 202 public override string ToString() 203 { 204 return string.Format("SmsSingleSenderResult\nresult {0}\nerrMsg {1}\next {2}\nsid {3}\nfee {4}", result, errmsg, ext, sid, fee); 205 } 206 } 207 #endregion 208 209 #region 群发 210 /// <summary> 211 /// 群发 212 /// </summary> 213 class SmsMultiSender 214 { 215 #region 变量 216 /// <summary> 217 /// appId 218 /// </summary> 219 private int appId; 220 /// <summary> 221 /// appkey 222 /// </summary> 223 private string appkey; 224 /// <summary> 225 /// url 226 /// </summary> 227 private string url = "https://yun.tim.qq.com/v5/tlssmssvr/sendmultisms2"; 228 /// <summary> 229 /// util 230 /// </summary> 231 private SmsSenderUtil util = new SmsSenderUtil(); 232 #endregion 233 234 #region 构造 235 /// <summary> 236 /// 构造 237 /// </summary> 238 /// <param name="sdkappid"></param> 239 /// <param name="appkey"></param> 240 public SmsMultiSender(int sdkappid, string appkey) 241 { 242 this.appId = sdkappid; 243 this.appkey = appkey; 244 } 245 #endregion 246 247 #region 普通群发短信接口 248 /// <summary> 249 /// 普通群发短信接口,明确指定内容,如果有多个签名,请在内容中以【】的方式添加到信息内容中,否则系统将使用默认签名 250 ///【注意】 海外短信无群发功能 251 /// </summary> 252 /// <param name="type">短信类型,0 为普通短信,1 营销短信</param> 253 /// <param name="nationCode"></param> 254 /// <param name="phoneNumbers">不带国家码的手机号列表</param> 255 /// <param name="msg">信息内容,必须与申请的模板格式一致,否则将返回错误</param> 256 /// <param name="extend">扩展码,可填空</param> 257 /// <param name="ext">服务端原样返回的参数,可填空</param> 258 /// <returns>SmsMultiSenderResult</returns> 259 public SmsMultiSenderResult Send(SmsType type, List<string> phoneNumbers, string msg, string extend = "", string ext = "") 260 { 261 long random = util.GetRandom(); 262 long curTime = util.GetCurTime(); 263 264 // 按照协议组织 post 请求包体 265 JObject data = new JObject(); 266 data.Add("tel", util.PhoneNumbersToJSONArray(SmsSenderUtil.nationCode, phoneNumbers)); 267 data.Add("type", (int)type); 268 data.Add("msg", msg); 269 data.Add("sig", util.CalculateSig(appkey, random, curTime, phoneNumbers)); 270 data.Add("time", curTime); 271 data.Add("extend", extend); 272 data.Add("ext", ext); 273 274 string wholeUrl = url + "?sdkappid=" + appId + "&random=" + random; 275 HttpWebRequest request = util.GetPostHttpConn(wholeUrl); 276 byte[] requestData = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(data)); 277 request.ContentLength = requestData.Length; 278 Stream requestStream = request.GetRequestStream(); 279 requestStream.Write(requestData, 0, requestData.Length); 280 requestStream.Close(); 281 282 // 接收返回包 283 HttpWebResponse response = (HttpWebResponse)request.GetResponse(); 284 Stream responseStream = response.GetResponseStream(); 285 StreamReader streamReader = new StreamReader(responseStream, Encoding.GetEncoding("utf-8")); 286 string responseStr = streamReader.ReadToEnd(); 287 streamReader.Close(); 288 responseStream.Close(); 289 SmsMultiSenderResult result; 290 if (HttpStatusCode.OK == response.StatusCode) 291 { 292 result = util.ResponseStrToMultiSenderResult(responseStr); 293 } 294 else 295 { 296 result = new SmsMultiSenderResult(); 297 result.result = -1; 298 result.errmsg = "http error " + response.StatusCode + " " + responseStr; 299 } 300 return result; 301 } 302 #endregion 303 304 #region 指定模板群发 305 /// <summary> 306 /// 指定模板群发 307 /// 【注意】海外短信无群发功能 308 /// </summary> 309 /// <param name="phoneNumbers">不带国家码的手机号列表</param> 310 /// <param name="templId"> 模板 id</param> 311 /// <param name="templParams">模板参数列表</param> 312 /// <param name="sign"> 签名,如果填空,系统会使用默认签名</param> 313 /// <param name="extend">扩展码,可以填空</param> 314 /// <param name="ext">服务端原样返回的参数,可以填空</param> 315 /// <returns> SmsMultiSenderResult</returns> 316 public SmsMultiSenderResult SendWithParam(List<string> phoneNumbers, int templId, List<string> templParams, string sign = "", string extend = "", string ext = "") 317 { 318 long random = util.GetRandom(); 319 long curTime = util.GetCurTime(); 320 321 // 按照协议组织 post 请求包体 322 JObject data = new JObject(); 323 data.Add("tel", util.PhoneNumbersToJSONArray(SmsSenderUtil.nationCode, phoneNumbers)); 324 data.Add("sig", util.CalculateSigForTempl(appkey, random, curTime, phoneNumbers)); 325 data.Add("tpl_id", templId); 326 data.Add("params", util.SmsParamsToJSONArray(templParams)); 327 data.Add("sign", sign); 328 data.Add("time", curTime); 329 data.Add("extend", extend); 330 data.Add("ext", ext); 331 332 string wholeUrl = url + "?sdkappid=" + appId + "&random=" + random; 333 HttpWebRequest request = util.GetPostHttpConn(wholeUrl); 334 byte[] requestData = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(data)); 335 request.ContentLength = requestData.Length; 336 Stream requestStream = request.GetRequestStream(); 337 requestStream.Write(requestData, 0, requestData.Length); 338 requestStream.Close(); 339 340 // 接收返回包 341 HttpWebResponse response = (HttpWebResponse)request.GetResponse(); 342 Stream responseStream = response.GetResponseStream(); 343 StreamReader streamReader = new StreamReader(responseStream, Encoding.GetEncoding("utf-8")); 344 string responseStr = streamReader.ReadToEnd(); 345 streamReader.Close(); 346 responseStream.Close(); 347 SmsMultiSenderResult result; 348 if (HttpStatusCode.OK == response.StatusCode) 349 { 350 result = util.ResponseStrToMultiSenderResult(responseStr); 351 } 352 else 353 { 354 result = new SmsMultiSenderResult(); 355 result.result = -1; 356 result.errmsg = "http error " + response.StatusCode + " " + responseStr; 357 } 358 return result; 359 } 360 #endregion 361 } 362 #endregion 363 364 #region 群发结果 365 /// <summary> 366 /// 群发结果 367 /// </summary> 368 class SmsMultiSenderResult 369 { 370 public class Detail 371 { 372 /// <summary> 373 /// 错误码,0 表示成功(计费依据),非 0 表示失败 374 /// </summary> 375 public int result { get; set; } 376 /// <summary> 377 /// 错误消息,result 非 0 时的具体错误信息 378 /// </summary> 379 public string errmsg { get; set; } 380 /// <summary> 381 /// 手机号码 382 /// </summary> 383 public string mobile { get; set; } 384 /// <summary> 385 /// 国家码 386 /// </summary> 387 public string nationcode { get; set; } 388 /// <summary> 389 /// 本次发送标识 id,标识一次短信下发记录 390 /// </summary> 391 public string sid { get; set; } 392 /// <summary> 393 /// 短信计费的条数 394 /// </summary> 395 public int fee { get; set; } 396 /// <summary> 397 /// ToString() 398 /// </summary> 399 /// <returns></returns> 400 public override string ToString() 401 { 402 return string.Format( 403 "\tDetail result {0} errmsg {1} mobile {2} nationcode {3} sid {4} fee {5}", 404 result, errmsg, mobile, nationcode, sid, fee); 405 } 406 } 407 408 public int result; 409 public string errmsg = ""; 410 public string ext = ""; 411 public IList<Detail> detail; 412 413 public override string ToString() 414 { 415 if (null != detail) 416 { 417 return String.Format( 418 "SmsMultiSenderResult\nresult {0}\nerrmsg {1}\next {2}\ndetail:\n{3}", 419 result, errmsg, ext, String.Join("\n", detail)); 420 } 421 else 422 { 423 return String.Format( 424 "SmsMultiSenderResult\nresult {0}\nerrmsg {1}\next {2}\n", 425 result, errmsg, ext); 426 } 427 } 428 } 429 #endregion 430 431 #region 公共类 432 /// <summary> 433 /// 公共类 434 /// </summary> 435 class SmsSenderUtil 436 { 437 /// <summary> 438 /// 国家码 439 /// </summary> 440 public static string nationCode = "86"; 441 /// <summary> 442 /// 随机数生成器 443 /// </summary> 444 private Random random = new Random(); 445 446 #region GetPostHttpConn 447 /// <summary> 448 /// GetPostHttpConn 449 /// </summary> 450 /// <param name="url"></param> 451 /// <returns></returns> 452 public HttpWebRequest GetPostHttpConn(string url) 453 { 454 HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); 455 request.Method = "POST"; 456 request.ContentType = "application/x-www-form-urlencoded"; 457 return request; 458 } 459 #endregion 460 461 #region 生成随机数 462 /// <summary> 463 /// 生成随机数 464 /// </summary> 465 /// <returns></returns> 466 public long GetRandom() 467 { 468 return random.Next(999999) % 900000 + 100000; 469 } 470 #endregion 471 472 #region 获取请求发起时间 473 /// <summary> 474 /// 获取请求发起时间, 475 /// unix 时间戳(单位:秒),如果和系统时间相差超过 10 分钟则会返回失败 476 /// </summary> 477 /// <returns></returns> 478 public long GetCurTime() 479 { 480 Int32 unixTimestamp = (Int32)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds; 481 return unixTimestamp; 482 } 483 #endregion 484 485 #region 字符串转SHA256 486 /// <summary> 487 /// 字符串转SHA256 488 /// </summary> 489 /// <param name="str"></param> 490 /// <returns></returns> 491 public string StrToHash(string str) 492 { 493 SHA256 sha256 = SHA256Managed.Create(); 494 byte[] resultByteArray = sha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes(str)); 495 return ByteArrayToHex(resultByteArray); 496 } 497 498 /// <summary> 499 /// 将二进制的数值转换为 16 进制字符串,如 "abc" => "616263" 500 /// </summary> 501 /// <param name="byteArray"></param> 502 /// <returns></returns> 503 private static string ByteArrayToHex(byte[] byteArray) 504 { 505 string returnStr = ""; 506 if (byteArray != null) 507 { 508 for (int i = 0; i < byteArray.Length; i++) 509 { 510 returnStr += byteArray[i].ToString("x2"); 511 } 512 } 513 return returnStr; 514 } 515 #endregion 516 517 #region 将单发回包解析成结果对象 518 /// <summary> 519 /// 将单发回包解析成结果对象 520 /// </summary> 521 /// <param name="str"></param> 522 /// <returns></returns> 523 public SmsSingleSenderResult ResponseStrToSingleSenderResult(string str) 524 { 525 SmsSingleSenderResult result = JsonConvert.DeserializeObject<SmsSingleSenderResult>(str); 526 return result; 527 } 528 #endregion 529 530 #region 将群发回包解析成结果对象 531 /// <summary> 532 /// 将群发回包解析成结果对象 533 /// </summary> 534 /// <param name="str"></param> 535 /// <returns></returns> 536 public SmsMultiSenderResult ResponseStrToMultiSenderResult(string str) 537 { 538 SmsMultiSenderResult result = JsonConvert.DeserializeObject<SmsMultiSenderResult>(str); 539 return result; 540 } 541 #endregion 542 543 #region List<string>转JArray 544 /// <summary> 545 /// List<string>转JArray 546 /// </summary> 547 /// <param name="templParams"></param> 548 /// <returns></returns> 549 public JArray SmsParamsToJSONArray(List<string> templParams) 550 { 551 JArray smsParams = new JArray(); 552 foreach (string templParamsElement in templParams) 553 { 554 smsParams.Add(templParamsElement); 555 } 556 return smsParams; 557 } 558 #endregion 559 560 #region PhoneNumbersToJSONArray 561 /// <summary> 562 /// PhoneNumbersToJSONArray 563 /// </summary> 564 /// <param name="nationCode"></param> 565 /// <param name="phoneNumbers"></param> 566 /// <returns></returns> 567 public JArray PhoneNumbersToJSONArray(string nationCode, List<string> phoneNumbers) 568 { 569 JArray tel = new JArray(); 570 int i = 0; 571 do 572 { 573 JObject telElement = new JObject(); 574 telElement.Add("nationcode", nationCode); 575 telElement.Add("mobile", phoneNumbers.ElementAt(i)); 576 tel.Add(telElement); 577 } while (++i < phoneNumbers.Count); 578 return tel; 579 } 580 #endregion 581 582 #region 计算App凭证 583 /* 584 "sig" 字段根据公式 sha256(appkey=$appkey&random=$random&time=$time&mobile=$mobile)生成 585 */ 586 public string CalculateSigForTempl(string appkey, long random, long curTime, List<string> phoneNumbers) 587 { 588 string phoneNumbersString = phoneNumbers.ElementAt(0); 589 for (int i = 1; i < phoneNumbers.Count; i++) 590 { 591 phoneNumbersString += "," + phoneNumbers.ElementAt(i); 592 } 593 return StrToHash(String.Format( 594 "appkey={0}&random={1}&time={2}&mobile={3}", 595 appkey, random, curTime, phoneNumbersString)); 596 } 597 598 public string CalculateSigForTempl(string appkey, long random, long curTime, string phoneNumber) 599 { 600 List<string> phoneNumbers = new List<string>(); 601 phoneNumbers.Add(phoneNumber); 602 return CalculateSigForTempl(appkey, random, curTime, phoneNumbers); 603 } 604 605 public string CalculateSig(string appkey, long random, long curTime, List<string> phoneNumbers) 606 { 607 string phoneNumbersString = phoneNumbers.ElementAt(0); 608 for (int i = 1; i < phoneNumbers.Count; i++) 609 { 610 phoneNumbersString += "," + phoneNumbers.ElementAt(i); 611 } 612 return StrToHash(String.Format( 613 "appkey={0}&random={1}&time={2}&mobile={3}", 614 appkey, random, curTime, phoneNumbersString)); 615 } 616 #endregion 617 } 618 #endregion 619 }
QcloudSms
demo下载路径为:https://files.cnblogs.com/files/LikeHeart/ConsoleTest.zip
原文地址:https://www.cnblogs.com/LikeHeart/p/8847465.html
时间: 2024-10-07 07:24:54