using COSXML.Network; using dodohold.core; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.FileSystemGlobbing.Internal; using Sayaka.Common; using Spire.Pdf.Annotations; using System.Security.Cryptography; using System.Text.RegularExpressions; using System.Web; using System.Xml.Linq; namespace molilian.core { public partial class DeeplinkParseCore { private static string _end_point; private static readonly string[] separators = ["\r\n", "\n"]; static DeeplinkParseCore() { _end_point = Environment.GetEnvironmentVariable("EndPoint"); } public static DeeplinkParseDataDTO GetFormattedObject(string content, string channel, string ip = "", string oaid = "") { return new DeeplinkParseDataDTO() { message = string.Empty, success = false, content = content, ip = ip, oaid = oaid, elapsedTime = 0, create_time = DateTime.Now, end_point = _end_point, }; } public static async Task ParseAsync(string content, string channel, string ip, string oaid) { var result = GetFormattedObject(content, channel, ip, oaid); content = content.Trim(); var rule = DeeplinkParseRuleCore.GetOne(channel); if (rule == null) { result.success = false; result.message = "转链失败"; result.reason = "无效平台"; _ = TkLogCore.ParseLogAsync(result); return new APIResult(new { result.success, result.message, result.reason, }); } if (ShouldIgnoreRequest(rule.IgnorePercentageCity, ip, oaid, out string reason)) { result.success = false; result.message = "放弃转链"; result.reason = reason; _ = TkLogCore.ParseLogAsync(result); return new APIResult(new { result.success, result.message, result.reason, }); } result.channel_id = rule.channel_id; result.channel_name = rule.channel_name; result = await GenerateDeeplink(content, rule, result); _ = TkLogCore.ParseLogAsync(result); return new APIResult(new { result.success, result.message, channel = result.channel_name, result.deeplink_url, result.itemName, }); } public static bool ShouldIgnoreRequest(string ignorePercentageCity, string ip, string oaid, out string reason) { reason = string.Empty; try { // IP和流量控制 string? ipInfo = IP2RegionPlus.Search(ip); if (AlimamaPlus.IgnoreRegionIncluded(ignorePercentageCity, ipInfo, out string regionInfo)) { reason = $"地区控制:{regionInfo}"; return true; } } catch (Exception ex) { reason = "配置异常"; } return false; } public static async Task GenerateDeeplink(string text, DeeplinkParseRuleDTO config, DeeplinkParseDataDTO result) { try { var list = config.rules.Convert2Object>(); foreach (var rule in list) { var e = await WorkEachRule(text, config, result, rule); if (e != null) return e; } } catch (Exception ex) { } result.success = false; return result; } private static async Task WorkEachRule(string text, DeeplinkParseRuleDTO config, DeeplinkParseDataDTO result, DeeplinkParseRule rule) { if (!rule.enable) return null; string content = text; if (!string.IsNullOrEmpty(rule.url_pattern)) { var regex = new Regex(rule.url_pattern); var match = regex.Match(content); if (!match.Success) return null; content = match.Groups[1].Value; } string url = ToolParsePlus.GetLink(text); try { switch (rule.url_action) { case UrlAction.Redirect: { var response = await new WebClientUtility { Proxy = ProxyNodesCore.RandomOne(), UserAgent = ProviderFakeUserAgent.RandomMobile, AllowAutoRedirect = false }.RequestAsync(url); if (response.ResponseMessage != null) { content = response.ResponseMessage.Headers.Location.ToString(); content = content.UrlDecode(); } } break; case UrlAction.Body: { var response = await new WebClientUtility { Proxy = ProxyNodesCore.RandomOne(), UserAgent = ProviderFakeUserAgent.RandomMobile, AllowAutoRedirect = true }.RequestAsync(url); content = response.Body(); } break; case UrlAction.None: //{ // content = url; //} break; } } catch (Exception ex) { //Console.WriteLine($"{url}\t{rule.url_action}\t{ex.Message}"); } if (rule.childs.Count > 0) { foreach (var sub_rule in rule.childs) { var e = await WorkEachRule(content, config, result, sub_rule); if (e != null) return e; } return null; } if (!string.IsNullOrEmpty(rule.body_pattern)) { var regex = new Regex(rule.body_pattern); var match = regex.Match(content); if (!match.Success) return null; } var deeplink = rule.deeplink_template; var prompt_text = rule.prompt_text; try { var regex = new Regex(rule.pattern); var match = regex.Match(content); if (match.Success) { if (match.Groups.Count > 1) { for (var i = 1; i < match.Groups.Count; i++) { string val = match.Groups[i].Value; switch (rule.plugin) { case "redu": string deeplink_url = await PluginDyParseAsync(text, val, result); if (!string.IsNullOrEmpty(deeplink_url)) { result.itemName = prompt_text; result.deeplink_url = deeplink_url; result.success = true; return result; } break; } if (deeplink.Contains("{url:")) { string encodedUrl = HttpUtility.UrlEncode(val); deeplink = deeplink.Replace($"{{url:{i - 1}}}", encodedUrl); //deeplink = deeplink.Replace($"{{url:{i - 1}}}", val.UrlEncode()); } if (prompt_text.Contains("{url:")) { string encodedUrl = HttpUtility.UrlEncode(val); prompt_text = prompt_text.Replace($"{{url:{i - 1}}}", encodedUrl); //prompt_text = prompt_text.Replace($"{{url:{i - 1}}}", val.UrlEncode()); } if (deeplink.Contains("{decode:")) { string encodedUrl = HttpUtility.UrlDecode(val); deeplink = deeplink.Replace($"{{decode:{i - 1}}}", encodedUrl); //deeplink = deeplink.Replace($"{{url:{i - 1}}}", val.UrlEncode()); } if (prompt_text.Contains("{decode:")) { string encodedUrl = HttpUtility.UrlDecode(val); prompt_text = prompt_text.Replace($"{{decode:{i - 1}}}", encodedUrl); } //两次url编码 if (deeplink.Contains("{url2:")) { string encodedUrl = HttpUtility.UrlEncode(val); encodedUrl = HttpUtility.UrlEncode(encodedUrl); deeplink = deeplink.Replace($"{{url2:{i - 1}}}", encodedUrl); } if (prompt_text.Contains("{url2:")) { string encodedUrl = HttpUtility.UrlEncode(val); encodedUrl = HttpUtility.UrlEncode(encodedUrl); prompt_text = prompt_text.Replace($"{{url2:{i - 1}}}", encodedUrl); } //三次url解码 if (deeplink.Contains("{url3:")) { string encodedUrl = HttpUtility.UrlEncode(val); encodedUrl = HttpUtility.UrlEncode(encodedUrl); deeplink = deeplink.Replace($"{{url3:{i - 1}}}", encodedUrl); } if (prompt_text.Contains("{url3:")) { string encodedUrl = HttpUtility.UrlEncode(val); encodedUrl = HttpUtility.UrlEncode(encodedUrl); prompt_text = prompt_text.Replace($"{{url3:{i - 1}}}", encodedUrl); } //atob if (deeplink.Contains("{atob:")) { string encodedUrl = HttpUtility.UrlDecode(val); deeplink = deeplink.Replace($"{{atob:{i - 1}}}", encodedUrl.FromBase64()); } if (prompt_text.Contains("{atob:")) { string encodedUrl = HttpUtility.UrlDecode(val); prompt_text = prompt_text.Replace($"{{atob:{i - 1}}}", encodedUrl.FromBase64()); } //btoa if (deeplink.Contains("{btoa:")) { string encodedUrl = HttpUtility.UrlEncode(val); deeplink = deeplink.Replace($"{{btoa:{i - 1}}}", val.ToBase64()); } if (prompt_text.Contains("{btoa:")) { string encodedUrl = HttpUtility.UrlEncode(val); prompt_text = prompt_text.Replace($"{{btoa:{i - 1}}}", val.ToBase64()); } deeplink = deeplink.Replace($"{{{i - 1}}}", val); prompt_text = prompt_text.Replace($"{{{i - 1}}}", val); } deeplink = Regex.Replace(deeplink, @"\{\d+\}", string.Empty); prompt_text = Regex.Replace(prompt_text, @"\{\d+\}", string.Empty); if (!string.IsNullOrEmpty(deeplink)) { result.deeplink_url = deeplink; result.itemName = prompt_text; result.success = true; return result; } } else { result.deeplink_url = deeplink; result.itemName = prompt_text; result.success = true; return result; } } } catch (Exception ex) { Console.WriteLine($"Error processing pattern '{rule.pattern}': {ex.Message}"); } return null; } public static async Task PluginDyParseAsync(string content, string matchText, DeeplinkParseDataDTO deeplinkResult, CancellationToken cancellationToken = default) { string ip = deeplinkResult.ip; string oaid = deeplinkResult.oaid; var result = DyUnionPlus.GetFormattedObject(content, ip, oaid); result.create_time = deeplinkResult.create_time; result.rawContent = content; bool IfExceptional = false; try { if (DyUnionPlus.ShouldIgnoreRequest(ip, oaid, out string reason)) { result.success = false; result.message = "放弃转链"; result.reason = reason; _ = TkLogCore.ParseLogAsync(result); return null; } var account = ReduPoolCore.GetOne(); if (account == null) { result.success = false; result.message = "放弃转链"; result.reason = "没有匹配账号"; _ = TkLogCore.ParseLogAsync(result); return null; } var now = DateTime.Now; var ts2 = now - result.create_time; result.elapsedTime2 = (int)ts2.TotalMilliseconds; result = await DyUnionPlus.DyParseAsync(account, matchText, result, cancellationToken); result.deeplink_url = result.deeplink_url.Replace("snssdk1128://", "snssdk561124://"); var ts3 = DateTime.Now - now; result.elapsedTime3 = (int)ts3.TotalMilliseconds; } catch (Exception ex) { if (ex.Message.Contains("was canceled")) { result.success = false; result.message = "放弃转链"; result.reason = "请求超时"; } else { IfExceptional = true; _ = new LoggerLibrary("unionParse", "dy_error") .Info(ip, oaid) .Info(content) .Info(ex.Message, ex.StackTrace) .SaveAsync(); NotifyCore.Notify(new NifyMessage { message = $"【转链异常DY】\n{content}\n\n{ex.Message}\n{ex.StackTrace}", priority = NifyMessagePriority.high, tags = ["red_circle"] }); result.success = false; result.message = "内部错误"; result.reason = "转链接口异常"; } } var ts = DateTime.Now - result.create_time; result.elapsedTime = (int)ts.TotalMilliseconds; _ = TkLogCore.ParseLogAsync(result); return result.deeplink_url; } } }