helper.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. const helper = {}
  2. const crypto = require('crypto');
  3. const dns = require('dns');
  4. helper.base64 = function(str){
  5. return Buffer.from(str).toString('base64')
  6. }
  7. // '&channel=rd'
  8. helper.checkChannel = function(url,key) {
  9. return url.includes(key);
  10. }
  11. helper.capitalize_string = function(K) {
  12. // 将字符串按 "-" 分割,对每个单词首字母大写,然后重新用 "-" 连接
  13. return K.split("-")
  14. .map(word => word.charAt(0).toUpperCase() + word.slice(1))
  15. .join("-");
  16. }
  17. helper.query_raw_text = function(K) {
  18. if (!K || typeof K !== 'object') {
  19. return "";
  20. }
  21. const result = [];
  22. const replace_dict = {
  23. "'": "%27",
  24. "/": "%2F",
  25. "+": "%2B",
  26. "=": "%3D",
  27. "&": "%26",
  28. "@": "%40",
  29. "#": "%23",
  30. ";": "%3B",
  31. "?": "%3F",
  32. "%5B": "[",
  33. "%5D": "]",
  34. "%20": "+"
  35. };
  36. // 自定义编码函数
  37. function customEncode(str) {
  38. let encoded = encodeURIComponent(str);
  39. for (const [char, encoding] of Object.entries(replace_dict)) {
  40. encoded = encoded.replace(new RegExp(escapeRegExp(char), 'g'), encoding);
  41. }
  42. return encoded;
  43. }
  44. // 转义正则表达式特殊字符
  45. function escapeRegExp(string) {
  46. return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
  47. }
  48. for (const [et, value] of Object.entries(K)) {
  49. if (value === null || value === undefined) {
  50. continue;
  51. }
  52. if (Array.isArray(value)) {
  53. // 处理数组
  54. for (const nt of value) {
  55. result.push(`${et}[]=${customEncode(String(nt))}`);
  56. }
  57. } else if (typeof value === 'object') {
  58. // 处理对象
  59. let nt = `${et}=${customEncode(JSON.stringify(value))}`;
  60. nt = nt.replace(/%5B/g, "[").replace(/%5D/g, "]");
  61. result.push(nt);
  62. } else {
  63. // 处理普通值
  64. result.push(`${et}=${customEncode(String(value))}`);
  65. }
  66. }
  67. return result.join("&");
  68. }
  69. helper.get_signature = function(token,K, O = 'agentd3dGiJc651gSQ28w9', H = 'https://new-media-fx.qimao.com/api') {
  70. // 构建 Authorization 对象
  71. const et = {
  72. "Authorization": token
  73. };
  74. // 获取排序后的参数字符串
  75. const nt = helper.params_sort(et);
  76. const at = helper.params_sort(K.get('data'), false);
  77. const it = helper.query_raw_text(K.get('params'));
  78. // 确定连接符
  79. const ot = K['url'].includes('?') ? '&' : '?';
  80. // 构建URL
  81. let st = K.get('url') + (it ? ot + it : '');
  82. if (H && st.startsWith(H)) {
  83. st = st.substring(H.length);
  84. }
  85. // 构建最终签名字符串
  86. const ct = `${st}&${nt}&${at}&${O}`;
  87. // 调试输出
  88. /*
  89. console.log('nt:', nt);
  90. console.log('at:', at);
  91. console.log('it:', it);
  92. console.log('ot:', ot);
  93. console.log('st:', st);
  94. console.log('ct:', ct);
  95. */
  96. return helper.sha256_hash(ct);
  97. }
  98. helper.params_sort = function(K, O = true) {
  99. if (!K || typeof K !== 'object') {
  100. return "";
  101. }
  102. // 按照键的字母顺序排序
  103. const sorted_keys = Object.keys(K).sort();
  104. const result = [];
  105. for (const et of sorted_keys) {
  106. if (K[et] !== null && K[et] !== undefined) {
  107. // 根据 O 决定是否首字母大写
  108. const nt = O ? helper.capitalize_string(et) : et;
  109. // 使用 JSON.stringify 来转换为 JSON 格式的字符串
  110. result.push(`${nt}=${JSON.stringify(K[et])}`);
  111. }
  112. }
  113. // 拼接成查询字符串
  114. return result.join("&");
  115. }
  116. helper.sha256_hash = function(data) {
  117. const crypto = require('crypto');
  118. // 创建一个 sha256 哈希对象
  119. const hash = crypto.createHash('sha256');
  120. // 更新哈希对象,传入数据
  121. hash.update(data, 'utf-8');
  122. // 获取最终的哈希值,返回一个十六进制字符串
  123. return hash.digest('hex');
  124. }
  125. helper.resolveDomain = async function(domain) {
  126. // 创建新的DNS解析器实例
  127. const resolver = new dns.promises.Resolver();
  128. // 尝试不同的DNS服务器
  129. const dnsServers = [
  130. '8.8.8.8', // Google DNS
  131. '8.8.4.4', // Google DNS备用
  132. '1.1.1.1', // Cloudflare DNS
  133. '1.0.0.1', // Cloudflare DNS备用
  134. '223.5.5.5', // 阿里DNS
  135. '223.6.6.6', // 阿里DNS备用
  136. '119.29.29.29', // 腾讯DNS
  137. '114.114.114.114' // 114 DNS
  138. ];
  139. for (const dnsServer of dnsServers) {
  140. try {
  141. console.log(`\nTrying DNS server: ${dnsServer}`);
  142. resolver.setServers([dnsServer]);
  143. const addresses = await resolver.resolve4(domain);
  144. console.log(`Success with DNS server ${dnsServer}:`);
  145. console.log('IP Addresses:', addresses);
  146. // 如果成功找到IP,尝试ping或TCP连接测试
  147. for (const ip of addresses) {
  148. console.log(`Testing connectivity to IP: ${ip}`);
  149. // 这里可以添加连接测试代码
  150. }
  151. return addresses;
  152. } catch (error) {
  153. console.error(`Failed with DNS server ${dnsServer}:`, error.message);
  154. }
  155. }
  156. throw new Error('Failed to resolve domain with all DNS servers');
  157. }
  158. helper.md5 = function(text) {
  159. return crypto.createHash('md5').update(text).digest('hex');
  160. }
  161. helper.getSign = function(distributorId,secretKey) {
  162. const params = [distributorId, secretKey, helper.getCurrentUnixTimestamp()];
  163. // 将参数数组中的每个元素转换为字符串并连接成一个单一的字符串
  164. const paramStr = params.map(String).join('');
  165. // 使用 MD5 算法生成哈希值
  166. const hash = crypto.createHash('md5');
  167. hash.update(paramStr);
  168. // 返回哈希值的十六进制表示
  169. return hash.digest('hex');
  170. }
  171. helper.getCurrentUnixTimestamp = function() {
  172. return Math.floor(Date.now() / 1000)
  173. }
  174. helper.getFqRequestOpt = function(key,sid_tt='b0390e26648a71801795b3b13c9d7d20'){
  175. const options = {
  176. url: `https://api.whbfxb.cn/api/novelsale/reader/get/content/v1/?aid=40013183&device_brand=realme&device_platform=android&device_type=RMX2020&mp_sdk_version=3.21.0&novelsale_app_scene=023001&version_code=230&key=${key}&item_source=1&module_name=ad_link&click_id=__CLICKID__&clickid=__CLICKID__&creativetype=__CTYPE__&demand_id=0&item_id=&media_source=1&mid1=__MID1__&mid2=__MID2__&mid3=__MID3__&mid4=__MID4__&mid5=__MID5__&projectid=__PROJECT_ID__&promotionid=__PROMOTION_ID__&request_id=__REQUESTID__&book_id=&host_novelsale_app_id=40013183`,
  177. headers: {
  178. 'cookie': `sid_tt=${sid_tt}; ` +
  179. 'ssid_ucp_v1=1.0.0-KDU0Nzc2NjZmZjRhNTZkYWM5YTMwN2ZmNzAyODMwNjFjZmQyMzA4OTYKFQjkktCcvMz_ARDGw7W6Bhjv6CA4CBoCbHEiIDhjOTg2NjgwY2Q4NzE2YmEzMmJhMjBjM2MwMmEwOTJk; ' +
  180. 'is_staff_user=false; ' +
  181. 'sid_ucp_v1=1.0.0-KDU0Nzc2NjZmZjRhNTZkYWM5YTMwN2ZmNzAyODMwNjFjZmQyMzA4OTYKFQjkktCcvMz_ARDGw7W6Bhjv6CA4CBoCbHEiIDhjOTg2NjgwY2Q4NzE2YmEzMmJhMjBjM2MwMmEwOTJk; ' +
  182. `sessionid=${sid_tt}; ` +
  183. 'ttwid=1%7CdTM3VEQ9KwlMsV2FV6Gaxv5CBXmd5i44fjDoZV_a8g8%7C1733124580%7Cc0952b31723a2f5d5e4b9f84a1c29cb7cbde5592906de58798b92a695d3fb292; ' +
  184. `sessionid_ss=${sid_tt}; ` +
  185. `sid_guard=${sid_tt}%7C1733124550%7C5184000%7CFri%2C+31-Jan-2025+07%3A29%3A10+GMT; ` +
  186. 'uid_tt=3361cf574bbdd82820371b4667fc9b91; ' +
  187. 'passport_csrf_token=f7e1da8668d26b8509725bcf7fa95628; ' +
  188. 'uid_tt_ss=3361cf574bbdd82820371b4667fc9b91; ' +
  189. 'passport_csrf_token_default=f7e1da8668d26b8509725bcf7fa95628; ' +
  190. 'store-region=cn-gs; ' +
  191. 'store-region-src=uid; ' +
  192. 'n_mh=9-mIeuD4wZnlYrrOvfzG3MuT6aQmCUtmr8FxV8Kl8xY',
  193. 'user-agent': 'Mozilla/5.0 (Linux; Android 9; Mi Note 3 Build/PKQ1.181007.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/120.0.6099.193 Mobile Safari/537.36 aweme/29.4.0 ToutiaoMicroApp/3.21.0 PluginVersion/29409006',
  194. 'content-type': 'application/json',
  195. 'referer': 'https://tmaservice.developer.toutiao.com/?appid=tt1b316d8c8401e42101&version=2.7.0'
  196. }
  197. };
  198. return options
  199. }
  200. helper.getHyCreateLinkOpt = function(token){
  201. const options = {
  202. url: `https://ms.zhangwenpindu.cn/manage/distribution/createLink`,
  203. headers: {
  204. 'Accept': 'application/json, text/plain, */*',
  205. 'Accept-Language': 'zh-CN,zh;q=0.9',
  206. 'Cache-Control': 'no-cache',
  207. 'Connection': 'keep-alive',
  208. 'Content-Type': 'application/x-www-form-urlencoded',
  209. 'Origin': 'https://manage.zhangwenpindu.cn',
  210. 'Pragma': 'no-cache',
  211. 'Referer': 'https://manage.zhangwenpindu.cn/',
  212. 'Sec-Fetch-Dest': 'empty',
  213. 'Sec-Fetch-Mode': 'cors',
  214. 'Sec-Fetch-Site': 'same-site',
  215. 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36',
  216. 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"',
  217. 'sec-ch-ua-mobile': '?0',
  218. 'sec-ch-ua-platform': '"Windows"'
  219. }
  220. };
  221. options.headers['Authorization'] = `Bearer ${token}`
  222. return options
  223. }
  224. helper.getTimeStampByHourMinute = function(timeStr,otherStr=null) {
  225. // 解析时间字符串
  226. const [hours, minutes] = timeStr.split(':').map(Number);
  227. let date;
  228. if (otherStr) {
  229. // 如果提供了日期字符串,解析完整的日期时间
  230. date = new Date(otherStr);
  231. // 设置时分秒
  232. date.setHours(hours, minutes, 0, 0);
  233. } else {
  234. // 如果没有提供日期,使用今天的日期
  235. date = new Date();
  236. date.setHours(hours, minutes, 0, 0);
  237. }
  238. return date;
  239. }
  240. helper.getLocalDate = function() {
  241. const d = new Date();
  242. const year = d.getFullYear();
  243. const month = String(d.getMonth() + 1).padStart(2, '0');
  244. const day = String(d.getDate()).padStart(2, '0');
  245. return `${year}-${month}-${day}`;
  246. }
  247. helper.getPaginationParams = function(total, pageSize = 500) {
  248. const pages = Math.ceil(total / pageSize);
  249. const result = [];
  250. for(let page = 0; page < pages; page++) {
  251. const offset = page * pageSize;
  252. const limit = Math.min(pageSize, total - offset);
  253. result.push({
  254. page,
  255. offset,
  256. limit
  257. });
  258. }
  259. return result;
  260. }
  261. module.exports = helper;