using Api.Framework.Cps; using Api.Framework.Utils; using CsharpHttpHelper; using CsharpHttpHelper.Enum; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Linq; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using System.Web; namespace Api.Framework.Tools { /// /// 抖音解析助手 /// public class DYHelper { /// /// 获取抖音链接中的商品ID(有问题,用api中的GetDyGoodsID) /// /// 包含抖音链接的文本 /// public static string GetDyGoodsID(string mess, out string promotion_id, out string title) { title = string.Empty; promotion_id = string.Empty; if (string.IsNullOrWhiteSpace(mess) || Regex.IsMatch(mess, @"\[图片=(.+?)\]") || Regex.IsMatch(mess, @"\[视频=(.+?)\]") || Regex.IsMatch(mess, @"\[卡片=(.+?)\]") || Regex.IsMatch(mess, @"\[公告=(.+?)\]") || Regex.IsMatch(mess, @"\[语音=(.+?)\]") ) return string.Empty; bool flag = false; if (mess.Contains("goods_id")) flag = true; //获取链接的正则表达式 var _url = HttpExtend.RegexMatchUrl(mess); #region 之前的拿不到promotion_id,就先不处理 这种连接: https://haohuo.jinritemai.com/views/product/item2?id=3479881104721353508 //if (!flag && _url.Success)//有链接的情况 //{ // mess = _url.Groups["链接"].Value; // if (Regex.IsMatch(mess, "http[s]?://haohuo.")) // { // if (mess == null || !mess.Contains("?id=")) return string.Empty; // else flag = true; // } // else // flag = true; //} #region 新的 if (!flag && !string.IsNullOrWhiteSpace(_url))//有链接的情况 { mess = _url; if (Regex.IsMatch(mess, "http[s]?://haohuo.")) { if (mess == null || !mess.Contains("?id=")) return string.Empty; else flag = true; } else if (Regex.IsMatch(mess, "http[s]?://www.iesdouyin.", RegexOptions.IgnoreCase)) { if (mess == null || mess.ToLower().Contains("object_id=")) { var reg = Regex.Match(mess, @"object_id=(?:\d+?)_(?\d+?)_(?\d+?)_(?:\d+?)", RegexOptions.IgnoreCase | RegexOptions.Multiline); if (reg.Success) { //https://www.iesdouyin.com/?schema_type=20&object_id=1460607342872671_3508329772335892947_3508329411592229101_0 promotion_id = reg.Groups["promotion_id"].ToString(); return reg.Groups["goodid"].ToString(); } } else flag = true; } else if (Regex.IsMatch(mess, "http[s]?://v.douyin.com", RegexOptions.IgnoreCase))//链接: https://v.douyin.com/LbN2pka/ { HttpHelper http = new HttpHelper(); HttpItem item = new HttpItem() { URL = mess, Method = "GET", Timeout = 100000, ReadWriteTimeout = 30000, IsToLower = false, Cookie = "", UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:18.0) Gecko/20100101 Firefox/18.0", Accept = "text/html, application/xhtml+xml, */*", ContentType = "application/x-www-form-urlencoded", Referer = "", Allowautoredirect = false, AutoRedirectCookie = false, Postdata = "", }; HttpResult result = http.GetHtml(item); mess = result.Html; flag = true; } else flag = true; } #endregion #endregion if (flag) { var regUrl = Regex.Match(mess, @"(yukhj.com|m.tb.cn|m.yeeoq.top|m.uqlsi|m.rijni|m.gkqmj|m.mulwt|uland\\.taobao\\.com|sjtm.me|aa5d.com|d11a.com|f61[a-z].com|92db.com|yqt.so|e22a.com|f61b.com|detail.m.tmall.hk|k.83jie.com|ewqcxz.com|items.alitrip.com|tmqd.me|detail.tmall.hk|url.cn|taobao.com|mashort.cn|tmall.com|tb.cn|detail.yao.95095.com|laiwang.com|dwz.cn|juhuasuan.com|detail.m.tmall.com|zmnxbc.com|s.click.taobao|detail.liangxinyao.com|m.tb.cn)", RegexOptions.IgnoreCase); if (!regUrl.Success) { var messTmp = HttpHelper.URLDecode(HttpHelper.URLDecode(mess)).Replace("&", "&"); var reg = Regex.Match(messTmp, @"origin_id=\d+_(?\d+)", RegexOptions.IgnoreCase | RegexOptions.Multiline); if (!reg.Success) reg = Regex.Match(messTmp, @"alkey=\d+?_\d+?_\d+?_(?\d+)_\d+?", RegexOptions.IgnoreCase | RegexOptions.Multiline); if (reg.Success) promotion_id = reg.Groups["promotion_id"].ToString(); reg = Regex.Match(messTmp, @"\?id=(?<商品id>\d{4,})", RegexOptions.IgnoreCase); if (!reg.Success) reg = Regex.Match(messTmp, @"product_id=(?<商品id>\d+)", RegexOptions.IgnoreCase); if (!reg.Success) reg = Regex.Match(messTmp, @"""product_id\\\"":\\\""(?<商品id>\d+)\\\""", RegexOptions.IgnoreCase); if (!reg.Success) reg = Regex.Match(messTmp, @"""product_id"":(?<商品id>\d+)", RegexOptions.IgnoreCase); if (!reg.Success) reg = Regex.Match(messTmp, @"""product_id"":""(?<商品id>\d+)""", RegexOptions.IgnoreCase); if (!reg.Success) reg = Regex.Match(messTmp, @"product_id"":""(?<商品id>\d+)""", RegexOptions.IgnoreCase); if (!reg.Success) //reg = Regex.Match(messTmp, @"&id=(?<商品id>\d{4,})", RegexOptions.IgnoreCase); reg = Regex.Match(messTmp, @"\\\\\\""gid\\\\\\"":(?<商品id>\d{4,}),", RegexOptions.IgnoreCase); if (!reg.Success) reg = Regex.Match(messTmp, @"origin_id=\d+?_\d+?_(?<商品id>\d+)", RegexOptions.IgnoreCase); if (!reg.Success) reg = Regex.Match(messTmp, @"""search_result_id\\+?"":\\+?""(?<商品id>\d+)", RegexOptions.IgnoreCase); if (!reg.Success) reg = Regex.Match(messTmp, @"""pre_product_id\\+?"":\\+?""(?<商品id>\d+)", RegexOptions.IgnoreCase); if (reg.Success) return reg.Groups["商品id"].ToString(); reg = Regex.Match(messTmp, @"object_id=(?:\d+)_(?<推广id>\d+)_(?<商品id>\d+)_(?:\d+)", RegexOptions.IgnoreCase); if (reg.Success) { promotion_id = reg.Groups["推广id"].ToString(); return reg.Groups["商品id"].ToString(); } } } else { //var regs1 = Regex.Matches(mess, "[^A-Za-z0-9]?([A-Za-z0-9]{11})[^A-Za-z0-9]?"); var reg = Regex.Match(mess, "([︽#]{2}[A-Za-z0-9]{11,14}[︽#]{2})"); if (reg.Success) { return DouyinApi.AnalysisCmdToGoodsUrl(reg.Groups[1].Value, out promotion_id, out title); } } return string.Empty; } public static string GetDyGoodsID(string mess, out string promotion_id) { promotion_id = string.Empty; if (string.IsNullOrWhiteSpace(mess) || Regex.IsMatch(mess, @"\[图片=(.+?)\]") || Regex.IsMatch(mess, @"\[视频=(.+?)\]") || Regex.IsMatch(mess, @"\[卡片=(.+?)\]") || Regex.IsMatch(mess, @"\[公告=(.+?)\]") || Regex.IsMatch(mess, @"\[语音=(.+?)\]") ) return string.Empty; bool flag = false; if (mess.Contains("goods_id")) flag = true; //获取链接的正则表达式 var _url = HttpExtend.RegexMatchUrl(mess); //之前的拿不到promotion_id,就先不处理 这种连接: https://haohuo.jinritemai.com/views/product/item2?id=3479881104721353508 #region 新的 if (!flag && !string.IsNullOrWhiteSpace(_url))//有链接的情况 { mess = _url; if (Regex.IsMatch(mess, "http[s]?://haohuo.", RegexOptions.IgnoreCase)) { if (mess == null || !mess.Contains("?id=")) return string.Empty; else flag = true; } else if (Regex.IsMatch(mess, "http[s]?://www.iesdouyin.", RegexOptions.IgnoreCase)) { if (mess == null || mess.ToLower().Contains("object_id=")) { var reg = Regex.Match(mess, @"object_id=(?:\d+?)_(?\d+?)_(?\d+?)_(?:\d+?)", RegexOptions.IgnoreCase | RegexOptions.Multiline); if (reg.Success) { //https://www.iesdouyin.com/?schema_type=20&object_id=1460607342872671_3508329772335892947_3508329411592229101_0 promotion_id = reg.Groups["promotion_id"].ToString(); return reg.Groups["goodid"].ToString(); } } else flag = true; } else if (Regex.IsMatch(mess, "http[s]?://v.douyin.com", RegexOptions.IgnoreCase))//链接: https://v.douyin.com/LbN2pka/ { HttpHelper http = new HttpHelper(); HttpItem item = new HttpItem() { URL = mess, Method = "GET", Timeout = 100000, ReadWriteTimeout = 30000, IsToLower = false, Cookie = "", UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:18.0) Gecko/20100101 Firefox/18.0", Accept = "text/html, application/xhtml+xml, */*", ContentType = "application/x-www-form-urlencoded", Referer = "", Allowautoredirect = false, AutoRedirectCookie = false, Postdata = "", }; HttpResult result = http.GetHtml(item); mess = result.Html; flag = true; } else flag = true; } #endregion if (flag) { var regUrl = Regex.Match(mess, @"(yukhj.com|m.tb.cn|m.yeeoq.top|m.uqlsi|m.rijni|m.gkqmj|m.mulwt|uland\\.taobao\\.com|sjtm.me|aa5d.com|d11a.com|f61[a-z].com|92db.com|yqt.so|e22a.com|f61b.com|detail.m.tmall.hk|k.83jie.com|ewqcxz.com|items.alitrip.com|tmqd.me|detail.tmall.hk|url.cn|taobao.com|mashort.cn|tmall.com|tb.cn|detail.yao.95095.com|laiwang.com|dwz.cn|juhuasuan.com|detail.m.tmall.com|zmnxbc.com|s.click.taobao|detail.liangxinyao.com|m.tb.cn)", RegexOptions.IgnoreCase); if (!regUrl.Success) { var messTmp = HttpHelper.URLDecode(HttpHelper.URLDecode(mess)).Replace("&", "&"); var reg = Regex.Match(messTmp, @"origin_id=\d+_(?\d+)", RegexOptions.IgnoreCase | RegexOptions.Multiline); if (!reg.Success) reg = Regex.Match(messTmp, @"alkey=\d+?_\d+?_\d+?_(?\d+)_\d+?", RegexOptions.IgnoreCase | RegexOptions.Multiline); if (reg.Success) { promotion_id = reg.Groups["promotion_id"].ToString(); //判断是promotion_id是推广id还是商品id return promotion_id; } //这里可能还有其他的可能 else { } } } else { //var regs1 = Regex.Matches(mess, "[^A-Za-z0-9]?([A-Za-z0-9]{11})[^A-Za-z0-9]?"); var reg = Regex.Match(mess, "([︽#]{2}[A-Za-z0-9]{11,14}[︽#]{2})"); if (reg.Success) { var title = string.Empty; return DouyinApi.AnalysisCmdToGoodsUrl(reg.Groups[1].Value, out promotion_id, out title); } } return string.Empty; } /// /// 唤醒app链接 /// /// 登录用户id /// 推广ID /// 商品ID /// public static string DyAppUrl(string uid, string promotion_id, string product_id) { //https://www.iesdouyin.com/?schema_type=20&object_id=” + 抖音账号原始id () + “_” + 推广商品id + “_” + 商品id + “_0 //https://www.iesdouyin.com/?schema_type=20&object_id=1460607342872671_3507936844719500526_3507742501886900674_0 return $"https://www.iesdouyin.com/?schema_type=20&object_id={uid}_{promotion_id}_{product_id}_0"; } /// /// 抖音短连接 /// /// /// public static string ShortDyUrl(string url) { for (int i = 0; i < 3; i++) { try { //{"code":0,"message":"success","now":1640269188,"data":"https://v.douyin.com/8Ntmkfp/"} var shortUrl = "https://lf.snssdk.com/shorten/?belong=aweme&target=" + HttpHelper.URLEncode(url); HttpHelper http = new HttpHelper(); var html = http.GetHtml(shortUrl).Html; if (!string.IsNullOrWhiteSpace(html)) { var jObj = JObject.Parse(html); if (jObj["code"] != null && ((int)jObj["code"] == 0 || jObj["message"]?.ToString() == "success")) { var data = jObj["data"]?.ToString(); if (data.ToLower().Contains("douyin")) return data; } } } catch (Exception ex) { } Thread.Sleep(200); } return url; } #region 口令算法 private static string Md5(string str) { MD5 md5 = MD5.Create(); var bytes = md5.ComputeHash(Encoding.UTF8.GetBytes(str)); // 第五步:把二进制转化为大写的十六进制 StringBuilder result = new StringBuilder(); for (int i = 0; i < bytes.Length; i++) { result.Append(bytes[i].ToString("x2")); } return result.ToString(); } private static long GetTimeStamp(bool Seconds = true) { TimeSpan ts = DateTime.Now - new DateTime(1970, 1, 1, 0, 0, 0, 0); if (Seconds) return Convert.ToInt64(ts.TotalSeconds); else return Convert.ToInt64(ts.TotalMilliseconds); } private static string hex_string(long num) { var tmp_string = num.ToString("x2"); if (tmp_string.Length < 2) tmp_string = '0' + tmp_string; return tmp_string; } private static int reverse(long num) { var tmp_string = hex_string(num); return Convert.ToInt32(tmp_string.Substring(1) + tmp_string.Substring(0, 1), 16); } private static int RIT(long num) { var result = ""; var tmp_string = Convert.ToString(num, 2); ;// bin(num)[2]; while (tmp_string.Length < 8) { tmp_string = "0" + tmp_string; } for (int i = 0; i < 8; i++) { result = result + tmp_string[7 - i]; } return Convert.ToInt32(result, 2); } private static Dictionary CreateXg(string url, string data = "", string cookies = "") { var gorgon = string.Empty; gorgon += Md5(url); if (!string.IsNullOrEmpty(data)) gorgon += Md5(data); else gorgon += "00000000000000000000000000000000"; if (!string.IsNullOrEmpty(cookies)) gorgon += Md5(cookies); else gorgon += "00000000000000000000000000000000"; gorgon += "00000000000000000000000000000000"; return calc_xg(gorgon); } private static Dictionary calc_xg(string data) { var ts = 1646128554;// GetTimeStamp(); var ts_x = 1646128554000;// GetTimeStamp(true); var len = 0x14; var key = new long[] { 0xDF, 0x77, 0xB9, 0x40, 0xb9, 0x9b, 0x84, 0x83, 0xd1, 0xb9, 0xcb, 0xd1, 0xf7, 0xc2, 0xb9, 0x85, 0xc3, 0xd0, 0xfb, 0xc3}; var param_list = new List(); long H = 0; // for i in range(0, 12, 4): for (int i = 0; i < 12; i = i + 4) { var temp = data.Substring(8 * i, 8);// data[8 * i: 8 * (i + 1)]; for (int j = 0; j < 4; j++) { // int(temp[j * 2:(j + 1) * 2], 16) var V = temp.Substring(j * 2, 2); H = Convert.ToInt32(temp.Substring(j * 2, 2), 16); param_list.Add(H); } } //param_list.extend([0x0, 0x6, 0xB, 0x1C]) param_list.Add(0x0); param_list.Add(0x6); param_list.Add(0xB); param_list.Add(0x1C); H = Convert.ToInt32(ts.ToString("x2"), 16); param_list.Add((H & 0xFF000000) >> 24); param_list.Add((H & 0x00FF0000) >> 16); param_list.Add((H & 0x0000FF00) >> 8); param_list.Add((H & 0x000000FF) >> 0); var eor_result_list = new List(); for (int i = 0; i < 20; i++) { //eor_result_list.append(A ^ B) eor_result_list.Add(param_list[i] ^ key[i]); } for (int i = 0; i < 20; i++) { var C = reverse(eor_result_list[i]); var D = eor_result_list[(i + 1) % len]; var E = C ^ D; var F = RIT(E); H = (F ^ 0xFFFFFFFF ^ len) & 0xFF; eor_result_list[i] = H; } var result = ""; foreach (var param in eor_result_list) { result += hex_string(param); } var xgorgon = "0408b0d30000" + result; Dictionary XgDatas = new Dictionary(); XgDatas["X-Gorgon"] = xgorgon; XgDatas["X-Khronos"] = ts.ToString(); XgDatas["X-SS-REQ-TICKET"] = ts_x.ToString(); return XgDatas; } private static Dictionary _device_info; private static Dictionary device_info { get { if (_device_info == null) { _device_info = new Dictionary(); _device_info["device_id"] = "655729250010781"; _device_info["install_id"] = "2590869714905182"; _device_info["device_id_str"] = "655729250010781"; _device_info["install_id_str"] = "2590869714905182"; _device_info["new_user"] = "1"; _device_info["device_token"] = "AAA45JXIIXBRRAL6BCSHWP2ZFWET2NU65PHNP6EREEYPF2WJ6NMY2TKTAZXV666654U44IRIACWWQKGDEJWZQS7JMB7KTT5MOQ6WEFSOPMATGN6FG2EYRZTG4YQ5A"; } return _device_info; } } /// /// 连接转口令 /// /// /// public static string CreateKouling(string url) { //https://haohuo.jinritemai.com/views/product/item2?id=3504966563675637147&origin_type=2002170010&origin_id=1460607342872671_3507916645882543574&alkey=1128_1460607342872671_0_3507916645882543574_011&sec_author_id=MS4wLjABAAAAEnpGkVpQS-WLtu18Me9lZ-9A284xvNbstU-HGyQK3vn2Pi-WtDWPRAXLCnSRvDsF&buyin_track=COgIEN-Q0K6ijcwCGgQIABAAIgkyB05ldmpGQ2Mom4uIjNCQidIwMAE&c_biz_combo=2 var uri = new Uri(url); var collection = HttpUtility.ParseQueryString(uri.Query);//默认采用UTF-8编码,当然也可以传入特定编码进行解析 //var collection= HttpUtility.ParseQueryString(uri.Query,Encoding.ASCII); var id = collection["id"]; var promotion_id = collection["origin_id"]?.Split('_')[1]; var sec_author_id = collection["sec_author_id"]; var buyin_track = collection["buyin_track"]; return CreateKouling(id, promotion_id, sec_author_id, buyin_track); } /// /// /// /// 商品ID /// 推广地址ID /// 推广人信息 private static string CreateKouling(string product_id, string promotion_id, string sec_author_id, string buyin_track) { try { var ts = GetTimeStamp(); //var sslocal = $"sslocal%3A%2F%2Fec_goods_detail%2F%3Fsec_author_id%3D%26promotion_id%3D{promotion_id}%26product_id%3D{product_id}%26commodity_id%3D%26commodity_type%3D%26promotion_source%3D%26enter_method%3Dclick_video_tag%26meta_params%3D%7B%22is_star_atlas%22%3Afalse%2C%22is_ad_traffic%22%3Afalse%2C%22entrance_info%22%3A%22%22%7D%26source_page%3Dtoken%26ecom_share_track_params%3D%7B%22is_ec_shopping%22%3A%221%22%7D%26request_additions%3D%7B%22sec_author_id%22%3A%22{}%22%7D"; var sslocal = "sslocal://ec_goods_detail/?sec_author_id=&promotion_id=" + promotion_id + "&product_id=" + product_id + "&commodity_id=&commodity_type=&promotion_source=&enter_method=click_video_tag&meta_params={\"is_star_atlas\":false,\"is_ad_traffic\":false,\"entrance_info\":\"\"}&source_page=token&ecom_share_track_params={\"is_ec_shopping\":\"1\"}&request_additions={\"sec_author_id\":\"" + sec_author_id + "\",\"buyin_track\":\"" + buyin_track + "\"}"; sslocal = CsharpHttpHelper.HttpHelper.URLEncode(sslocal, Encoding.UTF8); var otherData = $"&schema_type=20&object_id={product_id}©_type=auto&iid={device_info["install_id"]}&device_id={device_info["device_id"]}&ac=wifi&channel=douyin_juyouliang_and4&aid=1128&app_name=aweme&version_code=190200&version_name=19.2.0&device_platform=android&os=android&ssmix=a&device_type=Pixel+3&device_brand=google&language=zh&os_api=29&os_version=10&manifest_version_code=190201&resolution=1080*2028&dpi=440&update_version_code=19209900&_rticket=1646117336178&package=com.ss.android.ugc.aweme&cpu_support64=true&host_abi=armeabi-v7a&is_guest_mode=0&app_type=normal&minor_status=0&appTheme=light&need_personal_recommend=1&is_android_pad=0&ts={ts}&cdid=a090acdf-53e8-47a7-85ca-7d4f8d3d3b7c"; var param = $"schema={sslocal}{otherData}"; var url = $"https://api5-normal-c-lq.amemv.com/aweme/v2/platform/share/command/gen/?{param}"; var Headers = CreateXg(param); var http = new CsharpHttpHelper.HttpHelper(); var item = new CsharpHttpHelper.HttpItem() { URL = url, Method = "post", ContentType = "application/json", Postdata = "{'body': 'null'}", Timeout = 5000, UserAgent = "com.ss.android.ugc.aweme/190201 (Linux; U; Android 10; zh_CN_#Hans; Pixel 3; Build/QQ3A.200805.001; Cronet/TTNetVersion:28eaf52b 2021-12-28 QuicVersion:68cae75d 2021-08-12)" }; foreach (var v in Headers) { item.Header[v.Key] = v.Value; } //3:/ - var html = http.GetHtml(item).Html; var json = JObject.Parse(html); if (json["command_v2"] != null) return json["command_v2"].ToString(); else throw new Exception(html); } catch (Exception ex) { EventClient.OnEvent("", $"抖音口令转换失败:{ex.Message}"); } return String.Empty; } #endregion } }