DeeplinkParseCore.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537
  1. 
  2. using COSXML.Network;
  3. using dodohold.core;
  4. using Microsoft.AspNetCore.Mvc;
  5. using Microsoft.Extensions.FileSystemGlobbing.Internal;
  6. using Sayaka.Common;
  7. using Spire.Pdf.Annotations;
  8. using Spire.Pdf.Exporting.XPS.Schema;
  9. using System.Security.Cryptography;
  10. using System.Text.RegularExpressions;
  11. using System.Web;
  12. using System.Xml.Linq;
  13. using TencentCloud.Tcss.V20201101.Models;
  14. namespace molilian.core
  15. {
  16. public partial class DeeplinkParseCore
  17. {
  18. private static string _end_point;
  19. private static readonly string[] separators = ["\r\n", "\n"];
  20. static DeeplinkParseCore()
  21. {
  22. _end_point = Environment.GetEnvironmentVariable("EndPoint");
  23. }
  24. public static DeeplinkParseDataDTO GetFormattedObject(string content, string channel, string ip = "", string oaid = "")
  25. {
  26. return new DeeplinkParseDataDTO()
  27. {
  28. message = string.Empty,
  29. success = false,
  30. content = content,
  31. ip = ip,
  32. oaid = oaid,
  33. elapsedTime = 0,
  34. create_time = DateTime.Now,
  35. end_point = _end_point,
  36. };
  37. }
  38. public static async Task<ActionResult> ParseAsync(string content, string channel, string ip, string oaid)
  39. {
  40. var result = GetFormattedObject(content, channel, ip, oaid);
  41. content = content.Trim();
  42. var rule = DeeplinkParseRuleCore.GetOne(channel);
  43. if (rule == null)
  44. {
  45. result.success = false;
  46. result.message = "转链失败";
  47. result.reason = "无效平台";
  48. _ = TkLogCore.ParseLogAsync(result);
  49. return new APIResult(new
  50. {
  51. result.success,
  52. result.message,
  53. result.reason,
  54. });
  55. }
  56. if (ShouldIgnoreRequest(rule.IgnorePercentageCity, ip, oaid, out string reason))
  57. {
  58. result.success = false;
  59. result.message = "放弃转链";
  60. result.reason = reason;
  61. _ = TkLogCore.ParseLogAsync(result);
  62. return new APIResult(new
  63. {
  64. result.success,
  65. result.message,
  66. result.reason,
  67. });
  68. }
  69. result.channel_id = rule.channel_id;
  70. result.channel_name = rule.channel_name;
  71. result = await GenerateDeeplink(content, rule, result);
  72. _ = TkLogCore.ParseLogAsync(result);
  73. return new APIResult(new
  74. {
  75. result.success,
  76. result.message,
  77. channel = result.channel_name,
  78. result.deeplink_url,
  79. result.itemName,
  80. });
  81. }
  82. public static bool ShouldIgnoreRequest(string ignorePercentageCity, string ip, string oaid, out string reason)
  83. {
  84. reason = string.Empty;
  85. try
  86. {
  87. // IP和流量控制
  88. string? ipInfo = IP2RegionPlus.Search(ip);
  89. if (AlimamaPlus.IgnoreRegionIncluded(ignorePercentageCity, ipInfo, out string regionInfo))
  90. {
  91. reason = $"地区控制:{regionInfo}";
  92. return true;
  93. }
  94. }
  95. catch (Exception ex)
  96. {
  97. reason = "配置异常";
  98. }
  99. return false;
  100. }
  101. public static async Task<DeeplinkParseDataDTO> GenerateDeeplink(string text, DeeplinkParseRuleDTO config, DeeplinkParseDataDTO result)
  102. {
  103. try
  104. {
  105. var list = config.rules.Convert2Object<List<DeeplinkParseRule>>();
  106. foreach (var rule in list)
  107. {
  108. var e = await WorkEachRule(text, config, result, rule);
  109. if (e != null) return e;
  110. }
  111. }
  112. catch (Exception ex)
  113. {
  114. }
  115. result.success = false;
  116. return result;
  117. }
  118. private static async Task<DeeplinkParseDataDTO?> WorkEachRule(string text, DeeplinkParseRuleDTO config, DeeplinkParseDataDTO result, DeeplinkParseRule rule)
  119. {
  120. if (!rule.enable) return null;
  121. string content = text;
  122. if (!string.IsNullOrEmpty(rule.url_pattern))
  123. {
  124. var regex = new Regex(rule.url_pattern);
  125. var match = regex.Match(content);
  126. if (!match.Success) return null;
  127. content = match.Groups[1].Value;
  128. }
  129. string url = ToolParsePlus.GetLink(text);
  130. try
  131. {
  132. switch (rule.url_action)
  133. {
  134. case UrlAction.Redirect:
  135. {
  136. var response = await new WebClientUtility
  137. {
  138. Proxy = ProxyNodesCore.RandomOne(),
  139. UserAgent = ProviderFakeUserAgent.RandomMobile,
  140. AllowAutoRedirect = false
  141. }.RequestAsync(url);
  142. if (response.ResponseMessage != null)
  143. {
  144. content = response.ResponseMessage.Headers.Location.ToString();
  145. content = content.UrlDecode();
  146. }
  147. }
  148. break;
  149. case UrlAction.Body:
  150. {
  151. var response = await new WebClientUtility
  152. {
  153. Proxy = ProxyNodesCore.RandomOne(),
  154. UserAgent = ProviderFakeUserAgent.RandomMobile,
  155. AllowAutoRedirect = true
  156. }.RequestAsync(url);
  157. content = response.Body();
  158. }
  159. break;
  160. case UrlAction.None:
  161. //{
  162. // content = url;
  163. //}
  164. break;
  165. }
  166. }
  167. catch (Exception ex)
  168. {
  169. //Console.WriteLine($"{url}\t{rule.url_action}\t{ex.Message}");
  170. }
  171. if (rule.childs.Count > 0)
  172. {
  173. foreach (var sub_rule in rule.childs)
  174. {
  175. var e = await WorkEachRule(content, config, result, sub_rule);
  176. if (e != null) return e;
  177. }
  178. return null;
  179. }
  180. if (!string.IsNullOrEmpty(rule.body_pattern))
  181. {
  182. var regex = new Regex(rule.body_pattern);
  183. var match = regex.Match(content);
  184. if (!match.Success) return null;
  185. }
  186. var deeplink = rule.deeplink_template;
  187. var prompt_text = rule.prompt_text;
  188. try
  189. {
  190. var regex = new Regex(rule.pattern);
  191. var match = regex.Match(content);
  192. if (match.Success)
  193. {
  194. if (rule.resources.Count > 0)
  195. {
  196. foreach (var resource in rule.resources)
  197. {
  198. Dictionary<string, string> resResult = await ResourceProcessing(match, resource);
  199. foreach (var e in resResult)
  200. {
  201. if (deeplink.Contains(e.Key))
  202. {
  203. deeplink = deeplink.Replace(e.Key, e.Value);
  204. }
  205. }
  206. }
  207. }
  208. if (match.Groups.Count > 1)
  209. {
  210. for (var i = 1; i < match.Groups.Count; i++)
  211. {
  212. string val = match.Groups[i].Value;
  213. switch (rule.plugin)
  214. {
  215. case "redu":
  216. string deeplink_url = await PluginDyParseAsync(text, val, result);
  217. if (!string.IsNullOrEmpty(deeplink_url))
  218. {
  219. result.itemName = prompt_text;
  220. result.deeplink_url = deeplink_url;
  221. result.success = true;
  222. return result;
  223. }
  224. break;
  225. }
  226. ReplaceProcessing(val, i, ref deeplink, ref prompt_text);
  227. }
  228. deeplink = Regex.Replace(deeplink, @"\{\d+\}", string.Empty);
  229. prompt_text = Regex.Replace(prompt_text, @"\{\d+\}", string.Empty);
  230. if (!string.IsNullOrEmpty(deeplink))
  231. {
  232. result.deeplink_url = deeplink;
  233. result.itemName = prompt_text;
  234. result.success = true;
  235. return result;
  236. }
  237. }
  238. else
  239. {
  240. result.deeplink_url = deeplink;
  241. result.itemName = prompt_text;
  242. result.success = true;
  243. return result;
  244. }
  245. }
  246. }
  247. catch (Exception ex)
  248. {
  249. Console.WriteLine($"Error processing pattern '{rule.pattern}': {ex.Message}");
  250. }
  251. return null;
  252. }
  253. /// <summary>
  254. /// 占位符的处理
  255. /// </summary>
  256. /// <param name="word"></param>
  257. /// <param name="i"></param>
  258. /// <param name="deeplink"></param>
  259. /// <param name="prompt_text"></param>
  260. private static void ReplaceProcessing(string word, int i, ref string deeplink, ref string prompt_text)
  261. {
  262. if (deeplink.Contains("{url:"))
  263. {
  264. string encodedUrl = HttpUtility.UrlEncode(word);
  265. deeplink = deeplink.Replace($"{{url:{i - 1}}}", encodedUrl);
  266. }
  267. if (prompt_text.Contains("{url:"))
  268. {
  269. string encodedUrl = HttpUtility.UrlEncode(word);
  270. prompt_text = prompt_text.Replace($"{{url:{i - 1}}}", encodedUrl);
  271. }
  272. if (deeplink.Contains("{decode:"))
  273. {
  274. string encodedUrl = HttpUtility.UrlDecode(word);
  275. deeplink = deeplink.Replace($"{{decode:{i - 1}}}", encodedUrl);
  276. }
  277. if (prompt_text.Contains("{decode:"))
  278. {
  279. string encodedUrl = HttpUtility.UrlDecode(word);
  280. prompt_text = prompt_text.Replace($"{{decode:{i - 1}}}", encodedUrl);
  281. }
  282. //两次url编码
  283. if (deeplink.Contains("{url2:"))
  284. {
  285. string encodedUrl = HttpUtility.UrlEncode(word);
  286. encodedUrl = HttpUtility.UrlEncode(encodedUrl);
  287. deeplink = deeplink.Replace($"{{url2:{i - 1}}}", encodedUrl);
  288. }
  289. if (prompt_text.Contains("{url2:"))
  290. {
  291. string encodedUrl = HttpUtility.UrlEncode(word);
  292. encodedUrl = HttpUtility.UrlEncode(encodedUrl);
  293. prompt_text = prompt_text.Replace($"{{url2:{i - 1}}}", encodedUrl);
  294. }
  295. //三次url解码
  296. if (deeplink.Contains("{url3:"))
  297. {
  298. string encodedUrl = HttpUtility.UrlEncode(word);
  299. encodedUrl = HttpUtility.UrlEncode(encodedUrl);
  300. deeplink = deeplink.Replace($"{{url3:{i - 1}}}", encodedUrl);
  301. }
  302. if (prompt_text.Contains("{url3:"))
  303. {
  304. string encodedUrl = HttpUtility.UrlEncode(word);
  305. encodedUrl = HttpUtility.UrlEncode(encodedUrl);
  306. prompt_text = prompt_text.Replace($"{{url3:{i - 1}}}", encodedUrl);
  307. }
  308. //atob
  309. if (deeplink.Contains("{atob:"))
  310. {
  311. string encodedUrl = HttpUtility.UrlDecode(word);
  312. deeplink = deeplink.Replace($"{{atob:{i - 1}}}", encodedUrl.FromBase64());
  313. }
  314. if (prompt_text.Contains("{atob:"))
  315. {
  316. string encodedUrl = HttpUtility.UrlDecode(word);
  317. prompt_text = prompt_text.Replace($"{{atob:{i - 1}}}", encodedUrl.FromBase64());
  318. }
  319. //btoa
  320. if (deeplink.Contains("{btoa:"))
  321. {
  322. string encodedUrl = HttpUtility.UrlEncode(word);
  323. deeplink = deeplink.Replace($"{{btoa:{i - 1}}}", word.ToBase64());
  324. }
  325. if (prompt_text.Contains("{btoa:"))
  326. {
  327. string encodedUrl = HttpUtility.UrlEncode(word);
  328. prompt_text = prompt_text.Replace($"{{btoa:{i - 1}}}", word.ToBase64());
  329. }
  330. deeplink = deeplink.Replace($"{{{i - 1}}}", word);
  331. prompt_text = prompt_text.Replace($"{{{i - 1}}}", word);
  332. }
  333. private static async Task<Dictionary<string, string>> ResourceProcessing(Match baseMatch, DeeplinkResources resource)
  334. {
  335. Dictionary<string, string> result = new Dictionary<string, string>();
  336. string url = resource.url_pattern;
  337. string prompt_text = string.Empty;
  338. for (var i = 1; i < baseMatch.Groups.Count; i++)
  339. {
  340. string val = baseMatch.Groups[i].Value;
  341. ReplaceProcessing(val, i, ref url, ref prompt_text);
  342. }
  343. string content = string.Empty;
  344. try
  345. {
  346. switch (resource.url_action)
  347. {
  348. case UrlAction.Redirect:
  349. {
  350. var response = await new WebClientUtility
  351. {
  352. Proxy = ProxyNodesCore.RandomOne(),
  353. UserAgent = ProviderFakeUserAgent.RandomMobile,
  354. AllowAutoRedirect = false
  355. }.RequestAsync(url);
  356. if (response.ResponseMessage != null)
  357. {
  358. content = response.ResponseMessage.Headers.Location.ToString();
  359. content = content.UrlDecode();
  360. }
  361. }
  362. break;
  363. case UrlAction.Body:
  364. {
  365. var response = await new WebClientUtility
  366. {
  367. Proxy = ProxyNodesCore.RandomOne(),
  368. UserAgent = ProviderFakeUserAgent.RandomMobile,
  369. AllowAutoRedirect = true
  370. }.RequestAsync(url);
  371. content = response.Body();
  372. }
  373. break;
  374. case UrlAction.None:
  375. //{
  376. // content = url;
  377. //}
  378. break;
  379. }
  380. }
  381. catch (Exception ex)
  382. {
  383. //Console.WriteLine($"{url}\t{rule.url_action}\t{ex.Message}");
  384. }
  385. if (string.IsNullOrEmpty(content)) { return result; }
  386. for (int i = 0; i < resource.pattern.Count; i++)
  387. {
  388. string val = string.Empty;
  389. var regex = new Regex(resource.pattern[i]);
  390. var match = regex.Match(content);
  391. if (match.Success)
  392. {
  393. val = match.Groups[1].Value;
  394. }
  395. string key = $"{{{i}@{resource.name}}}";
  396. result.Add(key, val);
  397. }
  398. return result;
  399. }
  400. public static async Task<string> PluginDyParseAsync(string content, string matchText, DeeplinkParseDataDTO deeplinkResult, CancellationToken cancellationToken = default)
  401. {
  402. string ip = deeplinkResult.ip;
  403. string oaid = deeplinkResult.oaid;
  404. var result = DyUnionPlus.GetFormattedObject(content, ip, oaid);
  405. result.create_time = deeplinkResult.create_time;
  406. result.rawContent = content;
  407. bool IfExceptional = false;
  408. try
  409. {
  410. if (DyUnionPlus.ShouldIgnoreRequest(ip, oaid, out string reason))
  411. {
  412. result.success = false;
  413. result.message = "放弃转链";
  414. result.reason = reason;
  415. _ = TkLogCore.ParseLogAsync(result);
  416. return null;
  417. }
  418. var account = ReduPoolCore.GetOne();
  419. if (account == null)
  420. {
  421. result.success = false;
  422. result.message = "放弃转链";
  423. result.reason = "没有匹配账号";
  424. _ = TkLogCore.ParseLogAsync(result);
  425. return null;
  426. }
  427. var now = DateTime.Now;
  428. var ts2 = now - result.create_time;
  429. result.elapsedTime2 = (int)ts2.TotalMilliseconds;
  430. result = await DyUnionPlus.DyParseAsync(account, matchText, result, cancellationToken);
  431. switch (oaid)
  432. {
  433. case "3B191CFA4C6B48F9BA459E915B57743BEC7D424979CC9C51BCAD0C245B1C7BA2":
  434. case "6D5D56F3073844F2912B4552CEDF9E3BEC7D424979CC9C51C5461106F1915660":
  435. case "4CA55979100C4B5BAAA2AF4ABE15C360602e915cedae5786405419ccc9f4e3f8":
  436. break;
  437. default:
  438. result.deeplink_url = result.deeplink_url.Replace("snssdk1128://", "snssdk561124://");
  439. break;
  440. }
  441. var ts3 = DateTime.Now - now;
  442. result.elapsedTime3 = (int)ts3.TotalMilliseconds;
  443. }
  444. catch (Exception ex)
  445. {
  446. if (ex.Message.Contains("was canceled"))
  447. {
  448. result.success = false;
  449. result.message = "放弃转链";
  450. result.reason = "请求超时";
  451. }
  452. else
  453. {
  454. IfExceptional = true;
  455. _ = new LoggerLibrary("unionParse", "dy_error")
  456. .Info(ip, oaid)
  457. .Info(content)
  458. .Info(ex.Message, ex.StackTrace)
  459. .SaveAsync();
  460. NotifyCore.Notify(new NifyMessage
  461. {
  462. message = $"【转链异常DY】\n{content}\n\n{ex.Message}\n{ex.StackTrace}",
  463. priority = NifyMessagePriority.high,
  464. tags = ["red_circle"]
  465. });
  466. result.success = false;
  467. result.message = "内部错误";
  468. result.reason = "转链接口异常";
  469. }
  470. }
  471. _ = TkLogCore.ParseLogAsync(result);
  472. return result.deeplink_url;
  473. }
  474. }
  475. }