using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Filters; using System; using System.Collections.Generic; using System.Linq; using System.Text; using dodohold.core; using Dataoke; using System.Text.RegularExpressions; using Org.BouncyCastle.Ocsp; using System.Diagnostics; using TencentCloud.Fmu.V20191213.Models; using Microsoft.VisualBasic; using System.Text.Json; using static System.Runtime.InteropServices.JavaScript.JSType; using System.Security.Cryptography; using Google.Protobuf.WellKnownTypes; using Microsoft.Extensions.FileSystemGlobbing.Internal; using System.Web; namespace molilian.core { public partial class JdUnionPlus { private const string VIDEO_URL_PREFIX = "https://h5.m.jd.com/active/faxian/video/index.html"; public static string GetLink(string content) { if (string.IsNullOrEmpty(content)) return content; string result = content; //string pattern = @"https?:\/\/?([\da-z\.-]+)\.([a-z]{2,6})(:\d{1,5})?([\/\w\.-]*)*\/?(#[\S]+)?(\?[\S]+)?"; //openjd://virtual?params={%22category%22:%22jump%22,%20%22des%22:%22m%22,%20%22url%22:%22https://item.m.jd.com/product/10197819484796.html%22} //openapp.jdmobile://virtual?params={%22category%22:%22jump%22,%20%22des%22:%22m%22,%20%22url%22:%22https://item.m.jd.com/product/10197819484796.html%22} // 处理 openjd:// 和 openapp.jdmobile:// 格式的 deeplink if (content.StartsWith("openjd://virtual?params=") || content.StartsWith("openapp.jdmobile://virtual?params=")) { try { string paramsStr = content.GetContentPart("params={", "}"); if (!string.IsNullOrEmpty(paramsStr)) { paramsStr = $"{{{paramsStr}}}"; string decodedParams = HttpUtility.UrlDecode(paramsStr); // 使用正则提取 "url" 字段的值 var urlMatch = Regex.Match(decodedParams, @"""url""\s*:\s*""([^""]+)"""); if (urlMatch.Success) { return urlMatch.Groups[1].Value; } } } catch { // 如果解析失败,继续使用默认逻辑 } } Regex regex = new(AlimamaPlus._url_pattern); MatchCollection matches = regex.Matches(content); foreach (Match m in matches.Cast()) { string url = m.Value; return url; } return string.Empty; } public static string GetItemId(string url) { if (string.IsNullOrEmpty(url)) return string.Empty; string[] patterns = [ @"https?://(?:i-)?item(?:\.m)?\.jd\.com/(?:product/)?(\d+)\.html(?:\?.*)?" //@"https?:\/\/b2b\.m\.jd\.com\/goods-detail\/.*[?&]skuId=(\d+)" ]; foreach (var pattern in patterns) { Match match = Regex.Match(url, pattern); if (match.Success) { string idNumber = match.Groups[1].Value; // 捕获组1包含数字ID return idNumber; } } return string.Empty; } public static string GetDeeplink(string url, string clickId) { var config = TkConfigCore.Get(); if (string.IsNullOrEmpty(config.dp_style)) return GetDefaultStyleDeeplink(url); Random rand = new(); int randomValue = rand.Next(0, 101); if (randomValue > config.dp_style_percentage) return GetDefaultStyleDeeplink(url); if (string.IsNullOrEmpty(clickId)) clickId = config.dp_style_default_str; var ts = DateTime.Now.Convert2UnixTimestamp(true); string virtual_params; if (!string.IsNullOrEmpty(url) && url.StartsWith(VIDEO_URL_PREFIX)) { // 处理视频URL的情况 var queryString = url.Substring(VIDEO_URL_PREFIX.Length + 1); // +1 for the '?' character var queryParams = HttpUtility.ParseQueryString(queryString); var paramsDict = queryParams.AllKeys.ToDictionary(k => k, k => queryParams[k]); paramsDict["category"] = "jump"; paramsDict["m_param"] = $"{{\"jdv\":\"0|oppo_seedling|-|widget|{clickId}|{ts}\"}}"; virtual_params = JsonSerializer.Serialize(paramsDict); } else if (string.IsNullOrEmpty(url)) { virtual_params = $"{{\"category\":\"jump\",\"des\":\"HomePage\",\"m_param\":{{\"jdv\":\"0|oppo_seedling|-|widget|{clickId}|{ts}\"}}"; } else { virtual_params = $"{{\"category\":\"jump\",\"des\":\"m\",\"url\":\"{url}\",\"m_param\":{{\"jdv\":\"0|oppo_seedling|-|widget|{clickId}|{ts}\"}}}}"; } string result = $"openapp.jdmobile://virtual?params={virtual_params.UrlEncode()}"; return result; } public static string GetDefaultStyleDeeplink(string url) { if (string.IsNullOrEmpty(url)) return "openapp.jdmobile://"; string virtual_params; if (url.StartsWith(VIDEO_URL_PREFIX)) { // 处理视频URL的情况 var queryString = url.Substring(VIDEO_URL_PREFIX.Length + 1); var queryParams = HttpUtility.ParseQueryString(queryString); var paramsDict = queryParams.AllKeys.ToDictionary(k => k, k => queryParams[k]); paramsDict["category"] = "jump"; virtual_params = JsonSerializer.Serialize(paramsDict); } else { virtual_params = $"{{\"category\":\"jump\",\"des\":\"m\",\"sourceValue\":\"babel-act\",\"sourceType\":\"babel\",\"url\":\"{url}\",\"M_sourceFrom\":\"h5auto\",\"msf_type\":\"auto\"}}"; } string result = $"openapp.jdmobile://virtual?params={virtual_params.UrlEncode()}"; return result; } //public static string GetDefaultStyleDeeplink(string url) //{ // if (string.IsNullOrEmpty(url)) return "openapp.jdmobile://"; // string virtual_params = $"{{\"category\":\"jump\",\"des\":\"m\",\"sourceValue\":\"babel-act\",\"sourceType\":\"babel\",\"url\":\"{url}\",\"M_sourceFrom\":\"h5auto\",\"msf_type\":\"auto\"}}"; // string result = $"openapp.jdmobile://virtual?params={virtual_params.UrlEncode()}"; // return result; //} public static JdDataDTO GetFormattedObject(UnionParseRequest request) { string shortLinkurl = GetLink(request.Content); string deeplink_url = GetDefaultStyleDeeplink(shortLinkurl); return new JdDataDTO() { message = string.Empty, link_type = LinkTypeEnum.unknown, channel = TkChannelEnum.jd, accountName = string.Empty, success = false, content = request.Content, ip = request.Ip, oaid = request.Oaid, elapsedTime = 0, itemName = "点击打开京东APP", shortLinkurl = shortLinkurl, deeplink_url = deeplink_url, create_time = DateTime.Now, end_point = _end_point, parse_type = request.Type, }; } public static bool IsAffLlink(string url) { Uri uri = new(url); if (uri.Host.Equals("`u.jd.com", StringComparison.OrdinalIgnoreCase)) { return true; } return false; } public static string? GetDesiredUrl(string url) { try { WebClientUtility client = new() { AllowAutoRedirect = false, Referer = "http://www.jd.com", Proxy = ProxyNodesCore.RandomOne() }; #if DEBUG client.Proxy = null; #endif var response = client.Request(url); if (!response.Successed) return null; var target_url = response.ResponseMessage?.Headers?.Location?.ToString(); if (!url.StartsWith("https://3.cn")) return null; return target_url; } catch (Exception ex) { } return null; } public static LinkTypeEnum GetLinkType(string url, out string desiredUrl, out string itemId) { desiredUrl = url; itemId = string.Empty; if (string.IsNullOrEmpty(url)) return LinkTypeEnum.unknown; string lowerUrl = url.ToLower(); Uri uri = new(url); if ("3.cn".Equals(uri.Host)) { var target_url = GetDesiredUrl(url); return GetLinkType(target_url, out desiredUrl, out itemId); } string[] arr = ["mall.jd.com", "shop.m.jd.com", "shop.jd.com"]; if (arr.Contains(uri.Host)) return LinkTypeEnum.profile; arr = ["lives.jd.com"]; if (arr.Contains(uri.Host)) return LinkTypeEnum.live; arr = ["u.jd.com"]; if (arr.Contains(uri.Host)) return LinkTypeEnum.other_aff; arr = ["h5.m.jd.com"]; if (arr.Contains(uri.Host)) return LinkTypeEnum.video; if (lowerUrl.Contains("h5.m.jd.com/active/faxian/video/index.html")) return LinkTypeEnum.video; if (lowerUrl.Contains("eco.m.jd.com/content/dr_home/index.html")) return LinkTypeEnum.profile; if (uri.Host.Contains("jd.com")) { itemId = GetItemId(url); if (!string.IsNullOrEmpty(itemId)) return LinkTypeEnum.goods; return LinkTypeEnum.baseDomain; } return LinkTypeEnum.unknown; } static bool ContainsSpecialFormat(string content) { // 使用正则表达式匹配内容中是否含有特定格式的字符串 string pattern = @"[!$¥%]+[a-zA-Z0-9]+[!$¥%]]*"; return Regex.IsMatch(content, pattern); } public async Task JdParseAsync(string content, int commerceType, string clickId, JdDataDTO result, CancellationToken cancellationToken = default) { string message = "OK"; string url = GetLink(content); if (string.IsNullOrEmpty(url)) { result.success = false; result.link_type = LinkTypeEnum.unknown; result.channel_type = ChannelTypeEnum.jd; result.message = "放弃转链"; result.reason = "纯口令"; result.accountId = 0; result.accountName = string.Empty; result.content = content; result.shortLinkurl = url; result.deeplink_url = GetDefaultStyleDeeplink(url); return result; } string shortLinkurl = url; //result.link_type = plus.IsAffLlink(url) ? LinkTypeEnum.other_aff : LinkTypeEnum.goods; result.shortLinkurl = url; result.link_type = GetLinkType(url, out string out_url, out string itemId); url = out_url; result.itemId = itemId; switch (result.link_type) { case LinkTypeEnum.profile: case LinkTypeEnum.live: case LinkTypeEnum.video: result.success = false; result.channel_type = ChannelTypeEnum.jd; result.message = "放弃转链"; result.reason = result.link_type switch { LinkTypeEnum.profile => "店铺", LinkTypeEnum.live => "直播", LinkTypeEnum.video => "视频", _ => "非标链接", }; result.accountId = 0; result.accountName = string.Empty; result.content = content; result.shortLinkurl = url; result.deeplink_url = GetDefaultStyleDeeplink(url); return result; case LinkTypeEnum.other_aff: result.success = false; result.link_type = LinkTypeEnum.unknown; result.channel_type = ChannelTypeEnum.jd; result.message = "放弃转链"; result.reason = "其他推广链接"; result.accountId = 0; result.accountName = string.Empty; result.content = content; result.shortLinkurl = url; result.deeplink_url = GetDefaultStyleDeeplink(url); return result; default: if (string.IsNullOrEmpty(url) || string.IsNullOrEmpty(result.itemId) || (!url.StartsWith("https://item.jd.com/") && !url.StartsWith("http://item.jd.com/") && !url.StartsWith("https://item.m.jd.com/product/") && !url.StartsWith("http://item.m.jd.com/product/") && !url.StartsWith("http://item.m.jd.com/product/"))) { result.success = false; //result.link_type = LinkTypeEnum.unknown; result.channel_type = ChannelTypeEnum.jd; result.message = "放弃转链"; result.reason = "非标链接"; result.accountId = 0; result.accountName = string.Empty; result.content = content; result.shortLinkurl = shortLinkurl; result.deeplink_url = result.link_type switch { LinkTypeEnum.other_aff or LinkTypeEnum.baseDomain or LinkTypeEnum.goods => GetDefaultStyleDeeplink(result.shortLinkurl), _ => GetDefaultStyleDeeplink(result.shortLinkurl), }; return result; } break; } if (!TestParseCore.InWhitelist(result.ip, result.oaid) && FlowControlIgnoreRequest(_config, result.riskStrategy, result.launchScene, out string reason)) { result.success = false; result.link_type = LinkTypeEnum.unknown; result.channel_type = ChannelTypeEnum.jd; result.message = "放弃转链"; result.reason = reason; result.accountId = 0; result.accountName = string.Empty; result.content = content; result.shortLinkurl = url; result.deeplink_url = result.link_type switch { LinkTypeEnum.other_aff or LinkTypeEnum.goods => GetDefaultStyleDeeplink(result.shortLinkurl), _ => GetDefaultStyleDeeplink(result.shortLinkurl), }; return result; } switch (commerceType) { case 1: case 2: result.success = false; result.message = "放弃转链"; result.reason = $"厂商放弃{commerceType}"; return result; } switch (_account.work_mode) { case JdUnionWorkMode.SiteApi: result = await GetPromotionBySiteAsync(result, url, clickId, cancellationToken); break; case JdUnionWorkMode.AffApi: result = await GetPromotionByAffAsync(result, url, clickId, cancellationToken); break; case JdUnionWorkMode.Ddx: result = await GetPromotionByDdxAsync(result, url, clickId, cancellationToken); break; case JdUnionWorkMode.JingFen: result = await GetPromotionByCrawlerJingfenAsync(result, url, clickId, cancellationToken); break; case JdUnionWorkMode.Crawler: default: result = await GetPromotionByCrawlerAsync(result, url, clickId, cancellationToken); break; } return result; } } }