promo_goods.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574
  1. using dodohold.core;
  2. using StackExchange.Redis;
  3. using System.Diagnostics;
  4. using System.Text.Json;
  5. namespace molilian.core
  6. {
  7. public partial class AlimamaPlus
  8. {
  9. public async Task<TkDataDTO> endpoint_promo_goods(string content, TkDataDTO result, CancellationToken cancellationToken = default)
  10. {
  11. string t = $"{DateTime.Now.Convert2UnixTimestamp(true)}";
  12. Random random = new();
  13. double randomNumber = random.NextDouble();
  14. string randomString = randomNumber.ToString()[2..];
  15. string cna = _cookies.GetContentPart("cna=", ";");
  16. string firstFiveCharacters = cna[..Math.Min(5, cna.Length)];
  17. //var variableMap = new
  18. //{
  19. // url = content,
  20. // union_lens = $"b_pvid:a219t._portal_v2_tool_links_page_home_index_htm_{t}_{randomString}_{firstFiveCharacters}",
  21. // lensScene = "PUB",
  22. // spmB = "_portal_v2_tool_links_page_home_index_htm"
  23. //}.Convert2Json(true).UrlEncode();
  24. string item_id = content.GetContentPart("item.htm?id=", "");
  25. var variableMap = new
  26. {
  27. itemId = item_id
  28. }.Convert2Json(true).UrlEncode();
  29. variableMap = variableMap.Replace(" ", "%20");
  30. _floorId = "102359";
  31. string url = "https://pub.alimama.com/openapi/param2/1/gateway.unionpub/xt.entry.json?" +
  32. $"t={t}&_tb_token_={_tb_token}&floorId={_floorId}&refpid={_refpid}&variableMap={variableMap}";
  33. Stopwatch stopwatch = Stopwatch.StartNew();
  34. stopwatch.Start();
  35. var client = new WebClientUtility().SetContentType("application/json;charset=utf-8")
  36. .AddHeaders("X-Requested-With", "XMLHttpRequest")
  37. .AddHeaders("Cookie", _cookies);
  38. client.Proxy = _proxy;
  39. #if DEBUG
  40. client.Proxy = null;
  41. #endif
  42. if (!string.IsNullOrEmpty(_user_agent)) client.UserAgent = _user_agent;
  43. client.Timeout = GetRequestTimeout2(result.elapsedTime2);
  44. //var response = client.Request(url);
  45. #if DEBUG
  46. var response = await client.RequestAsync(url, "GET");
  47. #else
  48. var response = await client.RequestAsync(url, "GET", cancellationToken);
  49. #endif
  50. stopwatch.Stop();
  51. result.elapsedTime3 = (int)stopwatch.ElapsedMilliseconds;
  52. var body = string.Empty;
  53. try
  54. {
  55. if (!response.Successed)
  56. {
  57. result.channel = TkChannelEnum.tb;
  58. result.link_type = LinkTypeEnum.unknown;
  59. result.success = false;
  60. result.message = "fail";
  61. if (response.ResponseException != null)
  62. {
  63. _ = new LoggerLibrary("api_error", "fail")
  64. .Info(response.ResponseException.Message, response.ResponseException.StackTrace)
  65. .SaveAsync();
  66. throw response.ResponseException;
  67. }
  68. return result;
  69. }
  70. if (response.ResponseMessage.StatusCode == System.Net.HttpStatusCode.Found)
  71. {
  72. _ = new LoggerLibrary("api_error", "fail")
  73. .Info($"302:{response.ResponseMessage.Headers.Location}")
  74. .SaveAsync();
  75. throw new Exception("302 Found, was canceled");
  76. }
  77. JsonElement root;
  78. try
  79. {
  80. body = response.Body();
  81. //{"success":false,"message":"result empty"}
  82. if (body.Contains("bixi.alicdn.com"))
  83. {
  84. await TkEndpointManager.SuspendAsync(_accountId, "promo_goods", TkEndpointManager.SuspendReason.Passive);
  85. result.channel = TkChannelEnum.tb;
  86. result.link_type = LinkTypeEnum.unknown;
  87. result.subCode = TkSubCodeEnum.ParseDeny;
  88. result.success = false;
  89. result.message = "waf_block";
  90. result.reason = "接口风控";
  91. return result;
  92. }
  93. root = body.Convert2JsonElement();
  94. }
  95. catch (Exception ex)
  96. {
  97. _ = new LoggerLibrary("api_error", "fail")
  98. .Info(ex.Message, ex.StackTrace)
  99. .SaveAsync();
  100. if (body.Contains("https://g.alicdn.com/sd/punish/waf_block.html"))
  101. {
  102. await TkEndpointManager.SuspendAsync(_accountId, "promo_goods", TkEndpointManager.SuspendReason.Passive);
  103. result.channel = TkChannelEnum.tb;
  104. result.link_type = LinkTypeEnum.unknown;
  105. result.success = false;
  106. result.message = "waf_block";
  107. result.reason = "接口风控";
  108. return result;
  109. }
  110. throw new Exception(body);
  111. }
  112. bool success = root.Read<bool>("success", false);
  113. string message = root.Read("message", string.Empty);
  114. string info_message = root.PathRead("info.message", string.Empty);
  115. if (!string.IsNullOrEmpty(info_message)) message = info_message;
  116. if ("result empty".Equals(message))
  117. {
  118. result.subCode = TkSubCodeEnum.NoConvert;
  119. }
  120. JsonElement item = default;
  121. if (success)
  122. {
  123. var data = root.ElementRead("data");
  124. var resultList = data.ElementRead("resultList");
  125. if (resultList.ValueKind != JsonValueKind.Array)
  126. {
  127. _ = new LoggerLibrary("api_error", "fail")
  128. .Info("resultList Undefined", body)
  129. .SaveAsync();
  130. throw new Exception("resultList Undefined");
  131. }
  132. item = resultList[0];
  133. }
  134. string taoToken = item.Read("taoToken", string.Empty);
  135. string shortLinkurl = item.Read("shortLinkurl", string.Empty);
  136. string couponLinkTaoToken = item.Read("couponLinkTaoToken", string.Empty);
  137. string couponShortLinkUrl = item.Read("couponShortLinkUrl", string.Empty);
  138. decimal couponAmount = item.Read<decimal>("couponAmount", 0);
  139. string couponEffectiveEndTime = item.Read("couponEffectiveEndTime", string.Empty);
  140. string couponEffectiveStartTime = item.Read("couponEffectiveStartTime", string.Empty);
  141. string itemId = item.Read("itemId", string.Empty);
  142. string mktId = item.Read("outputMktId", string.Empty);
  143. string itemName = item.Read("itemName", string.Empty);
  144. decimal promotionPrice = item.Read<decimal>("promotionPrice", 0);
  145. string sellerNickName = item.Read("sellerNickName", string.Empty);
  146. string shopTitle = item.Read("shopTitle", string.Empty);
  147. string pic = item.Read("data.pic", string.Empty);
  148. if (pic.StartsWith("//")) pic = "https:" + pic;
  149. string itemTaoToken = taoToken;
  150. string qrCodeUrl = item.Read("qrCodeUrl", string.Empty);
  151. if (response.ResponseMessage != null)
  152. {
  153. switch (response.ResponseMessage.StatusCode)
  154. {
  155. case System.Net.HttpStatusCode.OK:
  156. {
  157. if (couponAmount > 0 && !string.IsNullOrEmpty(couponLinkTaoToken) && !string.IsNullOrEmpty(couponShortLinkUrl))
  158. {
  159. taoToken = couponLinkTaoToken;
  160. shortLinkurl = couponShortLinkUrl;
  161. }
  162. string shortLink = GetLink(taoToken);
  163. content = ReplaceUrls(content, shortLink);
  164. }
  165. break;
  166. case System.Net.HttpStatusCode.Found:
  167. string location = response.ResponseMessage.Headers.Location.OriginalString;
  168. if (!string.IsNullOrEmpty(location) && location.Contains("www.alimama.com/member/login.htm"))
  169. {
  170. success = false;
  171. message = "nologin";
  172. }
  173. break;
  174. default:
  175. {
  176. success = false;
  177. message = "other";
  178. }
  179. break;
  180. }
  181. }
  182. if (!success)
  183. {
  184. shortLinkurl = GetLink(content);
  185. switch (message)
  186. {
  187. case "该商品已经下架或未加入淘宝客":
  188. case "该链接不支持转化,请更换链接尝试":
  189. case "result empty":
  190. result.subCode = TkSubCodeEnum.NoConvert;
  191. break;
  192. case "网络错误":
  193. result.subCode = TkSubCodeEnum.NetError;
  194. break;
  195. default:
  196. var _headers = response.ResponseMessage.Headers;
  197. var headers = "";
  198. foreach (var h in _headers)
  199. {
  200. foreach (var Value in h.Value)
  201. {
  202. headers += $"{h.Key}: {Value}\n";
  203. }
  204. }
  205. _ = new LoggerLibrary("api_error", "fail")
  206. .Info(message, content)
  207. .Info(headers, body)
  208. .SaveAsync();
  209. break;
  210. }
  211. }
  212. shortLinkurl = GetLink(taoToken);
  213. string deeplink_url = GetDeeplink(shortLinkurl);
  214. //补充原商品链接 2025-06-01
  215. result.item_url = GetLink(itemTaoToken);
  216. result.item_deeplink_url = GetDeeplink(result.item_url);
  217. result.channel = TkChannelEnum.tb;
  218. if (success)
  219. {
  220. result.subCode = TkSubCodeEnum.Success;
  221. switch (result.link_type)
  222. {
  223. case LinkTypeEnum.profile:
  224. case LinkTypeEnum.goods:
  225. case LinkTypeEnum.video:
  226. case LinkTypeEnum.live:
  227. break;
  228. default:
  229. result.link_type = LinkTypeEnum.goods;
  230. break;
  231. }
  232. }
  233. result.success = success;
  234. result.message = message;
  235. result.reason = string.Empty;
  236. result.content = content;
  237. result.couponAmount = couponAmount;
  238. result.couponEffectiveEndTime = couponEffectiveEndTime;
  239. result.couponEffectiveStartTime = couponEffectiveStartTime;
  240. result.taoToken = taoToken;
  241. if (!string.IsNullOrEmpty(shortLinkurl))
  242. {
  243. result.shortLinkurl = shortLinkurl;
  244. result.deeplink_url = deeplink_url;
  245. }
  246. result.mktId = mktId;
  247. result.itemId = itemId;
  248. result.itemName = itemName;
  249. result.promotionPrice = promotionPrice;
  250. result.sellerNickName = sellerNickName;
  251. result.shopTitle = shopTitle;
  252. result.pic = pic;
  253. result.qrCodeUrl = qrCodeUrl;
  254. }
  255. catch (Exception ex)
  256. {
  257. if (response.ResponseMessage?.StatusCode == System.Net.HttpStatusCode.OK)
  258. {
  259. if (body.Contains("<p>抱歉!页面无法访问……</p>"))
  260. {
  261. throw new APIException("抱歉!页面无法访问……");
  262. }
  263. }
  264. throw;
  265. }
  266. return result;
  267. }
  268. public async Task<TkDataDTO> alimamaParseAsync(string content, TkDataDTO result, CancellationToken cancellationToken = default)
  269. {
  270. string t = $"{DateTime.Now.Convert2UnixTimestamp(true)}";
  271. Random random = new();
  272. double randomNumber = random.NextDouble();
  273. string randomString = randomNumber.ToString()[2..];
  274. string cna = _cookies.GetContentPart("cna=", ";");
  275. string firstFiveCharacters = cna[..Math.Min(5, cna.Length)];
  276. var variableMap = new
  277. {
  278. url = content,
  279. union_lens = $"b_pvid:a219t._portal_v2_tool_links_page_home_index_htm_{t}_{randomString}_{firstFiveCharacters}",
  280. lensScene = "PUB",
  281. spmB = "_portal_v2_tool_links_page_home_index_htm"
  282. }.Convert2Json(true).UrlEncode();
  283. variableMap = variableMap.Replace(" ", "%20");
  284. string url = "https://pub.alimama.com/openapi/param2/1/gateway.unionpub/xt.entry.json?" +
  285. $"t={t}&_tb_token_={_tb_token}&floorId={_floorId}&refpid={_refpid}&variableMap={variableMap}";
  286. Stopwatch stopwatch = Stopwatch.StartNew();
  287. stopwatch.Start();
  288. var client = new WebClientUtility().SetContentType("application/json;charset=utf-8")
  289. .AddHeaders("X-Requested-With", "XMLHttpRequest")
  290. .AddHeaders("Cookie", _cookies);
  291. client.Proxy = _proxy;
  292. #if DEBUG
  293. client.Proxy = null;
  294. #endif
  295. if (!string.IsNullOrEmpty(_user_agent)) client.UserAgent = _user_agent;
  296. client.Timeout = GetRequestTimeout2(result.elapsedTime2);
  297. //var response = client.Request(url);
  298. #if DEBUG
  299. var response = await client.RequestAsync(url, "GET");
  300. #else
  301. var response = await client.RequestAsync(url, "GET", cancellationToken);
  302. #endif
  303. stopwatch.Stop();
  304. result.elapsedTime3 = (int)stopwatch.ElapsedMilliseconds;
  305. var body = string.Empty;
  306. try
  307. {
  308. if (!response.Successed)
  309. {
  310. result.channel = TkChannelEnum.tb;
  311. result.link_type = LinkTypeEnum.unknown;
  312. result.success = false;
  313. result.message = "fail";
  314. if (response.ResponseException != null)
  315. {
  316. _ = new LoggerLibrary("api_error", "fail")
  317. .Info(response.ResponseException.Message, response.ResponseException.StackTrace)
  318. .SaveAsync();
  319. throw response.ResponseException;
  320. }
  321. return result;
  322. }
  323. if (response.ResponseMessage.StatusCode == System.Net.HttpStatusCode.Found)
  324. {
  325. _ = new LoggerLibrary("api_error", "fail")
  326. .Info($"302:{response.ResponseMessage.Headers.Location}")
  327. .SaveAsync();
  328. throw new Exception("302 Found, was canceled");
  329. }
  330. JsonElement root;
  331. try
  332. {
  333. body = response.Body();
  334. root = body.Convert2JsonElement();
  335. }
  336. catch (Exception ex)
  337. {
  338. _ = new LoggerLibrary("api_error", "fail")
  339. .Info(ex.Message, ex.StackTrace)
  340. .SaveAsync();
  341. if (body.Contains("https://g.alicdn.com/sd/punish/waf_block.html"))
  342. {
  343. result.channel = TkChannelEnum.tb;
  344. result.link_type = LinkTypeEnum.unknown;
  345. result.success = false;
  346. result.message = "waf_block";
  347. return result;
  348. }
  349. if (body.Contains("https://bixi.alicdn.com") && body.Contains("\"action\": \"deny\""))
  350. {
  351. result.channel = TkChannelEnum.tb;
  352. result.link_type = LinkTypeEnum.unknown;
  353. result.subCode = TkSubCodeEnum.ParseDeny;
  354. result.success = false;
  355. result.message = "waf_block";
  356. result.reason = "接口风控";
  357. return result;
  358. }
  359. throw new Exception(body);
  360. }
  361. bool success = root.Read<bool>("success", false);
  362. string message = root.Read("message", string.Empty);
  363. string info_message = root.PathRead("info.message", string.Empty);
  364. if (!string.IsNullOrEmpty(info_message)) message = info_message;
  365. string taoToken = root.PathRead("data.taoToken", string.Empty);
  366. string shortLinkurl = root.PathRead("data.shortLinkurl", string.Empty);
  367. string couponLinkTaoToken = root.PathRead("data.couponLinkTaoToken", string.Empty);
  368. string couponShortLinkUrl = root.PathRead("data.couponShortLinkUrl", string.Empty);
  369. decimal couponAmount = root.PathRead<decimal>("data.couponAmount", 0);
  370. string couponEffectiveEndTime = root.PathRead("data.couponEffectiveEndTime", string.Empty);
  371. string couponEffectiveStartTime = root.PathRead("data.couponEffectiveStartTime", string.Empty);
  372. string itemId = root.PathRead("data.itemId", string.Empty);
  373. string mktId = root.PathRead("data.outputMktId", string.Empty);
  374. string itemName = root.PathRead("data.itemName", string.Empty);
  375. decimal promotionPrice = root.PathRead<decimal>("data.promotionPrice", 0);
  376. string sellerNickName = root.PathRead("data.sellerNickName", string.Empty);
  377. string shopTitle = root.PathRead("data.shopTitle", string.Empty);
  378. string pic = root.PathRead("data.pic", string.Empty);
  379. if (pic.StartsWith("//")) pic = "https:" + pic;
  380. string qrCodeUrl = root.PathRead("data.qrCodeUrl", string.Empty);
  381. if (response.ResponseMessage != null)
  382. {
  383. switch (response.ResponseMessage.StatusCode)
  384. {
  385. case System.Net.HttpStatusCode.OK:
  386. {
  387. if (couponAmount > 0 && !string.IsNullOrEmpty(couponLinkTaoToken) && !string.IsNullOrEmpty(couponShortLinkUrl))
  388. {
  389. taoToken = couponLinkTaoToken;
  390. shortLinkurl = couponShortLinkUrl;
  391. }
  392. string shortLink = GetLink(taoToken);
  393. content = ReplaceUrls(content, shortLink);
  394. }
  395. break;
  396. case System.Net.HttpStatusCode.Found:
  397. string location = response.ResponseMessage.Headers.Location.OriginalString;
  398. if (!string.IsNullOrEmpty(location) && location.Contains("www.alimama.com/member/login.htm"))
  399. {
  400. success = false;
  401. message = "nologin";
  402. }
  403. break;
  404. default:
  405. {
  406. success = false;
  407. message = "other";
  408. }
  409. break;
  410. }
  411. }
  412. if (!success)
  413. {
  414. shortLinkurl = GetLink(content);
  415. switch (message)
  416. {
  417. case "该商品已经下架或未加入淘宝客":
  418. case "该链接不支持转化,请更换链接尝试":
  419. result.subCode = TkSubCodeEnum.NoConvert;
  420. break;
  421. case "网络错误":
  422. result.subCode = TkSubCodeEnum.NetError;
  423. break;
  424. default:
  425. var _headers = response.ResponseMessage.Headers;
  426. var headers = "";
  427. foreach (var h in _headers)
  428. {
  429. foreach (var Value in h.Value)
  430. {
  431. headers += $"{h.Key}: {Value}\n";
  432. }
  433. }
  434. _ = new LoggerLibrary("api_error", "fail")
  435. .Info(message, content)
  436. .Info(headers, body)
  437. .SaveAsync();
  438. break;
  439. }
  440. }
  441. shortLinkurl = GetLink(taoToken);
  442. string deeplink_url = GetDeeplink(shortLinkurl);
  443. result.channel = TkChannelEnum.tb;
  444. if (success)
  445. {
  446. result.subCode = TkSubCodeEnum.Success;
  447. switch (result.link_type)
  448. {
  449. case LinkTypeEnum.profile:
  450. case LinkTypeEnum.goods:
  451. case LinkTypeEnum.video:
  452. case LinkTypeEnum.live:
  453. break;
  454. default:
  455. result.link_type = LinkTypeEnum.goods;
  456. break;
  457. }
  458. }
  459. result.success = success;
  460. result.message = message;
  461. result.reason = string.Empty;
  462. result.content = content;
  463. result.couponAmount = couponAmount;
  464. result.couponEffectiveEndTime = couponEffectiveEndTime;
  465. result.couponEffectiveStartTime = couponEffectiveStartTime;
  466. result.taoToken = taoToken;
  467. if (!string.IsNullOrEmpty(shortLinkurl))
  468. {
  469. result.shortLinkurl = shortLinkurl;
  470. result.deeplink_url = deeplink_url;
  471. }
  472. result.mktId = mktId;
  473. result.itemId = itemId;
  474. result.itemName = itemName;
  475. result.promotionPrice = promotionPrice;
  476. result.sellerNickName = sellerNickName;
  477. result.shopTitle = shopTitle;
  478. result.pic = pic;
  479. result.qrCodeUrl = qrCodeUrl;
  480. }
  481. catch (Exception ex)
  482. {
  483. if (response.ResponseMessage?.StatusCode == System.Net.HttpStatusCode.OK)
  484. {
  485. if (body.Contains("<p>抱歉!页面无法访问……</p>"))
  486. {
  487. throw new APIException("抱歉!页面无法访问……");
  488. }
  489. }
  490. throw;
  491. }
  492. return result;
  493. }
  494. }
  495. }