UnionParseCore.cs 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662
  1. using Microsoft.AspNetCore.Http;
  2. using Microsoft.AspNetCore.Mvc.Controllers;
  3. using Microsoft.AspNetCore.Mvc.Filters;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Linq;
  7. using System.Text;
  8. using dodohold.core;
  9. using Spire.Pdf.Exporting.XPS.Schema;
  10. using System.Xml.Linq;
  11. using System.Net;
  12. using TencentCloud.Mrs.V20200910.Models;
  13. using Microsoft.AspNetCore.Mvc;
  14. using Microsoft.AspNetCore.Authentication;
  15. using Spire.Pdf.Graphics;
  16. using Google.Protobuf.WellKnownTypes;
  17. using TencentCloud.Tcm.V20210413.Models;
  18. using System.Diagnostics;
  19. using System.Threading;
  20. using TencentCloud.Soe.V20180724.Models;
  21. using OfficeOpenXml.FormulaParsing.LexicalAnalysis;
  22. using System.Runtime.Intrinsics.Arm;
  23. using ZstdSharp.Unsafe;
  24. using System.Text.RegularExpressions;
  25. namespace molilian.core
  26. {
  27. public partial class UnionParseCore
  28. {
  29. public static async Task<ActionResult> UnionParseAsync(string content, string channel, string ip, string oaid, int accountid = 0)
  30. {
  31. content = content.Trim();
  32. return channel switch
  33. {
  34. "wemeet" => await WemeetParseAsync(content, ip, oaid),
  35. "bdpan" => PanParse(content, ip, oaid),
  36. "jd" => await JdParseAsync(content, ip, oaid, accountid),
  37. "dy" => await DyParseAsync(content, ip, oaid),
  38. "pdd" => await PddParseAsync(content, ip, oaid, accountid),
  39. _ => await TaobaoParseAsync(content, ip, oaid, accountid),
  40. };
  41. }
  42. public static async Task<APIResult> WemeetParseAsync(string content, string ip, string oaid)
  43. {
  44. var result = ToolParsePlus.GetFormattedObject(TkChannelEnum.wemeet, content, ip, oaid);
  45. content = content.UrlDecode();
  46. result.rawContent = content;
  47. try
  48. {
  49. result = await ToolParsePlus.WemeetParseAsync(content, result);
  50. }
  51. catch (Exception ex)
  52. {
  53. _ = new LoggerLibrary("unionParse", "wemeet_error")
  54. .Info(ip, oaid)
  55. .Info(content)
  56. .Info(ex.Message, ex.StackTrace)
  57. .SaveAsync();
  58. NotifyCore.Notify(new NifyMessage
  59. {
  60. message = $"【转链异常Wemeet】\n{content}\n\n{ex.Message}\n{ex.StackTrace}",
  61. priority = NifyMessagePriority.high,
  62. tags = ["red_circle"]
  63. });
  64. result.success = false;
  65. result.message = "内部错误";
  66. result.reason = "转链接口异常";
  67. }
  68. _ = TkLogCore.ParseLogAsync(result);
  69. return new APIResult(new
  70. {
  71. result.success,
  72. result.message,
  73. channel = result?.channel.ToString(),
  74. result.shortLinkurl,
  75. result.deeplink_url,
  76. result.itemName,
  77. }, result.success ? APIResultCodeEnum.OK : APIResultCodeEnum.NotAcceptable);
  78. }
  79. public static APIResult PanParse(string content, string ip, string oaid)
  80. {
  81. var result = ToolParsePlus.GetFormattedObject(TkChannelEnum.bdpan, content, ip, oaid);
  82. content = content.UrlDecode();
  83. result.rawContent = content;
  84. try
  85. {
  86. ToolParsePlus.PanParse(content, ref result);
  87. }
  88. catch (Exception ex)
  89. {
  90. _ = new LoggerLibrary("unionParse", "pan_error")
  91. .Info(ip, oaid)
  92. .Info(content)
  93. .Info(ex.Message, ex.StackTrace)
  94. .SaveAsync();
  95. NotifyCore.Notify(new NifyMessage
  96. {
  97. message = $"【转链异常Pan】\n{content}\n\n{ex.Message}\n{ex.StackTrace}",
  98. priority = NifyMessagePriority.high,
  99. tags = ["red_circle"]
  100. });
  101. result.success = false;
  102. result.message = "内部错误";
  103. result.reason = "转链接口异常";
  104. }
  105. _ = TkLogCore.ParseLogAsync(result);
  106. return new APIResult(new
  107. {
  108. result.success,
  109. result.message,
  110. channel = result?.channel.ToString(),
  111. result.shortLinkurl,
  112. result.deeplink_url,
  113. result.itemName,
  114. }, !string.IsNullOrEmpty(result.deeplink_url) ? APIResultCodeEnum.OK : APIResultCodeEnum.NotAcceptable);
  115. }
  116. private static APIResult TaobaoParseOutput(TkDataDTO result, Dictionary<string, long> swData, object info, APIResultCodeEnum code = APIResultCodeEnum.OK)
  117. {
  118. if (result.elapsedTime > 2000)
  119. {
  120. LoggerLibrary log = new("debug", "stopwatch");
  121. foreach ((var key, var value) in swData)
  122. {
  123. log.Info($"{key}\t{value}");
  124. }
  125. log.SaveAsync();
  126. }
  127. return new APIResult(info, code);
  128. }
  129. public static async Task<APIResult> TaobaoParseAsync(string content,
  130. string ip = "", string oaid = "",
  131. int accountid = 0)
  132. {
  133. AlimamaPlus alimama = null;
  134. //content = content.UrlDecode();
  135. content = content[..Math.Min(1000, content.Length)];
  136. var result = AlimamaPlus.GetFormattedObject(content, ip, oaid);
  137. Dictionary<string, long> swData = new();
  138. swData.Add("StartNew", 0);
  139. Stopwatch stopwatch = Stopwatch.StartNew();
  140. result.rawContent = content;
  141. try
  142. {
  143. string reason = string.Empty;
  144. var config = TkConfigCore.Get();
  145. var arr = config.tk_whitelist_regular.Split('\n')
  146. .Where(line => !string.IsNullOrWhiteSpace(line))
  147. .ToList();
  148. var arr2 = config.tk_blacklist_regular.Split('\n')
  149. .Where(line => !string.IsNullOrWhiteSpace(line))
  150. .ToList();
  151. var arr3 = config.multi_token_regular.Split('\n')
  152. .Where(line => !string.IsNullOrWhiteSpace(line))
  153. .ToList();
  154. bool is_tao_url = !string.IsNullOrEmpty(result.shortLinkurl);
  155. bool is_tao_token = AlimamaPlus.MatchRegexes(content, arr);
  156. bool is_other = AlimamaPlus.MatchOtherInfo(content, arr2);
  157. //============================== 放弃转链-初步筛选1 ==============================
  158. if (!is_tao_url && !is_tao_token || is_other)
  159. {
  160. //没有链接都放弃
  161. result.success = false;
  162. result.message = "放弃转链";
  163. result.reason = "初步筛选1";
  164. result.subCode = TkSubCodeEnum.Prelim1;
  165. result.itemName = "";
  166. result.deeplink_url = "";
  167. _ = TkLogCore.ParseLogAsync(result);
  168. return TaobaoParseOutput(result, swData, new
  169. {
  170. result.success,
  171. result.message,
  172. result.content,
  173. result.itemName,
  174. result.deeplink_url,
  175. });
  176. }
  177. bool is_multi_token = AlimamaPlus.MatchMultiToken(content, arr3);
  178. //============================== 放弃转链-初步筛选1.5 ==============================
  179. if (is_multi_token)
  180. {
  181. //没有链接都放弃
  182. result.success = false;
  183. result.message = "放弃转链";
  184. result.reason = "初步筛选1.5";
  185. result.subCode = TkSubCodeEnum.Prelim1_5;
  186. result.itemName = "";
  187. result.deeplink_url = "";
  188. _ = TkLogCore.ParseLogAsync(result);
  189. return TaobaoParseOutput(result, swData, new
  190. {
  191. result.success,
  192. result.message,
  193. result.content,
  194. result.itemName,
  195. result.deeplink_url,
  196. });
  197. }
  198. //============================== 放弃转链-初步筛选2 ==============================
  199. bool is_aff_link = false;
  200. if (!string.IsNullOrEmpty(result.shortLinkurl)) is_aff_link = AlimamaPlus.IsAffLink(result.shortLinkurl);
  201. if ((!is_tao_url && is_tao_token) || is_aff_link)
  202. {
  203. //没有短链接都放弃
  204. result.success = false;
  205. result.message = "放弃转链";
  206. result.reason = "初步筛选2";
  207. result.subCode = TkSubCodeEnum.Prelim2;
  208. result.itemName = "点击打开淘宝APP";
  209. _ = TkLogCore.ParseLogAsync(result);
  210. return TaobaoParseOutput(result, swData, new
  211. {
  212. result.success,
  213. result.message,
  214. result.content,
  215. result.itemName,
  216. result.deeplink_url,
  217. });
  218. }
  219. //那就初步筛选3 有tb.cn 并且有 or 手淘发现
  220. string pattern = @"^(?!【淘宝】).*https?:\/\/m\.tb\.cn.*(?:手淘发现|手淘搜索)";
  221. bool isMatch = Regex.IsMatch(content, pattern);
  222. if (isMatch)
  223. {
  224. result.success = false;
  225. result.message = "放弃转链";
  226. result.reason = "初步筛选3";
  227. result.subCode = TkSubCodeEnum.Prelim3;
  228. result.itemName = "点击打开淘宝APP";
  229. _ = TkLogCore.ParseLogAsync(result);
  230. return TaobaoParseOutput(result, swData, new
  231. {
  232. result.success,
  233. result.message,
  234. result.content,
  235. result.itemName,
  236. result.deeplink_url,
  237. });
  238. }
  239. //============================== 放弃转链-地区过滤 ==============================
  240. bool is_ignore2 = AlimamaPlus.ShouldIgnoreRequest(ip, oaid, out reason);
  241. if (is_ignore2)
  242. {
  243. result.success = false;
  244. result.message = "放弃转链";
  245. result.itemName = "点击打开淘宝APP";
  246. result.reason = reason;
  247. result.subCode = TkSubCodeEnum.TrafficCtrl;
  248. _ = TkLogCore.ParseLogAsync(result);
  249. return TaobaoParseOutput(result, swData, new
  250. {
  251. result.success,
  252. result.message,
  253. result.content,
  254. result.itemName,
  255. result.deeplink_url,
  256. });
  257. }
  258. //============================== 放弃转链-没有匹配账号 ==============================
  259. TkPoolDTO? account = accountid > 0 ?
  260. TkPoolCore.GetOne(accountid) :
  261. TkPoolCore.GetOne(TkPoolCore.TkAction.parse);
  262. if (account == null)
  263. {
  264. result.success = false;
  265. result.message = "放弃转链";
  266. result.reason = "没有匹配账号";
  267. result.subCode = TkSubCodeEnum.Other;
  268. result.itemName = "点击打开淘宝APP";
  269. _ = TkLogCore.ParseLogAsync(result);
  270. return TaobaoParseOutput(result, swData, new
  271. {
  272. result.success,
  273. result.message,
  274. result.content,
  275. result.couponAmount,
  276. result.taoToken,
  277. result.shortLinkurl,
  278. result.deeplink_url,
  279. });
  280. }
  281. result.accountId = account.id;
  282. result.accountName = account.company;
  283. alimama = new AlimamaPlus(account);
  284. int timeout = alimama._config.rt_max;
  285. #if DEBUG
  286. timeout = 10000;
  287. #endif
  288. using var cts = new CancellationTokenSource();
  289. cts.CancelAfter(timeout);
  290. var requestTask = Task.Run(() => alimama.UnionParse2Async(content, result, cts.Token, swData), cts.Token);
  291. var delayTask = Task.Delay(timeout, cts.Token);
  292. var completedTask = await Task.WhenAny(requestTask, delayTask);
  293. if (completedTask == requestTask)
  294. {
  295. result = await requestTask;
  296. }
  297. else
  298. {
  299. //============================== 放弃转链-请求超时 ==============================
  300. cts.Cancel();
  301. result.success = false;
  302. result.message = "放弃转链";
  303. result.reason = "请求超时";
  304. result.subCode = TkSubCodeEnum.Other;
  305. result.itemName = "点击打开淘宝APP";
  306. }
  307. result.accountName = account.company;
  308. //result = await alimama.UnionParse2Async(content, result);
  309. }
  310. catch (Exception ex)
  311. {
  312. //============================== 放弃转链-请求超时 ==============================
  313. if (ex.Message.Contains("was canceled"))
  314. {
  315. result.success = false;
  316. result.message = "放弃转链";
  317. result.reason = "请求超时";
  318. result.subCode = TkSubCodeEnum.Other;
  319. result.itemName = "点击打开淘宝APP";
  320. }
  321. else
  322. {
  323. //============================== 转链接口异常 ==============================
  324. _ = new LoggerLibrary("unionParse", "tb_error")
  325. .Info(ip, oaid)
  326. .Info(content)
  327. .Info(ex.Message, ex.StackTrace)
  328. .SaveAsync();
  329. NotifyCore.Notify(new NifyMessage
  330. {
  331. message = $"【转链异常TB】\n{content}\n\n{ex.Message}\n{ex.StackTrace}",
  332. priority = NifyMessagePriority.high,
  333. tags = ["red_circle"]
  334. });
  335. result.success = false;
  336. result.subCode = TkSubCodeEnum.Other;
  337. result.message = "转链接口异常";
  338. result.itemName = "点击打开淘宝APP";
  339. }
  340. }
  341. _ = TkLogCore.ParseLogAsync(result, alimama);
  342. return TaobaoParseOutput(result, swData, new
  343. {
  344. result.success,
  345. result.message,
  346. link_type = result.link_type.ToString(),
  347. channel = result?.channel.ToString(),
  348. result.channel_type,
  349. result.itemId,
  350. result.itemName,
  351. result.pic,
  352. result.promotionPrice,
  353. result.couponAmount,
  354. result.couponEffectiveStartTime,
  355. result?.couponEffectiveEndTime,
  356. result.shortLinkurl,
  357. result.deeplink_url,
  358. result.content,
  359. result.taoToken,
  360. });
  361. }
  362. private static APIResult JdParseOutput(JdDataDTO result, APIResultCodeEnum code = APIResultCodeEnum.OK)
  363. {
  364. return new APIResult(new
  365. {
  366. result.success,
  367. result.message,
  368. link_type = result.link_type.ToString(),
  369. channel = result?.channel.ToString(),
  370. result.channel_type,
  371. result.itemId,
  372. result.itemName,
  373. result.shortLinkurl,
  374. result.deeplink_url,
  375. }, code);
  376. }
  377. public static async Task<APIResult> JdParseAsync(string content, string ip, string oaid,
  378. int accountid = 0, CancellationToken cancellationToken = default)
  379. {
  380. var result = JdUnionPlus.GetFormattedObject(content, ip, oaid);
  381. content = content.UrlDecode();
  382. result.rawContent = content;
  383. bool IfExceptional = false;
  384. try
  385. {
  386. var config = TkConfigCore.Get();
  387. //============================== 放弃转链-地区过滤 ==============================
  388. if (accountid == 0 && JdUnionPlus.ShouldIgnoreRequest(config, ip, oaid, out string reason))
  389. {
  390. result.success = false;
  391. result.message = "放弃转链";
  392. result.reason = reason;
  393. _ = TkLogCore.ParseLogAsync(result);
  394. return JdParseOutput(result);
  395. }
  396. // bool is_ignore2 = JdUnionPlus.FlowControlIgnoreRequest(config, out reason);
  397. //#if DEBUG
  398. // is_ignore2 = false;
  399. //#endif
  400. // if (accountid == 0 && is_ignore2)
  401. // {
  402. // result.success = false;
  403. // result.message = "放弃转链";
  404. // result.reason = reason;
  405. // _ = TkLogCore.ParseLogAsync(result);
  406. // return JdParseOutput(result);
  407. // }
  408. var account = accountid > 0 ? JdPoolCore.GetOne(accountid, JdPoolCore.JdAction.parse) : JdPoolCore.GetOne(JdPoolCore.JdAction.parse);
  409. if (account == null)
  410. {
  411. result.success = false;
  412. result.message = "放弃转链";
  413. result.reason = "没有匹配账号";
  414. _ = TkLogCore.ParseLogAsync(result);
  415. return JdParseOutput(result);
  416. }
  417. result.accountId = account.id;
  418. result.accountName = account.name;
  419. var plus = new JdUnionPlus(account);
  420. result = await plus.JdParseAsync(content, result, cancellationToken);
  421. }
  422. catch (Exception ex)
  423. {
  424. if (ex.Message.Contains("was canceled"))
  425. {
  426. result.success = false;
  427. result.message = "放弃转链";
  428. result.reason = "请求超时";
  429. }
  430. else
  431. {
  432. IfExceptional = true;
  433. _ = new LoggerLibrary("unionParse", "jd_error")
  434. .Info(ip, oaid)
  435. .Info(content)
  436. .Info(ex.Message, ex.StackTrace)
  437. .SaveAsync();
  438. NotifyCore.Notify(new NifyMessage
  439. {
  440. message = $"【转链异常JD】\n{content}\n\n{ex.Message}\n{ex.StackTrace}",
  441. priority = NifyMessagePriority.high,
  442. tags = ["red_circle"]
  443. });
  444. result.success = false;
  445. result.message = "内部错误";
  446. result.reason = "转链接口异常";
  447. }
  448. }
  449. _ = TkLogCore.ParseLogAsync(result);
  450. return JdParseOutput(result, IfExceptional ? APIResultCodeEnum.NotAcceptable : APIResultCodeEnum.OK);
  451. }
  452. public static async Task<APIResult> DyParseAsync(string content, string ip, string oaid, CancellationToken cancellationToken = default)
  453. {
  454. var result = DyUnionPlus.GetFormattedObject(content, ip, oaid);
  455. content = content.UrlDecode();
  456. result.rawContent = content;
  457. bool IfExceptional = false;
  458. try
  459. {
  460. // 2024-06-19 联盟业务下线
  461. result.success = false;
  462. result.message = "放弃转链";
  463. result.reason = "联盟下线";
  464. _ = TkLogCore.ParseLogAsync(result);
  465. return new APIResult(result);
  466. if (DyUnionPlus.ShouldIgnoreRequest(ip, oaid, out string reason))
  467. {
  468. result.success = false;
  469. result.message = "放弃转链";
  470. result.reason = reason;
  471. _ = TkLogCore.ParseLogAsync(result);
  472. return new APIResult(result);
  473. }
  474. var account = PangolinPoolCore.GetOne();
  475. if (account == null)
  476. {
  477. result.success = false;
  478. result.message = "放弃转链";
  479. result.reason = "没有匹配账号";
  480. _ = TkLogCore.ParseLogAsync(result);
  481. return new APIResult(result);
  482. }
  483. result = await DyUnionPlus.DyParseAsync(account, content, result, cancellationToken);
  484. }
  485. catch (Exception ex)
  486. {
  487. if (ex.Message.Contains("was canceled"))
  488. {
  489. result.success = false;
  490. result.message = "放弃转链";
  491. result.reason = "请求超时";
  492. }
  493. else
  494. {
  495. IfExceptional = true;
  496. _ = new LoggerLibrary("unionParse", "dy_error")
  497. .Info(ip, oaid)
  498. .Info(content)
  499. .Info(ex.Message, ex.StackTrace)
  500. .SaveAsync();
  501. NotifyCore.Notify(new NifyMessage
  502. {
  503. message = $"【转链异常DY】\n{content}\n\n{ex.Message}\n{ex.StackTrace}",
  504. priority = NifyMessagePriority.high,
  505. tags = ["red_circle"]
  506. });
  507. result.success = false;
  508. result.message = "内部错误";
  509. result.reason = "转链接口异常";
  510. }
  511. }
  512. _ = TkLogCore.ParseLogAsync(result);
  513. return new APIResult(new
  514. {
  515. result.success,
  516. result.message,
  517. link_type = result.link_type.ToString(),
  518. channel = result?.channel.ToString(),
  519. content = result?.rawContent.ToString(),
  520. taoToken = result?.taoToken.ToString(),
  521. result.channel_type,
  522. result.itemName,
  523. result.shortLinkurl,
  524. result.deeplink_url,
  525. }, IfExceptional ? APIResultCodeEnum.NotAcceptable : APIResultCodeEnum.OK);
  526. }
  527. public static async Task<APIResult> PddParseAsync(string content, string ip, string oaid,
  528. int accountid = 0, CancellationToken cancellationToken = default)
  529. {
  530. var result = PddUnionPlus.GetFormattedObject(content, ip, oaid);
  531. content = content.UrlDecode();
  532. result.rawContent = content;
  533. bool IfExceptional = false;
  534. try
  535. {
  536. var config = TkConfigCore.Get();
  537. //============================== 放弃转链-地区过滤 ==============================
  538. if (accountid == 0 && PddUnionPlus.ShouldIgnoreRequest(config, ip, oaid, out string reason))
  539. {
  540. result.success = false;
  541. result.message = "放弃转链";
  542. result.reason = reason;
  543. _ = TkLogCore.ParseLogAsync(result);
  544. return PddParseOutput(result);
  545. }
  546. var account = PddPoolCore.GetOne(PddUnionWorkMode.All, accountid);
  547. if (account == null)
  548. {
  549. result.success = false;
  550. result.message = "放弃转链";
  551. result.reason = "没有匹配账号";
  552. _ = TkLogCore.ParseLogAsync(result);
  553. return PddParseOutput(result);
  554. }
  555. result.accountId = account.id;
  556. result.accountName = account.name;
  557. var plus = new PddUnionPlus(account);
  558. result = await plus.PddParseAsync(content, result, cancellationToken);
  559. }
  560. catch (Exception ex)
  561. {
  562. if (ex.Message.Contains("was canceled"))
  563. {
  564. result.success = false;
  565. result.message = "放弃转链";
  566. result.reason = "请求超时";
  567. }
  568. else
  569. {
  570. IfExceptional = true;
  571. _ = new LoggerLibrary("unionParse", "pdd_error")
  572. .Info(ip, oaid)
  573. .Info(content)
  574. .Info(ex.Message, ex.StackTrace)
  575. .SaveAsync();
  576. NotifyCore.Notify(new NifyMessage
  577. {
  578. message = $"【转链异常PDD】\n{content}\n\n{ex.Message}\n{ex.StackTrace}",
  579. priority = NifyMessagePriority.high,
  580. tags = ["red_circle"]
  581. });
  582. result.success = false;
  583. result.message = "内部错误";
  584. result.reason = "转链接口异常";
  585. }
  586. }
  587. _ = TkLogCore.ParseLogAsync(result);
  588. return PddParseOutput(result, IfExceptional ? APIResultCodeEnum.NotAcceptable : APIResultCodeEnum.OK);
  589. }
  590. private static APIResult PddParseOutput(PddDataDTO result, APIResultCodeEnum code = APIResultCodeEnum.OK)
  591. {
  592. return new APIResult(new
  593. {
  594. result.success,
  595. result.message,
  596. link_type = result.link_type.ToString(),
  597. channel = result?.channel.ToString(),
  598. result.itemId,
  599. result.itemName,
  600. result.shortLinkurl,
  601. result.deeplink_url,
  602. }, code);
  603. }
  604. }
  605. }