import UserScriptEngine from "../../enum/UserScriptEngine"; import WuhuBase from "../WuhuBase"; import Log from "../Log"; import Device from "../../enum/Device"; import AjaxFetchOption from "../../interface/AjaxFetchOption"; import Alert from "./Alert"; import LOADING_IMG_HTML from "../../static/html/loading_img.html"; import Timer from "./Timer"; import FetchUtils from "./FetchUtils"; export default class CommonUtils extends WuhuBase { className = 'CommonUtils'; static getScriptEngine() { let glob = CommonUtils.glob; return glob.GM_xmlhttpRequest ? UserScriptEngine.GM : glob.isPDA ? UserScriptEngine.PDA : UserScriptEngine.RAW; } static COFetch(url: URL | string, method: 'get' | 'post' = 'get', body: any = null): Promise { const engine = this.getScriptEngine(); let start = performance.now(); Log.info('跨域获取数据开始, 脚本引擎: ' + engine); return new Promise((resolve, reject) => { switch (engine) { case UserScriptEngine.RAW: { Log.error(`跨域请求错误:${ UserScriptEngine.RAW }环境下无法进行跨域请求`); reject(`错误:${ UserScriptEngine.RAW }环境下无法进行跨域请求`); break; } case UserScriptEngine.PDA: { const { PDA_httpGet, PDA_httpPost } = window; // get if (method === 'get') { if (typeof PDA_httpGet !== 'function') { Log.error('COFetch网络错误:PDA版本不支持'); reject('COFetch网络错误:PDA版本不支持'); } PDA_httpGet(url) .then(res => { Log.info('跨域获取数据成功, 耗时' + (performance.now() - start | 0) + 'ms'); resolve(res.responseText); }) .catch(e => { Log.error('COFetch网络错误', e); reject(`COFetch网络错误 ${ e }`); }) } // post else { if (typeof PDA_httpPost !== 'function') { Log.error('COFetch网络错误:PDA版本不支持'); reject('COFetch网络错误:PDA版本不支持'); } PDA_httpPost(url, { 'content-type': 'application/json' }, body) .then(res => resolve(res.responseText)) .catch(e => { Log.error('COFetch网络错误', e); reject(`COFetch网络错误 ${ e }`); }); } break; } case UserScriptEngine.GM: { let { GM_xmlhttpRequest } = CommonUtils.glob; if (typeof GM_xmlhttpRequest !== 'function') { Log.error('COFetch网络错误:用户脚本扩展API错误'); reject('错误:用户脚本扩展API错误'); } GM_xmlhttpRequest({ method: method, url: url, data: method === 'get' ? null : body, headers: method === 'get' ? null : { 'content-type': 'application/json' }, onload: res => { Log.info('跨域获取数据成功,耗时' + (performance.now() - start | 0) + 'ms'); resolve(res.response); }, onerror: res => reject(`连接错误 ${ JSON.stringify(res) }`), ontimeout: res => reject(`连接超时 ${ JSON.stringify(res) }`), }); } } }); } /** * 返回玩家信息的对象 { playername: string, userID: number } * @return {PlayerInfo} rs */ static getPlayerInfo(): PlayerInfo { const node = document.querySelector('script[uid]'); if (node) { return { playername: node.getAttribute('name'), userID: node.getAttribute('uid') as unknown as number, } } else { new Alert('严重错误:芜湖助手无法获取用户数据,已退出'); throw '芜湖助手无法获取用户数据'; } } // 用户设备类型 对应PC MOBILE TABLET public static getDeviceType(): Device { return window.innerWidth >= 1000 ? Device.PC : window.innerWidth <= 600 ? Device.MOBILE : Device.TABLET; } public static getYaoCD(): string { if (document.querySelector("#icon49-sidebar")) { // 0-10min return '<10分' } else if (document.querySelector("#icon50-sidebar")) { // 10min-1h return '<1时' } else if (document.querySelector("#icon51-sidebar")) { // 1h-2h return '1~2时' } else if (document.querySelector("#icon52-sidebar")) { // 2h-5h return '2~5时' } else if (document.querySelector("#icon53-sidebar")) { // 5h+ return '>5时' } else { return '无效' } } public static ajaxFetch(opt: AjaxFetchOption) { let { url, referrer = '/', method, body = null } = opt; let req_params: RequestInit = { headers: { 'X-Requested-With': 'XMLHttpRequest' }, referrer, method, }; if (method === 'POST') { req_params.headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'; req_params.body = body; } return window.fetch(url, req_params); } /** * 通过 mutation.observe 方法异步返回元素 * @param {String} selectors - CSS规则的HTML元素选择器 * @param {Document} content - 上下文 * @param {number} timeout - 超时毫秒数 * @returns {Promise} */ public static elementReady(selectors: string, content: Document = document, timeout: number = 30000): Promise { Log.info('等待元素:' + selectors); let timer = new Timer(); return new Promise((resolve, reject) => { let el = content.querySelector(selectors) as HTMLElement; if (el) { Log.info('已获取元素, 耗时' + timer.getTimeMs(), el); resolve(el); return; } let observer = new MutationObserver((_, observer) => { content.querySelectorAll(selectors).forEach((element) => { Log.info({ innerHTML: element.innerHTML, element }); observer.disconnect(); Log.info('已获取元素, 耗时' + timer.getTimeMs(), element); resolve(element as HTMLElement); }); }); setTimeout(() => { observer.disconnect(); Log.error(`等待元素超时! [${ selectors }]\n${ content.documentElement.tagName }, 耗时` + timer.getTimeMs()); reject(`等待元素超时! [${ selectors }]\n${ content.documentElement.tagName }, 耗时` + timer.getTimeMs()); }, timeout); observer.observe(content.documentElement, { childList: true, subtree: true }); }); } public static querySelector(selectors: string, content: Document = document, timeout: number = 30000): Promise { return CommonUtils.elementReady(selectors, content, timeout); } public static addStyle(rules: string): void { let element = document.querySelector('style#wh-trans-gStyle'); if (element) { element.innerHTML += rules; } else { element = document.createElement("style"); element.id = 'wh-trans-gStyle'; element.innerHTML = rules; document.head.appendChild(element); } Log.info('CSS规则已添加', element); } public static loading_gif_html(): string { return LOADING_IMG_HTML; } /** * 播放音频 * @param {string} url 播放的音频URL * @returns {undefined} */ public audioPlay(url: string = 'https://www.torn.com/js/chat/sounds/Warble_1.mp3') { const audio = new Audio(url); audio.addEventListener("canplaythrough", () => { audio.play() .catch(err => Log.error(err)) .then(); }); } /** * 与给定日期对比,现在是否是新的一天 * @param target */ public isNewDay(target: number | Date): boolean { let tar: Date = typeof target === "number" ? new Date(target) : target; let nowUtc: Date = new Date(); nowUtc.setHours(8); nowUtc.setMinutes(0); nowUtc.setSeconds(0); nowUtc.setMilliseconds(0); return nowUtc > tar; } public jQueryReady(): Promise { Log.info('等待jQuery加载中...'); FetchUtils.getInstance().fetchText('/js/script/lib/jquery-1.8.2.js?v=f9128651g') .then(res => window.eval(res)); let intervalId = window.setInterval(() => { Log.info('仍在等待jQuery加载中...'); }, 1000); return new Promise(async resolve => { while (true) { if (typeof window.$ === 'function') break; await this.sleep(100); } window.clearInterval(intervalId); Log.info('jQuery已加载'); resolve(null); }); } /** * 等待毫秒数 * @param {Number} ms 毫秒 * @returns {Promise} */ public sleep(ms: number): Promise { let time = Math.max(ms, 10); return new Promise(resolve => setTimeout(() => resolve(null), time)); } }