using COSXML.Network; using dodohold.core; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.FileSystemGlobbing.Internal; using Sayaka.Common; using Spire.Pdf.Annotations; using Spire.Pdf.Exporting.XPS.Schema; using System.Security.Cryptography; using System.Text.Json; using System.Text.RegularExpressions; using System.Web; using System.Xml.Linq; using TencentCloud.Tcss.V20201101.Models; 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 (rule.resources.Count > 0) { foreach (var resource in rule.resources) { Dictionary resResult = await ResourceProcessing(match, resource, rule.replacements); foreach (var e in resResult) { if (deeplink.Contains(e.Key)) { deeplink = deeplink.Replace(e.Key, e.Value); } } } } 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; } ReplaceProcessing(val, i, rule.replacements, ref deeplink, ref prompt_text); } if (rule.replace != null && rule.replace.Count > 0) { foreach (var r in rule.replace) { string key = r[0]; if (string.IsNullOrEmpty(key)) continue; string val = string.Empty; if (r.Length > 1) { val = r[1]; } deeplink = Regex.Replace(deeplink, key, 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; } /// /// 占位符的处理 /// /// /// /// /// private static void ReplaceProcessing(string word, int i, List replacements, ref string deeplink, ref string prompt_text) { if (deeplink.Contains("{url:")) { string encodedUrl = HttpUtility.UrlEncode(word); deeplink = deeplink.Replace($"{{url:{i - 1}}}", encodedUrl); } if (prompt_text.Contains("{url:")) { string encodedUrl = HttpUtility.UrlEncode(word); prompt_text = prompt_text.Replace($"{{url:{i - 1}}}", encodedUrl); } if (deeplink.Contains("{decode:")) { string encodedUrl = HttpUtility.UrlDecode(word); deeplink = deeplink.Replace($"{{decode:{i - 1}}}", encodedUrl); } if (prompt_text.Contains("{decode:")) { string encodedUrl = HttpUtility.UrlDecode(word); prompt_text = prompt_text.Replace($"{{decode:{i - 1}}}", encodedUrl); } //两次url编码 if (deeplink.Contains("{url2:")) { string encodedUrl = HttpUtility.UrlEncode(word); encodedUrl = HttpUtility.UrlEncode(encodedUrl); deeplink = deeplink.Replace($"{{url2:{i - 1}}}", encodedUrl); } if (prompt_text.Contains("{url2:")) { string encodedUrl = HttpUtility.UrlEncode(word); encodedUrl = HttpUtility.UrlEncode(encodedUrl); prompt_text = prompt_text.Replace($"{{url2:{i - 1}}}", encodedUrl); } //三次url解码 if (deeplink.Contains("{url3:")) { string encodedUrl = HttpUtility.UrlEncode(word); encodedUrl = HttpUtility.UrlEncode(encodedUrl); deeplink = deeplink.Replace($"{{url3:{i - 1}}}", encodedUrl); } if (prompt_text.Contains("{url3:")) { string encodedUrl = HttpUtility.UrlEncode(word); encodedUrl = HttpUtility.UrlEncode(encodedUrl); prompt_text = prompt_text.Replace($"{{url3:{i - 1}}}", encodedUrl); } //atob if (deeplink.Contains("{atob:")) { string encodedUrl = HttpUtility.UrlDecode(word); deeplink = deeplink.Replace($"{{atob:{i - 1}}}", encodedUrl.FromBase64()); } if (prompt_text.Contains("{atob:")) { string encodedUrl = HttpUtility.UrlDecode(word); prompt_text = prompt_text.Replace($"{{atob:{i - 1}}}", encodedUrl.FromBase64()); } //btoa if (deeplink.Contains("{btoa:")) { string encodedUrl = HttpUtility.UrlEncode(word); deeplink = deeplink.Replace($"{{btoa:{i - 1}}}", word.ToBase64()); } if (prompt_text.Contains("{btoa:")) { string encodedUrl = HttpUtility.UrlEncode(word); prompt_text = prompt_text.Replace($"{{btoa:{i - 1}}}", word.ToBase64()); } deeplink = deeplink.Replace($"{{{i - 1}}}", word); prompt_text = prompt_text.Replace($"{{{i - 1}}}", word); // 新增 <<>> 格式处理 if (replacements != null) { deeplink = Regex.Replace(deeplink, @"<<(\w+):([^>]+)>>", match => { var ruleName = match.Groups[1].Value; var value = match.Groups[2].Value; // 查找并应用替换规则 var rule = replacements.FirstOrDefault(r => r.Name == ruleName); if (rule != null) { foreach (var r in rule.Rules) { if (r.When.Evaluate(value)) { return r.Then; } } return rule.Default; } return value; }); prompt_text = Regex.Replace(prompt_text, @"<<(\w+):([^>]+)>>", match => { var ruleName = match.Groups[1].Value; var value = match.Groups[2].Value; // 查找并应用替换规则 var rule = replacements.FirstOrDefault(r => r.Name == ruleName); if (rule != null) { foreach (var r in rule.Rules) { if (r.When.Evaluate(value)) { return r.Then; } } return rule.Default; } return value; }); } // 最后处理普通的索引替换 deeplink = deeplink.Replace($"{{{i - 1}}}", word); prompt_text = prompt_text.Replace($"{{{i - 1}}}", word); } private static async Task> ResourceProcessing(Match baseMatch, DeeplinkResources resource, List replacements) { Dictionary result = new Dictionary(); string url = resource.url_pattern; string prompt_text = string.Empty; for (var i = 1; i < baseMatch.Groups.Count; i++) { string val = baseMatch.Groups[i].Value; ReplaceProcessing(val, i, replacements, ref url, ref prompt_text); } string content = string.Empty; try { switch (resource.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: case UrlAction.JsonBody: { 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 (string.IsNullOrEmpty(content)) { return result; } try { JsonElement root = default; if (UrlAction.JsonBody.Equals(resource.url_action)) { root = content.Convert2JsonElement(); } for (int i = 0; i < resource.pattern.Count; i++) { string val = string.Empty; if (UrlAction.JsonBody.Equals(resource.url_action)) { val = root.PathRead(resource.pattern[i], string.Empty); } else { var regex = new Regex(resource.pattern[i]); var match = regex.Match(content); if (match.Success) { val = match.Groups[1].Value; } } string key = $"{{{i}@{resource.name}}}"; result.Add(key, val); } } catch (Exception ex) { } return result; } 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); switch (oaid) { case "3B191CFA4C6B48F9BA459E915B57743BEC7D424979CC9C51BCAD0C245B1C7BA2": case "6D5D56F3073844F2912B4552CEDF9E3BEC7D424979CC9C51C5461106F1915660": case "4CA55979100C4B5BAAA2AF4ABE15C360602e915cedae5786405419ccc9f4e3f8": break; default: result.deeplink_url = result.deeplink_url.Replace("snssdk1128://", "snssdk561124://"); break; } 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 = "转链接口异常"; } } _ = TkLogCore.ParseLogAsync(result); return result.deeplink_url; } } }