const helper = {} const crypto = require('crypto'); const dns = require('dns'); helper.base64 = function(str){ return Buffer.from(str).toString('base64') } // '&channel=rd' helper.checkChannel = function(url,key) { return url.includes(key); } helper.checkChannelListKey = function(url,key_list) { for (let index = 0; index < key_list.length; index++) { const key = key_list[index]; if(!this.checkChannel(url,key)){ return false } } return true } helper.capitalize_string = function(K) { // 将字符串按 "-" 分割,对每个单词首字母大写,然后重新用 "-" 连接 return K.split("-") .map(word => word.charAt(0).toUpperCase() + word.slice(1)) .join("-"); } helper.query_raw_text = function(K) { if (!K || typeof K !== 'object') { return ""; } const result = []; const replace_dict = { "'": "%27", "/": "%2F", "+": "%2B", "=": "%3D", "&": "%26", "@": "%40", "#": "%23", ";": "%3B", "?": "%3F", "%5B": "[", "%5D": "]", "%20": "+" }; // 自定义编码函数 function customEncode(str) { let encoded = encodeURIComponent(str); for (const [char, encoding] of Object.entries(replace_dict)) { encoded = encoded.replace(new RegExp(escapeRegExp(char), 'g'), encoding); } return encoded; } // 转义正则表达式特殊字符 function escapeRegExp(string) { return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } for (const [et, value] of Object.entries(K)) { if (value === null || value === undefined) { continue; } if (Array.isArray(value)) { // 处理数组 for (const nt of value) { result.push(`${et}[]=${customEncode(String(nt))}`); } } else if (typeof value === 'object') { // 处理对象 let nt = `${et}=${customEncode(JSON.stringify(value))}`; nt = nt.replace(/%5B/g, "[").replace(/%5D/g, "]"); result.push(nt); } else { // 处理普通值 result.push(`${et}=${customEncode(String(value))}`); } } return result.join("&"); } helper.get_signature = function(token,K, O = 'agentd3dGiJc651gSQ28w9', H = 'https://new-media-fx.qimao.com/api') { // 构建 Authorization 对象 const et = { "Authorization": token }; // 获取排序后的参数字符串 const nt = helper.params_sort(et); const at = helper.params_sort(K.get('data'), false); const it = helper.query_raw_text(K.get('params')); // 确定连接符 const ot = K['url'].includes('?') ? '&' : '?'; // 构建URL let st = K.get('url') + (it ? ot + it : ''); if (H && st.startsWith(H)) { st = st.substring(H.length); } // 构建最终签名字符串 const ct = `${st}&${nt}&${at}&${O}`; // 调试输出 /* console.log('nt:', nt); console.log('at:', at); console.log('it:', it); console.log('ot:', ot); console.log('st:', st); console.log('ct:', ct); */ return helper.sha256_hash(ct); } helper.params_sort = function(K, O = true) { if (!K || typeof K !== 'object') { return ""; } // 按照键的字母顺序排序 const sorted_keys = Object.keys(K).sort(); const result = []; for (const et of sorted_keys) { if (K[et] !== null && K[et] !== undefined) { // 根据 O 决定是否首字母大写 const nt = O ? helper.capitalize_string(et) : et; // 使用 JSON.stringify 来转换为 JSON 格式的字符串 result.push(`${nt}=${JSON.stringify(K[et])}`); } } // 拼接成查询字符串 return result.join("&"); } helper.sha256_hash = function(data) { const crypto = require('crypto'); // 创建一个 sha256 哈希对象 const hash = crypto.createHash('sha256'); // 更新哈希对象,传入数据 hash.update(data, 'utf-8'); // 获取最终的哈希值,返回一个十六进制字符串 return hash.digest('hex'); } helper.resolveDomain = async function(domain) { // 创建新的DNS解析器实例 const resolver = new dns.promises.Resolver(); // 尝试不同的DNS服务器 const dnsServers = [ '8.8.8.8', // Google DNS '8.8.4.4', // Google DNS备用 '1.1.1.1', // Cloudflare DNS '1.0.0.1', // Cloudflare DNS备用 '223.5.5.5', // 阿里DNS '223.6.6.6', // 阿里DNS备用 '119.29.29.29', // 腾讯DNS '114.114.114.114' // 114 DNS ]; for (const dnsServer of dnsServers) { try { console.log(`\nTrying DNS server: ${dnsServer}`); resolver.setServers([dnsServer]); const addresses = await resolver.resolve4(domain); console.log(`Success with DNS server ${dnsServer}:`); console.log('IP Addresses:', addresses); // 如果成功找到IP,尝试ping或TCP连接测试 for (const ip of addresses) { console.log(`Testing connectivity to IP: ${ip}`); // 这里可以添加连接测试代码 } return addresses; } catch (error) { console.error(`Failed with DNS server ${dnsServer}:`, error.message); } } throw new Error('Failed to resolve domain with all DNS servers'); } helper.md5 = function(text) { return crypto.createHash('md5').update(text).digest('hex'); } helper.getSign = function(distributorId,secretKey) { const params = [distributorId, secretKey, helper.getCurrentUnixTimestamp()]; // 将参数数组中的每个元素转换为字符串并连接成一个单一的字符串 const paramStr = params.map(String).join(''); // 使用 MD5 算法生成哈希值 const hash = crypto.createHash('md5'); hash.update(paramStr); // 返回哈希值的十六进制表示 return hash.digest('hex'); } helper.getCurrentUnixTimestamp = function() { return Math.floor(Date.now() / 1000) } helper.getFqRequestOpt = function(key,sid_tt='b0390e26648a71801795b3b13c9d7d20'){ const options = { url: `https://api.whbfxb.cn/api/novelsale/reader/get/content/v1/?aid=40013183&key=${key}`, // 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`, headers: { // 'cookie': `sid_tt=${sid_tt}; ` + // 'ssid_ucp_v1=1.0.0-KDU0Nzc2NjZmZjRhNTZkYWM5YTMwN2ZmNzAyODMwNjFjZmQyMzA4OTYKFQjkktCcvMz_ARDGw7W6Bhjv6CA4CBoCbHEiIDhjOTg2NjgwY2Q4NzE2YmEzMmJhMjBjM2MwMmEwOTJk; ' + // 'is_staff_user=false; ' + // 'sid_ucp_v1=1.0.0-KDU0Nzc2NjZmZjRhNTZkYWM5YTMwN2ZmNzAyODMwNjFjZmQyMzA4OTYKFQjkktCcvMz_ARDGw7W6Bhjv6CA4CBoCbHEiIDhjOTg2NjgwY2Q4NzE2YmEzMmJhMjBjM2MwMmEwOTJk; ' + // `sessionid=${sid_tt}; ` + // 'ttwid=1%7CdTM3VEQ9KwlMsV2FV6Gaxv5CBXmd5i44fjDoZV_a8g8%7C1733124580%7Cc0952b31723a2f5d5e4b9f84a1c29cb7cbde5592906de58798b92a695d3fb292; ' + // `sessionid_ss=${sid_tt}; ` + // `sid_guard=${sid_tt}%7C1733124550%7C5184000%7CFri%2C+31-Jan-2025+07%3A29%3A10+GMT; ` + // 'uid_tt=3361cf574bbdd82820371b4667fc9b91; ' + // 'passport_csrf_token=f7e1da8668d26b8509725bcf7fa95628; ' + // 'uid_tt_ss=3361cf574bbdd82820371b4667fc9b91; ' + // 'passport_csrf_token_default=f7e1da8668d26b8509725bcf7fa95628; ' + // 'store-region=cn-gs; ' + // 'store-region-src=uid; ' + // 'n_mh=9-mIeuD4wZnlYrrOvfzG3MuT6aQmCUtmr8FxV8Kl8xY', 'cookie': `sessionid=${sid_tt};`, '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', 'content-type': 'application/json', 'referer': 'https://tmaservice.developer.toutiao.com/?appid=tt1b316d8c8401e42101&version=2.7.0' } }; return options } helper.getMfFqRequestOpt = function(key,sid_tt='85486acb04e1918afbee91a9466a9fdc'){ const options = { // url: `https://sdksaleapi.hubeidehuic.com/open_sdk/reader/directory/list/v1?novelsale_aid=40017686&novelsdk_aid=638505&novelsdk_version_code=230&novelsdk_device_type=V2049A&promotion_code=${key}&novelsdk_device_platform=android&version_code=290400&device_type=V2049A&sale_app_id=40017686&book_id&force_no_user_id=true&novelsdk_app_name=undefined&app_name=aweme&device_brand=vivo&device_platform=android&aid=1128&mini_app_version=2.3.0&iid=2936130190182979&device_id=825067091580075&ac=wifi&channel=douyin-huidu-gw-huidu-2940&version_name=29.4.0&os=android&ssmix=a&language=zh&os_api=31&os_version=12&manifest_version_code=290400&resolution=1080*2193&dpi=480&update_version_code=29400100&_rticket=1735289341306&first_launch_timestamp=1733382087&last_deeplink_update_version_code=0&cpu_support64=true&host_abi=arm64-v8a&is_guest_mode=0&app_type=normal&minor_status=0&appTheme=light&is_preinstall=0&need_personal_recommend=1&is_android_pad=0&is_android_fold=0&ts=1735289340&cdid=623fd2d5-5680-4aa4-95e8-78378a2ac7c5`, url: `https://sdksaleapi.hubeidehuic.com/open_sdk/reader/directory/list/v1?novelsale_aid=40017686&novelsdk_aid=638505&promotion_code=${key}`, headers: { 'Connection': 'keep-alive', 'Cookie': `sessionid=${sid_tt};`, // 'Cookie': `sid_tt=${sid_tt};ssid_ucp_v1=1.0.0-KDVhZTIzM2QyOGFlMGE1MWRhMGIzNmI1MzFmZjlmMTVlODhjYmJiOTkKFQjr19Cz7syDAxC9zrm7Bhip_CY4CBoCbGYiIDg1NDg2YWNiMDRlMTkxOGFmYmVlOTFhOTQ2NmE5ZmRj;is_staff_user=false;sid_ucp_v1=1.0.0-KDVhZTIzM2QyOGFlMGE1MWRhMGIzNmI1MzFmZjlmMTVlODhjYmJiOTkKFQjr19Cz7syDAxC9zrm7Bhip_CY4CBoCbGYiIDg1NDg2YWNiMDRlMTkxOGFmYmVlOTFhOTQ2NmE5ZmRj;sessionid=${sid_tt};sessionid_ss=${sid_tt};uid_tt=88e8f06afa51aef4fac18f5d77607921;sid_guard=${sid_tt}%7C1735288637%7C5184000%7CTue%2C+25-Feb-2025+08%3A37%3A17+GMT;passport_csrf_token=08c2b9062233b43ce0e2dd24171203b1;odin_tt=9783bbcf8f09663820810574a01f08b307b6e74c90aa3d8a3b4a80faa9a7d4b197d7e807043c111dcbabde8551e1111145cc87c65fce067f3c532bd796b3681a;uid_tt_ss=88e8f06afa51aef4fac18f5d77607921;passport_csrf_token_default=08c2b9062233b43ce0e2dd24171203b1;store-region=cn-gd;store-region-src=uid;n_mh=9-mIeuD4wZnlYrrOvfzG3MuT6aQmCUtmr8FxV8Kl8xY`, 'User-Agent': 'Mozilla/5.0 (Linux; Android 12; V2049A Build/SP1A.210812.003; 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', 'bdp-sec-uid': 'MS4wLjABAAAA1M_29BGiEjsgNHMP-bmblk-9qGkx5vN7Y3HgltXiFghmy1mfPlH3eonnTWpXANjE', 'bdp-did': '825067091580075', 'bdp-channel': 'douyin-huidu-gw-huidu-2940', 'bdp-device-timezone-offset': '28800', 'bdp-os-version': '12', 'bdp-os-name': 'Android', 'bdp-uid': '1788263942327546', 'bdp-version-code': '290400', 'bdp-device-platform': 'Android', 'bdp-aid': '1128', 'bdp-device-manufacturer': 'vivo', 'bdp-app-id': 'tt26e45059b9d1239401', 'bdp-device-model': 'V2049A', 'Content-Type': 'application/json', 'activity_now_client': '0', 'x-bd-kmsv': '1', 'x-tt-request-tag': 's=-1;p=1', 'x-tt-trace-id': '00-074e072b0d2ee64e6e8d4abbf55d0468-074e072b0d2ee64e-01', 'X-Argus': '/WluZw==', 'X-Gorgon': '8404f06a00014b0d761b864b2932a41f362b9276f3488e7700de', 'X-Helios': 'AkKvNQvLH57rNKMEdZDNQPlc89Q1Zrs974uhRy+IDcTR/y8Z', 'X-Khronos': '1735289341', 'X-Ladon': 'Z25p/Q==', 'X-Medusa': '/mluZwqBMZ0qvrJcK0OmF6qmD3/QqgABDTUpyT1IA9+MOFfCQlYXoNnb+C/rkhLwH0AzGIam8DGpfzu3MFDs3fD3Jx12yo1AKxmV8ar5cQEJ+yz2srKfjMWVZhnlSHsA8UOdYdeclol6hoyOKCcvD9k+84icb0lW39sUS4b+3c/ZdQ2g/VnINn0Bpn+V99xK211ut9hvj+pP6zAVmrqx+c86fuUyOPwBgmLtJdoya9Z5ChWFBUpxgDZiurbYqGWHG1vDjIJSnKdKLRBhRNCmsvXf7lPifdINI8I3UOlRrZJW4m/sbQ46JlozT8Dza62mFCUJaMXSVXxrPdVxq9C06cZiwamtMQw6cnQZxcI0B2R2i65Ct45ohgrJE2g4z+nZRVrpBO9ApgIXQ/sp6jPKfbkSXHFzykdk75Ktwz67Vji7puQOOUUlyeMkaBGvEaq9EZnwOyJ3VxggUaqaH5CtKfYD7lnAM4TbjtLE3SMfZZNviajoNh7ouLeyNLUab/Qd9+MBMbWsTN49BwVlOUHKn9hp56XqRNWIWyrA55jDqL6iicuXtb8Xx//kvkccyUByLEde72oGf2/IRBIAgCvfVXH/hP6wJy1f6XjL0Or6I1qX98gWSGvN9HYObpKNGm/64vtoSpHDC1JWtcuf5ZzlsA4R8XbZKS/Tn0Y9kC89pmPnHYLscWtOFoirIRJ0DXqF/PM3kB4jilEe5Y26cvQK2LaSYcuKX7iL4rWLlVrWIh7EkoFH9Un4f8hruVSbhJMWbN+dXZyNn5BHq/+rASd0pCts3fz05xX4dYlfawDeX74MhFxnKcPZu9pS2hjqDZAPmuqKo/DWWDcsO2wT0R0Fd5k/rwtXFOxQgH07cDVkeIDpLjlT3jBdxQ6X9eDfC/EP+nQv+9WgwKkeG5FXDiIbd1++iS6Mb9V4OHmUowZDopiRFqCD5GeBQTkwEe8w05QJ4Dhdu5a5l1y3CzpS/hGgwr9Sos0QrrVXJI7x6Ah1+U05c4TNHXRe8lBUPJNWUHWBXgKznDKBh1+O+Y+jqKxTyIldTfkfXP/5H13/3Hk=' } }; return options } helper.getHyCreateLinkOpt = function(token){ const options = { url: `https://ms.zhangwenpindu.cn/manage/distribution/createLink`, headers: { 'Accept': 'application/json, text/plain, */*', 'Accept-Language': 'zh-CN,zh;q=0.9', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', 'Content-Type': 'application/x-www-form-urlencoded', 'Origin': 'https://manage.zhangwenpindu.cn', 'Pragma': 'no-cache', 'Referer': 'https://manage.zhangwenpindu.cn/', 'Sec-Fetch-Dest': 'empty', 'Sec-Fetch-Mode': 'cors', 'Sec-Fetch-Site': 'same-site', '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', 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', 'sec-ch-ua-mobile': '?0', 'sec-ch-ua-platform': '"Windows"' } }; options.headers['Authorization'] = `Bearer ${token}` return options } helper.getTimeStampByHourMinute = function(timeStr,otherStr=null) { // 解析时间字符串 const [hours, minutes] = timeStr.split(':').map(Number); let date; if (otherStr) { // 如果提供了日期字符串,解析完整的日期时间 date = new Date(otherStr); // 设置时分秒 date.setHours(hours, minutes, 0, 0); } else { // 如果没有提供日期,使用今天的日期 date = new Date(); date.setHours(hours, minutes, 0, 0); } return date; } helper.getLocalDate = function() { const d = new Date(); const year = d.getFullYear(); const month = String(d.getMonth() + 1).padStart(2, '0'); const day = String(d.getDate()).padStart(2, '0'); return `${year}-${month}-${day}`; } helper.getPaginationParams = function(total, pageSize = 500) { const pages = Math.ceil(total / pageSize); const result = []; for(let page = 0; page < pages; page++) { const offset = page * pageSize; const limit = Math.min(pageSize, total - offset); result.push({ page, offset, limit }); } return result; } helper.parseJsonP = function(jsonpStr){ // 去除JSONP包装,提取纯JSON字符串 const jsonStr = jsonpStr.replace(/^_aq_88765\((.*)\)$/, '$1'); // 解析JSON try { const data = JSON.parse(jsonStr); // 现在可以访问解析后的数据 console.log('Status:', data.state); console.log('Session:', data.sess); // 访问验证码配置 const captchaConfig = data.data.comm_captcha_cfg; console.log('TDC Path:', captchaConfig.tdc_path); // 访问显示信息 const showInfo = data.data.dyn_show_info; console.log('Language:', showInfo.lang); console.log('Instruction:', showInfo.instruction); // 访问背景元素配置 const bgConfig = showInfo.bg_elem_cfg; const img_url_1 = bgConfig.img_url // const img_url_1 = showInfo.sprite_url.replace('img_index=0', 'img_index=1'); const img_url_0 = showInfo.sprite_url console.log('Background Size:', bgConfig.size_2d); // 访问前景元素列表 showInfo.fg_elem_list.forEach((elem, index) => { console.log(`Element ${index + 1}:`, { id: elem.id, size: elem.size_2d, position: elem.init_pos }); }); // 访问绑定配置 showInfo.fg_binding_list.forEach((binding, index) => { console.log(`Binding ${index + 1}:`, { master: binding.master, slave: binding.slave, type: binding.bind_type }); }); return {img_url_1:img_url_1.match(/image=([^&]+)/)[1],img_url_0:img_url_0.match(/image=([^&]+)/)[1]} } catch (error) { console.error('Error parsing JSON:', error); return null } } helper.generateSign = function(map, appSecret) { const keyArray = Object.keys(map).sort(); const parts = []; for (const key of keyArray) { if (key === 'sign') { continue; } const value = map[key]; if (!value || value.toString().trim().length <= 0) { continue; } parts.push(`${key}=${value.toString().trim()}`); } parts.push(`key=${appSecret}`); const signStr = parts.join('&'); // Using crypto module for MD5 hash const crypto = require('crypto'); return crypto.createHash('md5') .update(signStr) .digest('hex') .toUpperCase(); } helper.getDate7DaysBefore = function(dateString, inputFormat, outputFormat) { const inputDate = new Date(dateString); if (isNaN(inputDate.getTime())) { // 如果默认解析失败,尝试手动解析 // 这里可以添加更多格式的解析逻辑 throw new Error('Unsupported date format - consider using moment.js for complex formats'); } const date7DaysBefore = new Date(inputDate); date7DaysBefore.setDate(date7DaysBefore.getDate() + 0); // 简单格式化函数 function formatDate(date, format) { if (!format || format.toLowerCase() === 'iso') { return date.toISOString(); } const pad = num => num.toString().padStart(2, '0'); const year = date.getFullYear(); const month = pad(date.getMonth() + 1); const day = pad(date.getDate()); const hours = pad(date.getHours()); const minutes = pad(date.getMinutes()); const seconds = pad(date.getSeconds()); return format .replace('YYYY', year) .replace('MM', month) .replace('DD', day) .replace('HH', hours) .replace('mm', minutes) .replace('ss', seconds); } return formatDate(date7DaysBefore, outputFormat); } module.exports = helper;