const {jsdomFromText, browser} = require("sdenv"); const JsUtil = require("../../utils/JsUtil"); const {Script} = require("node:vm"); const Store = require("../../utils/Store"); let TimeUtil = require("../../utils/TimeUtil"); class Rs6Service { store = new Store(); // service handle async handle(url, uuid, htmlStr, cookie, userAgent) { // 获取 origin var var1 = new URL(url) let baseUrl = var1.origin; // 初始化 jsDom 和 cookieJar const [jsDom, cookieJar] = jsdomFromText({ url: url, referrer: url, userAgent: userAgent, contentType: "text/html", runScripts: "outside-only", // runScripts: 'dangerously'/'outside-only' }) // 加载dom let dom = await jsDom(htmlStr); console.log(`${uuid};html 加载长度--->`, dom.serialize().length) const window = dom.window // ------------------------------------------------ param ---------------------------------------------------------- // 标志判断cookie是否生成 window[uuid] = false // ------------------------------------------------ function ------------------------------------------------------- // js执行成功后会跳转页面 会触发onbeforeunload钩子 window.onbeforeunload = async (url) => { console.debug(`${url} 页面回调完成`); window[uuid] = true } // 设置 cookie if (cookie != null) { let cookieList = this.CookieStr2List(cookie); console.log(`${uuid};cookie 加载长度--->`, cookieList.length, baseUrl) for (let i = 0; i < cookieList.length; i++) { cookieJar.setCookieSync(cookieList[i], baseUrl); } } // 方案1 通过监听cookie 判断cookie是否生成 const superSetCookie = cookieJar.setCookie; let generateCookieKey = null; // 设置 setCookie 代理 cookieJar.setCookie = function (cookie, currentUrl, options, callback) { console.debug(`${uuid};正在设置 Cookie:`, cookie, currentUrl); let call = superSetCookie.call(this, cookie, currentUrl, options, callback); // 根据瑞树6 cookie 特性 截取 `enable_XXXXXXXXXXXX=true` XXXXXXXX 为即将生成的 cookie key if (generateCookieKey != null && cookie.includes(generateCookieKey)) { window[uuid] = true console.debug('匹配---->', cookie) } let match = cookie.match(/enable_(.*?)=true/); if (match) { generateCookieKey = match[1]; console.debug('设置 匹配---->', generateCookieKey) } return call; }; // ------------------------------------------------ 实例化浏览器 ----------------------------------------------------- browser(window, 'chrome'); // 加载js let js = await JsUtil.loadJs(window.document, var1.hostname, cookie); console.log(`${uuid};js 加载长度--->`, js.length) // 执行 js let script = new Script(js); let internalVMContext = dom.getInternalVMContext(); script.runInContext(internalVMContext, {timeout: 1000}); // 等待 cookie 被设置 for (let i = 0; i < 10; i++) { if (window[uuid]) { break; } await TimeUtil.sleep(100) } // 获取cookie let resCookie = cookieJar.getCookieStringSync(baseUrl); // 关闭 dom.window.close() return resCookie; } // 将 cookie 转为 list CookieStr2List(cookies) { let list = [] for (let cookie of cookies.trim().split("; ")) { list.push(cookie); } return list } /** * 方案1 * 利用rs特性 js加载完成后会刷新页面 * 利用 window.onbeforeunload 判断页面是否执行完毕 * @param window * @param cookieJar * @param uuid * @returns {Promise<*|null>} */ scheme1_before(window, cookieJar, uuid) { window.onbeforeunload = async (url) => { let baseUrl = new URL(url).origin; const cookies = cookieJar.getCookieStringSync(baseUrl); console.debug(`${url} 页面回调生成cookie:`, cookies); this.store.set(uuid, cookies) // window.close(); } } /** * 方案1 * @param window * @param cookieJar * @param uuid * @returns {Promise<*|null>} */ async scheme1_after(window, cookieJar, uuid) { // 等待 onbeforeunload 钩子触发后的回掉 let val = await this.store.waitGetAndDelete(uuid, 100, 10) if (val != null) { return val; } return null; } /** * 监听cookie出现指定cookie * @param window * @param cookieJar * @param uuid * @returns {Promise} */ scheme2_before(window, cookieJar, uuid) { // window[uuid] = false; const superSetCookie = cookieJar.setCookie; // 设置 setCookie 代理 cookieJar.setCookie = function (cookie, currentUrl, options, callback) { console.log(`${uuid};正在设置 Cookie:`, cookie, currentUrl); return superSetCookie.call(this, cookie, currentUrl, options, callback); // let cookieStringSync = super.getCookieStringSync(); // console.log(cookieStringSync) // if (cookie.includes(key)) { // // 设置标志可取标志 // window[uuid] = true // } // return call; }; } /** * 方案2 * @param window * @param cookieJar * @param key * @param uuid * @returns {Promise} */ async scheme2_after(window, cookieJar, key, uuid) { for (let i = 0; i < 10; i++) { let cookieStringSync = cookieJar.getCookieStringSync(); if (cookieStringSync.includes(key)) { return cookieStringSync; } await TimeUtil.sleep(100) } return null; } /** * 方案3 根据cookie现有的数量判断 * @param window * @param cookieJar * @param baseUrl * @param uuid */ scheme3_before(window, cookieJar, baseUrl, uuid) { const initCookie = cookieJar.getCookieStringSync(baseUrl); window[uuid + 'CookieSize'] = initCookie != null ? initCookie.trim().split("; ").length : 0; } /** * 方案3 * @param window * @param cookieJar * @param baseUrl * @param uuid * @returns {Promise} */ async scheme3_after(window, cookieJar, baseUrl, uuid) { let initCookieLength = window[uuid + 'CookieSize'] for (let i = 0; i < 10; i++) { let cookieStringSync = cookieJar.getCookieStringSync(baseUrl); let cookies = cookieStringSync != null ? cookieStringSync.trim().split("; ").length : 0; if (cookies > initCookieLength) { return cookieStringSync; } await TimeUtil.sleep(100) } return null; } } module.exports = Rs6Service