DeeplinkParseCore.cs 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694
  1. 
  2. using dodohold.core;
  3. using Microsoft.AspNetCore.Mvc;
  4. using Sayaka.Common;
  5. using System.Text.Json;
  6. using System.Text.RegularExpressions;
  7. using System.Web;
  8. namespace molilian.core
  9. {
  10. public partial class DeeplinkParseCore
  11. {
  12. private static string _end_point;
  13. private static readonly string[] separators = ["\r\n", "\n"];
  14. static DeeplinkParseCore()
  15. {
  16. _end_point = Environment.GetEnvironmentVariable("EndPoint");
  17. }
  18. public static DeeplinkParseDataDTO GetFormattedObject(UnionParseRequest request)
  19. {
  20. return new DeeplinkParseDataDTO()
  21. {
  22. message = string.Empty,
  23. success = false,
  24. content = request.Content,
  25. ip = request.Ip,
  26. oaid = request.Oaid,
  27. elapsedTime = 0,
  28. create_time = DateTime.Now,
  29. end_point = _end_point,
  30. };
  31. }
  32. public static async Task<ActionResult> ParseAsync(UnionParseRequest request)
  33. {
  34. var result = GetFormattedObject(request);
  35. request.Content = request.Content.Trim();
  36. var rule = DeeplinkParseRuleCore.GetOne(request.Channel);
  37. if (rule == null)
  38. {
  39. result.success = false;
  40. result.message = "转链失败";
  41. result.reason = "无效平台";
  42. _ = TkLogCore.ParseLogAsync(result);
  43. return new APIResult(new
  44. {
  45. result.success,
  46. result.message,
  47. result.reason,
  48. });
  49. }
  50. result.channel_id = rule.channel_id;
  51. result.channel_name = rule.channel_name;
  52. var overrideContent = await OverrideRuleCore.ProcessAsync(request);
  53. if (overrideContent != null)
  54. {
  55. result.success = overrideContent.response_success;
  56. result.message = overrideContent.ResolveMessage("OK");
  57. result.reason = overrideContent.ResolveReason(result.reason);
  58. result.deeplink_url = overrideContent.output_text;
  59. _ = TkLogCore.ParseLogAsync(result);
  60. return new APIResult(new
  61. {
  62. result.success,
  63. result.message,
  64. result.reason,
  65. channel = result.channel_name,
  66. result.deeplink_url
  67. });
  68. }
  69. if (ShouldIgnoreRequest(rule.IgnorePercentageCity, request.Ip, request.Oaid, out string reason))
  70. {
  71. result.success = false;
  72. result.message = "放弃转链";
  73. result.reason = reason;
  74. _ = TkLogCore.ParseLogAsync(result);
  75. return new APIResult(new
  76. {
  77. result.success,
  78. result.message,
  79. result.reason,
  80. });
  81. }
  82. result = await GenerateDeeplink(request.Content, rule, result);
  83. _ = TkLogCore.ParseLogAsync(result);
  84. return new APIResult(new
  85. {
  86. result.success,
  87. result.message,
  88. channel = result.channel_name,
  89. result.deeplink_url,
  90. result.itemName,
  91. });
  92. }
  93. public static bool ShouldIgnoreRequest(string ignorePercentageCity, string ip, string oaid, out string reason)
  94. {
  95. reason = string.Empty;
  96. try
  97. {
  98. // IP和流量控制
  99. string? ipInfo = IP2RegionPlus.Search(ip);
  100. if (!TestParseCore.InWhitelist(ip, oaid) && AlimamaPlus.IgnoreRegionIncluded(ignorePercentageCity, ipInfo, out string regionInfo))
  101. {
  102. reason = $"地区控制:{regionInfo}";
  103. return true;
  104. }
  105. }
  106. catch (Exception ex)
  107. {
  108. reason = "配置异常";
  109. }
  110. return false;
  111. }
  112. public static async Task<DeeplinkParseDataDTO> GenerateDeeplink(string text, DeeplinkParseRuleDTO config, DeeplinkParseDataDTO result)
  113. {
  114. try
  115. {
  116. var list = config.rules.Convert2Object<List<DeeplinkParseRule>>();
  117. foreach (var rule in list)
  118. {
  119. var e = await WorkEachRule(text, config, result, rule);
  120. if (e != null) return e;
  121. }
  122. }
  123. catch (Exception ex)
  124. {
  125. }
  126. result.success = false;
  127. return result;
  128. }
  129. private static async Task<DeeplinkParseDataDTO?> WorkEachRule(string text, DeeplinkParseRuleDTO config, DeeplinkParseDataDTO result, DeeplinkParseRule rule)
  130. {
  131. if (!rule.enable) return null;
  132. string content = text;
  133. if (!string.IsNullOrEmpty(rule.url_pattern))
  134. {
  135. var regex = new Regex(rule.url_pattern);
  136. var match = regex.Match(content);
  137. if (!match.Success) return null;
  138. content = match.Groups[1].Value;
  139. }
  140. string url = ToolParsePlus.GetLink(text);
  141. try
  142. {
  143. switch (rule.url_action)
  144. {
  145. case UrlAction.Redirect:
  146. {
  147. var response = await new WebClientUtility
  148. {
  149. Proxy = ProxyNodesCore.RandomOne(),
  150. UserAgent = ProviderFakeUserAgent.RandomMobile,
  151. AllowAutoRedirect = false
  152. }.RequestAsync(url);
  153. if (response.ResponseMessage != null)
  154. {
  155. content = response.ResponseMessage.Headers.Location.ToString();
  156. //content = content.UrlDecode();
  157. }
  158. if (string.IsNullOrEmpty(content)) return null;
  159. }
  160. break;
  161. case UrlAction.Body:
  162. case UrlAction.JsonBody:
  163. {
  164. var client = new WebClientUtility
  165. {
  166. Proxy = ProxyNodesCore.RandomOne(),
  167. UserAgent = ProviderFakeUserAgent.RandomMobile,
  168. AllowAutoRedirect = true
  169. };
  170. if (!string.IsNullOrEmpty(rule.post))
  171. {
  172. string postData = rule.post;
  173. // 使用现有占位符处理逻辑
  174. if (!string.IsNullOrEmpty(rule.url_pattern))
  175. {
  176. var urlRegex = new Regex(rule.url_pattern);
  177. var urlMatch = urlRegex.Match(text);
  178. if (urlMatch.Success)
  179. {
  180. for (var i = 1; i < urlMatch.Groups.Count; i++)
  181. {
  182. string val = urlMatch.Groups[i].Value;
  183. string tempPrompt = string.Empty;
  184. ReplaceProcessing(val, i, rule.replacements, ref postData, ref tempPrompt);
  185. }
  186. }
  187. }
  188. client.Post(postData);
  189. }
  190. //https://weitoutiao.zjurl.cn/tt_living/?app=news_article&module_name=Android_tt_url&room_id=MDUEDEq2rcgYaV8/SarVCgQTTv3vQwSt/yRnpWHHMzSd1LxjfgQQXTdEOrBDTfdJCzQ+vyO1aQ==&share_did=MS4wLjACAAAASvJER5nlmrwCRfsC2t661YpMGJbq2HtGQS31wppWCCm_j6e1qZ7w9bp94n9T6Ouq&share_token=99222dee-be68-4f79-af9e-c32eff162ea8&share_uid=MS4wLjABAAAA9PBhcsrMuvXb3-0VPXN47TT4TKDHqiZR-LCS4B2BodaRdd7OHm7rl1Xx3SxlLrbn&timestamp=1756194703&tt_from=copy_link&upstream_biz=client_share&utm_campaign=client_share&utm_medium=toutiao_android&utm_source=copy_link
  191. //https://weitoutiao.zjurl.cn/tt_living/?app=news_article&module_name=Android_tt_url&room_id=MDUEDEq2rcgYaV8%2FSarVCgQTTv3vQwSt%2FyRnpWHHMzSd1LxjfgQQXTdEOrBDTfdJCzQ%2BvyO1aQ%3D%3D&share_did=MS4wLjACAAAASvJER5nlmrwCRfsC2t661YpMGJbq2HtGQS31wppWCCm_j6e1qZ7w9bp94n9T6Ouq&share_token=99222dee-be68-4f79-af9e-c32eff162ea8&share_uid=MS4wLjABAAAA9PBhcsrMuvXb3-0VPXN47TT4TKDHqiZR-LCS4B2BodaRdd7OHm7rl1Xx3SxlLrbn&timestamp=1756194703&tt_from=copy_link&upstream_biz=client_share&utm_campaign=client_share&utm_medium=toutiao_android&utm_source=copy_link
  192. var response = await client.RequestAsync(url);
  193. content = response.Body();
  194. if (string.IsNullOrEmpty(content)) return null;
  195. }
  196. break;
  197. case UrlAction.None:
  198. //{
  199. // content = url;
  200. //}
  201. break;
  202. }
  203. }
  204. catch (Exception ex)
  205. {
  206. //Console.WriteLine($"{url}\t{rule.url_action}\t{ex.Message}");
  207. }
  208. if (!string.IsNullOrEmpty(rule.slice_pattern))
  209. {
  210. var regex = new Regex(rule.slice_pattern);
  211. var match = regex.Match(content);
  212. if (!match.Success) return null;
  213. content = match.Groups[1].Value;
  214. }
  215. if (rule.childs.Count > 0)
  216. {
  217. foreach (var sub_rule in rule.childs)
  218. {
  219. var e = await WorkEachRule(content, config, result, sub_rule);
  220. if (e != null) return e;
  221. }
  222. return null;
  223. }
  224. if (!string.IsNullOrEmpty(rule.body_pattern))
  225. {
  226. var regex = new Regex(rule.body_pattern);
  227. var match = regex.Match(content);
  228. if (!match.Success) return null;
  229. }
  230. var deeplink = rule.deeplink_template;
  231. var prompt_text = rule.prompt_text;
  232. try
  233. {
  234. var regex = new Regex(rule.pattern);
  235. var match = regex.Match(content);
  236. if (match.Success)
  237. {
  238. if (rule.resources.Count > 0)
  239. {
  240. foreach (var resource in rule.resources)
  241. {
  242. Dictionary<string, string> resResult = await ResourceProcessing(match, resource, rule.replacements);
  243. foreach (var e in resResult)
  244. {
  245. if (deeplink.Contains(e.Key))
  246. {
  247. deeplink = deeplink.Replace(e.Key, e.Value);
  248. }
  249. }
  250. }
  251. }
  252. if (match.Groups.Count > 1)
  253. {
  254. for (var i = 1; i < match.Groups.Count; i++)
  255. {
  256. string val = match.Groups[i].Value;
  257. switch (rule.plugin)
  258. {
  259. case "redu":
  260. string deeplink_url = await PluginDyParseAsync(text, val, result);
  261. if (!string.IsNullOrEmpty(deeplink_url))
  262. {
  263. result.itemName = prompt_text;
  264. result.deeplink_url = deeplink_url;
  265. result.success = true;
  266. return result;
  267. }
  268. break;
  269. }
  270. ReplaceProcessing(val, i, rule.replacements, ref deeplink, ref prompt_text);
  271. }
  272. if (rule.replace != null && rule.replace.Count > 0)
  273. {
  274. foreach (var r in rule.replace)
  275. {
  276. string key = r[0];
  277. if (string.IsNullOrEmpty(key)) continue;
  278. string val = string.Empty;
  279. if (r.Length > 1)
  280. {
  281. val = r[1];
  282. }
  283. deeplink = Regex.Replace(deeplink, key, val);
  284. }
  285. }
  286. deeplink = Regex.Replace(deeplink, @"\{\d+\}", string.Empty);
  287. prompt_text = Regex.Replace(prompt_text, @"\{\d+\}", string.Empty);
  288. if (!string.IsNullOrEmpty(deeplink))
  289. {
  290. result.deeplink_url = deeplink;
  291. result.itemName = prompt_text;
  292. result.success = true;
  293. return result;
  294. }
  295. }
  296. else
  297. {
  298. result.deeplink_url = deeplink;
  299. result.itemName = prompt_text;
  300. result.success = true;
  301. return result;
  302. }
  303. }
  304. }
  305. catch (Exception ex)
  306. {
  307. Console.WriteLine($"Error processing pattern '{rule.pattern}': {ex.Message}");
  308. }
  309. return null;
  310. }
  311. /// <summary>
  312. /// 占位符的处理
  313. /// </summary>
  314. /// <param name="word"></param>
  315. /// <param name="i"></param>
  316. /// <param name="deeplink"></param>
  317. /// <param name="prompt_text"></param>
  318. private static void ReplaceProcessing(string word, int i, List<ReplacementRule> replacements, ref string deeplink, ref string prompt_text)
  319. {
  320. if (deeplink.Contains("{url:"))
  321. {
  322. string encodedUrl = HttpUtility.UrlEncode(word);
  323. deeplink = deeplink.Replace($"{{url:{i - 1}}}", encodedUrl);
  324. }
  325. if (prompt_text.Contains("{url:"))
  326. {
  327. string encodedUrl = HttpUtility.UrlEncode(word);
  328. prompt_text = prompt_text.Replace($"{{url:{i - 1}}}", encodedUrl);
  329. }
  330. if (deeplink.Contains("{decode:"))
  331. {
  332. string encodedUrl = HttpUtility.UrlDecode(word);
  333. deeplink = deeplink.Replace($"{{decode:{i - 1}}}", encodedUrl);
  334. }
  335. if (prompt_text.Contains("{decode:"))
  336. {
  337. string encodedUrl = HttpUtility.UrlDecode(word);
  338. prompt_text = prompt_text.Replace($"{{decode:{i - 1}}}", encodedUrl);
  339. }
  340. //两次url编码
  341. if (deeplink.Contains("{url2:"))
  342. {
  343. string encodedUrl = HttpUtility.UrlEncode(word);
  344. encodedUrl = HttpUtility.UrlEncode(encodedUrl);
  345. deeplink = deeplink.Replace($"{{url2:{i - 1}}}", encodedUrl);
  346. }
  347. if (prompt_text.Contains("{url2:"))
  348. {
  349. string encodedUrl = HttpUtility.UrlEncode(word);
  350. encodedUrl = HttpUtility.UrlEncode(encodedUrl);
  351. prompt_text = prompt_text.Replace($"{{url2:{i - 1}}}", encodedUrl);
  352. }
  353. //三次url解码
  354. if (deeplink.Contains("{url3:"))
  355. {
  356. string encodedUrl = HttpUtility.UrlEncode(word);
  357. encodedUrl = HttpUtility.UrlEncode(encodedUrl);
  358. deeplink = deeplink.Replace($"{{url3:{i - 1}}}", encodedUrl);
  359. }
  360. if (prompt_text.Contains("{url3:"))
  361. {
  362. string encodedUrl = HttpUtility.UrlEncode(word);
  363. encodedUrl = HttpUtility.UrlEncode(encodedUrl);
  364. prompt_text = prompt_text.Replace($"{{url3:{i - 1}}}", encodedUrl);
  365. }
  366. //atob
  367. if (deeplink.Contains("{atob:"))
  368. {
  369. string encodedUrl = HttpUtility.UrlDecode(word);
  370. deeplink = deeplink.Replace($"{{atob:{i - 1}}}", encodedUrl.FromBase64());
  371. }
  372. if (prompt_text.Contains("{atob:"))
  373. {
  374. string encodedUrl = HttpUtility.UrlDecode(word);
  375. prompt_text = prompt_text.Replace($"{{atob:{i - 1}}}", encodedUrl.FromBase64());
  376. }
  377. //btoa
  378. if (deeplink.Contains("{btoa:"))
  379. {
  380. string encodedUrl = HttpUtility.UrlEncode(word);
  381. deeplink = deeplink.Replace($"{{btoa:{i - 1}}}", word.ToBase64());
  382. }
  383. if (prompt_text.Contains("{btoa:"))
  384. {
  385. string encodedUrl = HttpUtility.UrlEncode(word);
  386. prompt_text = prompt_text.Replace($"{{btoa:{i - 1}}}", word.ToBase64());
  387. }
  388. deeplink = deeplink.Replace($"{{{i - 1}}}", word);
  389. prompt_text = prompt_text.Replace($"{{{i - 1}}}", word);
  390. // 新增 <<>> 格式处理
  391. if (replacements != null)
  392. {
  393. deeplink = Regex.Replace(deeplink, @"<<(\w+):([^>]+)>>", match =>
  394. {
  395. var ruleName = match.Groups[1].Value;
  396. var value = match.Groups[2].Value;
  397. // 查找并应用替换规则
  398. var rule = replacements.FirstOrDefault(r => r.Name == ruleName);
  399. if (rule != null)
  400. {
  401. foreach (var r in rule.Rules)
  402. {
  403. if (r.When.Evaluate(value))
  404. {
  405. return r.Then;
  406. }
  407. }
  408. return rule.Default;
  409. }
  410. return value;
  411. });
  412. prompt_text = Regex.Replace(prompt_text, @"<<(\w+):([^>]+)>>", match =>
  413. {
  414. var ruleName = match.Groups[1].Value;
  415. var value = match.Groups[2].Value;
  416. // 查找并应用替换规则
  417. var rule = replacements.FirstOrDefault(r => r.Name == ruleName);
  418. if (rule != null)
  419. {
  420. foreach (var r in rule.Rules)
  421. {
  422. if (r.When.Evaluate(value))
  423. {
  424. return r.Then;
  425. }
  426. }
  427. return rule.Default;
  428. }
  429. return value;
  430. });
  431. }
  432. // 最后处理普通的索引替换
  433. deeplink = deeplink.Replace($"{{{i - 1}}}", word);
  434. prompt_text = prompt_text.Replace($"{{{i - 1}}}", word);
  435. }
  436. private static async Task<Dictionary<string, string>> ResourceProcessing(Match baseMatch, DeeplinkResources resource, List<ReplacementRule> replacements)
  437. {
  438. Dictionary<string, string> result = new Dictionary<string, string>();
  439. string url = resource.url_pattern;
  440. string prompt_text = string.Empty;
  441. for (var i = 1; i < baseMatch.Groups.Count; i++)
  442. {
  443. string val = baseMatch.Groups[i].Value;
  444. ReplaceProcessing(val, i, replacements, ref url, ref prompt_text);
  445. }
  446. string content = string.Empty;
  447. try
  448. {
  449. switch (resource.url_action)
  450. {
  451. case UrlAction.Redirect:
  452. {
  453. var response = await new WebClientUtility
  454. {
  455. Proxy = ProxyNodesCore.RandomOne(),
  456. UserAgent = ProviderFakeUserAgent.RandomMobile,
  457. AllowAutoRedirect = false
  458. }.RequestAsync(url);
  459. if (response.ResponseMessage != null)
  460. {
  461. content = response.ResponseMessage.Headers.Location.ToString();
  462. content = content.UrlDecode();
  463. }
  464. }
  465. break;
  466. case UrlAction.Body:
  467. case UrlAction.JsonBody:
  468. {
  469. //var response = await new WebClientUtility
  470. //{
  471. // Proxy = ProxyNodesCore.RandomOne(),
  472. // UserAgent = ProviderFakeUserAgent.RandomMobile,
  473. // AllowAutoRedirect = true
  474. //}.RequestAsync(url);
  475. var client = new WebClientUtility
  476. {
  477. Proxy = ProxyNodesCore.RandomOne(),
  478. UserAgent = ProviderFakeUserAgent.RandomMobile,
  479. AllowAutoRedirect = true
  480. };
  481. if (!string.IsNullOrEmpty(resource.post))
  482. {
  483. string postData = resource.post;
  484. // 处理POST数据中的占位符
  485. for (var i = 1; i < baseMatch.Groups.Count; i++)
  486. {
  487. string val = baseMatch.Groups[i].Value;
  488. ReplaceProcessing(val, i, replacements, ref postData, ref prompt_text);
  489. }
  490. client.Post(postData);
  491. }
  492. var response = await client.RequestAsync(url);
  493. content = response.Body();
  494. }
  495. break;
  496. case UrlAction.None:
  497. //{
  498. // content = url;
  499. //}
  500. break;
  501. }
  502. }
  503. catch (Exception ex)
  504. {
  505. //Console.WriteLine($"{url}\t{rule.url_action}\t{ex.Message}");
  506. }
  507. if (string.IsNullOrEmpty(content)) { return result; }
  508. try
  509. {
  510. JsonElement root = default;
  511. if (UrlAction.JsonBody.Equals(resource.url_action))
  512. {
  513. root = content.Convert2JsonElement();
  514. }
  515. for (int i = 0; i < resource.pattern.Count; i++)
  516. {
  517. string val = string.Empty;
  518. if (UrlAction.JsonBody.Equals(resource.url_action))
  519. {
  520. val = root.PathRead(resource.pattern[i], string.Empty);
  521. }
  522. else
  523. {
  524. var regex = new Regex(resource.pattern[i]);
  525. var match = regex.Match(content);
  526. if (match.Success)
  527. {
  528. val = match.Groups[1].Value;
  529. }
  530. }
  531. string key = $"{{{i}@{resource.name}}}";
  532. result.Add(key, val);
  533. }
  534. }
  535. catch (Exception ex) { }
  536. return result;
  537. }
  538. public static async Task<string> PluginDyParseAsync(string content, string matchText, DeeplinkParseDataDTO deeplinkResult, CancellationToken cancellationToken = default)
  539. {
  540. string ip = deeplinkResult.ip;
  541. string oaid = deeplinkResult.oaid;
  542. var result = DyUnionPlus.GetFormattedObject(content, ip, oaid);
  543. result.create_time = deeplinkResult.create_time;
  544. result.rawContent = content;
  545. bool IfExceptional = false;
  546. try
  547. {
  548. if (DyUnionPlus.ShouldIgnoreRequest(ip, oaid, out string reason))
  549. {
  550. result.success = false;
  551. result.message = "放弃转链";
  552. result.reason = reason;
  553. _ = TkLogCore.ParseLogAsync(result);
  554. return null;
  555. }
  556. var account = ReduPoolCore.GetOne();
  557. if (account == null)
  558. {
  559. result.success = false;
  560. result.message = "放弃转链";
  561. result.reason = "没有匹配账号";
  562. _ = TkLogCore.ParseLogAsync(result);
  563. return null;
  564. }
  565. var now = DateTime.Now;
  566. var ts2 = now - result.create_time;
  567. result.elapsedTime2 = (int)ts2.TotalMilliseconds;
  568. result = await DyUnionPlus.DyParseAsync(account, matchText, result, cancellationToken);
  569. switch (oaid)
  570. {
  571. case "3B191CFA4C6B48F9BA459E915B57743BEC7D424979CC9C51BCAD0C245B1C7BA2":
  572. case "6D5D56F3073844F2912B4552CEDF9E3BEC7D424979CC9C51C5461106F1915660":
  573. case "4CA55979100C4B5BAAA2AF4ABE15C360602e915cedae5786405419ccc9f4e3f8":
  574. break;
  575. default:
  576. result.deeplink_url = result.deeplink_url.Replace("snssdk1128://", "snssdk561124://");
  577. break;
  578. }
  579. var ts3 = DateTime.Now - now;
  580. result.elapsedTime3 = (int)ts3.TotalMilliseconds;
  581. }
  582. catch (Exception ex)
  583. {
  584. if (ex.Message.Contains("was canceled"))
  585. {
  586. result.success = false;
  587. result.message = "放弃转链";
  588. result.reason = "请求超时";
  589. }
  590. else
  591. {
  592. IfExceptional = true;
  593. _ = new LoggerLibrary("unionParse", "dy_error")
  594. .Info(ip, oaid)
  595. .Info(content)
  596. .Info(ex.Message, ex.StackTrace)
  597. .SaveAsync();
  598. NotifyCore.Notify(new NifyMessage
  599. {
  600. message = $"【转链异常DY】\n{content}\n\n{ex.Message}\n{ex.StackTrace}",
  601. priority = NifyMessagePriority.high,
  602. tags = ["red_circle"]
  603. });
  604. result.success = false;
  605. result.message = "内部错误";
  606. result.reason = "转链接口异常";
  607. }
  608. }
  609. _ = TkLogCore.ParseLogAsync(result);
  610. return result.deeplink_url;
  611. }
  612. }
  613. }