wuhu-torn-helper/src/class/utils/CommonUtils.ts
2022-11-03 16:34:58 +08:00

356 lines
14 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import UserScriptEngine from "../../enum/UserScriptEngine";
import WuhuBase from "../WuhuBase";
import Log from "../Log";
import Device from "../../enum/Device";
import LOADING_IMG_HTML from "../../static/html/loading_img.html";
import Timer from "./Timer";
import FetchUtils from "./FetchUtils";
import TornStyleSwitch from "./TornStyleSwitch";
import WuhuConfig from "../WuhuConfig";
import { MenuItemConfig } from "../ZhongIcon";
import TRAVEL_STATE from "../../enum/TravelState";
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<string> {
const engine = this.getScriptEngine();
let start = performance.now();
Log.info('跨域获取数据开始, 脚本引擎: ' + engine);
return new Promise<string>((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<HTMLElement|null>}
*/
public static elementReady(selectors: string, content: Document = document, timeout: number = 30000): Promise<HTMLElement> {
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 });
});
}
/**
* 通过 mutation.observe 方法异步返回元素
* @param selectors
* @param content
* @param timeout
*/
public static querySelector(selectors: string, content: Document = document, timeout: number = 30000): Promise<HTMLElement> {
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
* @param offsetHours 比对时间的偏移小时数
*/
public isNewDay(target: number | Date, offsetHours: number = 0): boolean {
let tar: Date = typeof target === "number" ? new Date(target) : target;
let today = new Date();
let utcNewDay = new Date(Date.UTC(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate()));
utcNewDay.setHours(utcNewDay.getHours() + offsetHours);
return utcNewDay > tar;
}
public jQueryReady(): Promise<null> {
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<void>}
*/
public sleep(ms: number): Promise<null> {
let time = Math.max(ms, 10);
return new Promise(resolve => setTimeout(() => resolve(null), time));
}
public elemGenerator(setting: MenuItemConfig, root_node: Element): HTMLElement {
let { tip, domType } = setting;
let new_node = null;
switch (domType) {
case 'checkbox': {
new_node = document.createElement('div');
let { domId, dictName, domText, changeEv } = setting;
let switcher = new TornStyleSwitch(domText);
let _input = switcher.getInput();
switcher.getBase().id = domId;
(tip) && (switcher.getBase().setAttribute('title', tip));
_input.checked = WuhuConfig.get(dictName);
_input.onchange = e => {
WuhuConfig.set(dictName, _input.checked, true);
if (changeEv) changeEv(e);
};
new_node.appendChild(switcher.getBase());
break;
}
case 'button': {
new_node = document.createElement('div');
let { domId, domText, clickFunc } = setting;
let btn = document.createElement('button');
(tip) && (btn.setAttribute('title', tip));
btn.id = domId;
btn.innerHTML = domText;
btn.addEventListener('click', clickFunc);
new_node.appendChild(btn);
break;
}
case 'select': {
new_node = document.createElement('div');
let { domSelectOpt, dictName, domId, domText } = setting;
let label = document.createElement('label');
(tip) && (label.setAttribute('title', tip));
let text = document.createTextNode(domText);
let select = document.createElement('select');
select.id = domId;
domSelectOpt.forEach((opt, i) => {
let { domVal, domText } = opt;
let option = document.createElement('option');
option.value = domVal;
option.innerHTML = domText;
option.selected = i === WuhuConfig.get(dictName);
option.innerHTML = domText;
select.appendChild(option);
});
select.onchange = e => WuhuConfig.set(dictName, (<HTMLSelectElement>e.target).selectedIndex);
label.appendChild(text);
label.appendChild(select);
new_node.appendChild(label);
break;
}
case 'plain': {
let tag = setting.tagName || 'div';
new_node = document.createElement(tag);
if (setting.domId) new_node.id = setting.domId;
new_node.innerHTML += setting['domHTML'];
break;
}
}
// 移动节点
return root_node.appendChild(new_node);
}
public exportTextFile(filename, content) {
const tmp = document.createElement('a');
tmp.href = URL.createObjectURL(new Blob(content, { type: 'text/plain', endings: 'transparent' }));
tmp.download = filename;
tmp.click();
tmp.remove();
URL.revokeObjectURL(tmp.href);
}
public isValidUid(id: string | number): boolean {
if (typeof id === 'string') {
return /^[0-9]{1,7}$/.test(id);
} else {
return /^[0-9]{1,7}$/.test(id.toString());
}
}
public getTravelStage(): TRAVEL_STATE {
let global = CommonUtils.glob;
if (global.bodyAttrs["data-abroad"] === 'false') {
return TRAVEL_STATE.IN_TORN;
}
// 海外落地abroad
else if (global.bodyAttrs["data-traveling"] === 'false') {
return TRAVEL_STATE.ABROAD;
}
// 飞行中traveling+abroad
else {
return TRAVEL_STATE.FLYING;
}
}
}