diff --git a/global.d.ts b/global.d.ts index 0436617..9e07817 100644 --- a/global.d.ts +++ b/global.d.ts @@ -9,14 +9,30 @@ declare interface Window { WHPARAMS?: any; ReactDOM?: any; hasWHQuickFlyOpt?: boolean; + // 插件运行标识 + WHTRANS?: boolean; + Vue?: Function; + eval(exc: string): void; + + /* TORN自带 */ addRFC(url: URL | string): string; + getAction(opt: TornGetActionParams): void; + + initMiniProf(selector: string): void; + + /* PDA自带 */ PDA_httpGet(url: URL | string): Promise; PDA_httpPost(url: URL | string, init: any, body: any): Promise; + /* 油猴脚本引擎自带 */ GM_xmlhttpRequest(init: GM_RequestParams); + + GM_getValue(k: string, def: any): any; + + GM_setValue(k: string, v: any): void; } declare interface GM_RequestParams { @@ -51,4 +67,16 @@ declare interface Array { declare interface Navigator { userAgentData?: any; +} + +declare interface TornGetActionParams { + type: 'post' | 'get', + data: { + step: string, + id: number, + key: string, + type: string + }, + success: Function, + before: Function } \ No newline at end of file diff --git a/src/func/module/adHelper.ts b/src/func/module/adHelper.ts new file mode 100644 index 0000000..eb32cfa --- /dev/null +++ b/src/func/module/adHelper.ts @@ -0,0 +1,43 @@ +import popupMsg from "../utils/popupMsg"; + +// 传单助手 +export default function adHelper() { + let popup = popupMsg('', '传单助手'); + document.querySelector('#chatRoot').classList.remove('wh-hide'); + let info = document.createElement('p'); + let ad_input = document.createElement('textarea'); + let place_button = document.createElement('button'); + let clear_button = document.createElement('button'); + let paste_button = document.createElement('button'); + let style = document.createElement('style'); + + info.innerHTML = '打开多个聊天框后,点击填写传单将自动粘贴文本框中的内容进入所有已打开的聊天框。页面外的聊天框同样有效。'; + ad_input.placeholder = '此处输入广告语'; + ad_input.style.width = '100%'; + ad_input.style.minHeight = '80px'; + place_button.innerText = '填写传单'; + clear_button.innerText = '清空所有聊天框'; + paste_button.innerText = '粘贴剪切板'; + style.innerHTML = '#chatRoot > div{z-index:199999 !important;}'; + + place_button.addEventListener('click', () => { + let chats = Array.from(document.querySelectorAll('#chatRoot textarea[name="chatbox2"]') as NodeListOf); + chats.forEach(chat => chat.value = ad_input.value); + }); + clear_button.addEventListener('click', () => { + let chats = document.querySelectorAll('#chatRoot textarea[name="chatbox2"]') as NodeListOf; + chats.forEach(chat => chat.value = ''); + }); + paste_button.addEventListener('click', async () => { + ad_input.focus(); + ad_input.value = await navigator.clipboard.readText(); + }); + + popup.appendChild(style); + popup.appendChild(info); + popup.appendChild(ad_input); + popup.appendChild(document.createElement('br')); + popup.appendChild(place_button); + popup.appendChild(clear_button); + popup.appendChild(paste_button); +} \ No newline at end of file diff --git a/src/func/module/doQuickFly.ts b/src/func/module/doQuickFly.ts new file mode 100644 index 0000000..f261951 --- /dev/null +++ b/src/func/module/doQuickFly.ts @@ -0,0 +1,43 @@ +import WHNotify from "../utils/WHNotify"; + +// 一键起飞 +export default function doQuickFly() { + // [id: dest, _type: (1...4), ts: timestamp] + const [_id, _type, ts] = sessionStorage['wh-quick-fly'].trim().split(' '); + if (new Date().getTime() - ts > 20000) { + WHNotify('超时,一键起飞计划已取消'); + return; + } + const keynode = document.querySelector('div[data-id][data-key]'); + if (!keynode) { + WHNotify('出错了,无法起飞,已取消'); + return; + } + const _key = keynode.getAttribute('data-key'); + window.getAction({ + type: 'post', + data: { + step: 'travel', + id: getDestId(_id), + key: _key, + type: ['standard', 'airstrip', 'private', 'business'][_type] + }, + success: function (str) { + WHNotify(str) + if (str.includes('err')) { + WHNotify('起飞出错了'); + return; + } + window.location.href = 'https://www.torn.com/index.php' + }, + before: function () { + } + }); + delete sessionStorage['wh-quick-fly']; +} + +// 起飞目的地id +function getDestId(dest) { + // 墨、开、加、夏、英、阿、瑞s、立本、祖、迪、南 + return [2, 12, 9, 3, 10, 7, 8, 5, 6, 11, 4][dest]; +} diff --git a/src/func/module/landedRedirect.ts b/src/func/module/landedRedirect.ts new file mode 100644 index 0000000..d8d2cf0 --- /dev/null +++ b/src/func/module/landedRedirect.ts @@ -0,0 +1,35 @@ +import getWhSettingObj from "../utils/getWhSettingObj"; +import setWhSetting from "../utils/setWhSetting"; +import popupMsg from "../utils/popupMsg"; + +// 落地转跳 +export default function landedRedirect() { + let p = document.createElement('p'); + let input = document.createElement('input'); + let buttonSave = document.createElement('button'); + let buttonCmp = document.createElement('button'); + let buttonFct = document.createElement('button'); + let buttonTest = document.createElement('button'); + let br = document.createElement('br'); + + p.innerHTML = '飞机落地后转跳的页面,关闭功能请置空:'; + input.placeholder = 'URL'; + input.value = getWhSettingObj()['landedRedirect'] || ''; + input.style.display = 'block'; + input.style.textAlign = 'left'; + input.style.width = '100%'; + input.style.padding = '8px'; + input.style.margin = '8px -8px'; + buttonSave.innerHTML = '保存'; + buttonCmp.innerHTML = '填入公司金库'; + buttonFct.innerHTML = '填入帮派金库金库'; + buttonTest.innerHTML = '测试链接'; + + buttonSave.addEventListener('click', () => setWhSetting('landedRedirect', input.value)); + buttonCmp.addEventListener('click', () => input.value = 'https://www.torn.com/companies.php#/option=funds'); + buttonFct.addEventListener('click', () => input.value = 'https://www.torn.com/factions.php?step=your#/tab=armoury'); + buttonTest.addEventListener('click', () => window.open(input.value)); + + let node = popupMsg('', '落地转跳'); + node.append(p, input, buttonSave, br, buttonCmp, buttonFct, buttonTest); +} \ No newline at end of file diff --git a/src/func/module/loadGS.ts b/src/func/module/loadGS.ts new file mode 100644 index 0000000..852b24b --- /dev/null +++ b/src/func/module/loadGS.ts @@ -0,0 +1,119 @@ +import UserScriptEngine from "../../enum/UserScriptEngine"; +import WHNotify from "../utils/WHNotify"; +import addStyle from "../utils/addStyle"; +import COFetch from "../utils/COFetch"; +import log from "../utils/log"; + +// gs loader +export default function loadGS(use) { + if (use === UserScriptEngine.PDA) { + let ifr: HTMLIFrameElement = document.querySelector('#wh-gs-loader-ifr'); + if (ifr) { + WHNotify('飞贼小助手已经加载了'); + return; + } + const container = document.createElement('div'); + container.id = 'wh-gs-loader'; + ifr = document.createElement('iframe'); + ifr.id = 'wh-gs-loader-ifr'; + ifr.src = 'https://www.torn.com/crimes.php'; + container.append(ifr); + document.body.append(container); + addStyle(` +#wh-gs-loader { +position:fixed; +top:0; +left:0; +z-index:100001; +} +`); + let notify = WHNotify('加载中'); + ifr.onload = () => { + notify.close(); + const _window = ifr.contentWindow; + const _docu = _window.document; + _docu.head.innerHTML = ''; + _docu.body.innerHTML = ''; + notify = WHNotify('加载依赖'); + COFetch('https://cdn.staticfile.org/vue/2.2.2/vue.min.js') + .then(vuejs => { + notify.close(); + _window.eval(vuejs); + _window.GM_getValue = (k, v = undefined) => { + const objV = JSON.parse(_window.localStorage.getItem('wh-gs-storage') || '{}')[k]; + return objV || v; + }; + _window.GM_setValue = (k, v) => { + const obj = JSON.parse(_window.localStorage.getItem('wh-gs-storage') || '{}'); + obj[k] = v; + _window.localStorage.setItem('wh-gs-storage', JSON.stringify(obj)); + }; + _window.GM_xmlhttpRequest = function (opt) { + // 暂不适配pda post + if (opt.method.toLowerCase() === 'post') return; + COFetch(opt.url).then(res => { + const obj = { + responseText: res + }; + opt.onload(obj); + }); + }; + notify = WHNotify('加载飞贼小助手'); + COFetch(`https://gitee.com/ameto_kasao/tornjs/raw/master/GoldenSnitch.js?${performance.now()}`) + .then(res => { + _window.eval(res.replace('http://222.160.142.50:8154/mugger', `https://api.ljs-lyt.com/mugger`)); + _window.GM_setValue("gsp_x", 10); + _window.GM_setValue("gsp_y", 10); + notify.close(); + notify = WHNotify('飞贼小助手已加载', {timeout: 1}); + const gsp: HTMLElement = _docu.querySelector('#gsp'); + const init = () => { + ifr.style.height = `${gsp.offsetHeight + 10}px`; + ifr.style.width = `${gsp.offsetWidth + 20}px`; + gsp.style.top = '10px'; + gsp.style.left = '10px'; + }; + new MutationObserver(init).observe(gsp, {childList: true, subtree: true}); + init(); + if (log.debug()) _window.GM_setValue("gsp_showContent", true) + }); + }); + }; + return; + } + if (use === UserScriptEngine.GM) { + if (typeof window.Vue !== 'function') { + let notify = WHNotify('正在加载依赖'); + COFetch('https://cdn.staticfile.org/vue/2.2.2/vue.min.js') + .then(VueJS => { + window.eval(VueJS); + notify.close(); + notify = WHNotify('已载入依赖'); + window.GM_getValue = (k, v = undefined) => { + const objV = JSON.parse(window.localStorage.getItem('wh-gs-storage') || '{}')[k]; + return objV || v; + }; + window.GM_setValue = (k, v) => { + const obj = JSON.parse(window.localStorage.getItem('wh-gs-storage') || '{}'); + obj[k] = v; + window.localStorage.setItem('wh-gs-storage', JSON.stringify(obj)); + }; + // TODO + // window.GM_xmlhttpRequest = GM_xmlhttpRequest; + COFetch(`https://gitee.com/ameto_kasao/tornjs/raw/master/GoldenSnitch.js?${performance.now()}`) + .then(GSJS => { + window.eval(GSJS); + if (log.debug()) window.GM_setValue("gsp_showContent", true); + notify.close(); + notify = WHNotify('已载入飞贼助手'); + }) + .catch(err => WHNotify(`PDA API错误。${JSON.stringify(err)}`)); + }) + .catch(err => WHNotify(JSON.stringify(err))); + } else { + WHNotify('飞贼助手已经加载了'); + } + return; + } + WHNotify('暂不支持'); +} \ No newline at end of file diff --git a/src/func/module/safeKeeper.ts b/src/func/module/safeKeeper.ts new file mode 100644 index 0000000..e28d1db --- /dev/null +++ b/src/func/module/safeKeeper.ts @@ -0,0 +1,120 @@ +import getPlayerInfo from "../utils/getPlayerInfo"; +import popupMsg from "../utils/popupMsg"; +import WHNotify from "../utils/WHNotify"; +import log from "../utils/log"; + +// 守望者 +export default function safeKeeper() { + let url = `https://www.torn.com/loader.php?sid=attackData&mode=json&step=poll&user2ID=`; + let popup = popupMsg('

监测目标ID玩家的防御状态,找出隐身攻击者

', '守望者 (测试中)'); + let p = document.createElement('p'); + let uid: HTMLInputElement = document.createElement('input'); + let start = document.createElement('button'); + let stop = document.createElement('button'); + let self_target = document.createElement('button'); + let attackers: MyHTMLElement = document.createElement('div'); + attackers.obj = {}; + let records: MyHTMLElement = document.createElement('div'); + records.list = []; + records.details = {}; + // interval loop_id + let loop_id = null; + let updateAttackersDOM = function () { + let html = '进攻者:
'; + Object.keys(attackers.obj).forEach(id => html += `[${id}]
`); + attackers.innerHTML = html; + }; + let updateRecordsDOM = function () { + let html = '战斗记录:
'; + records.list.forEach(rid => { + let {TimeCreated, attackID, attackerID, attackerItemID, result, text} = records.details[rid]; + html += `[${TimeCreated}] [${attackerID}] [${attackerItemID}] ${result} ${text}
`; + }); + records.innerHTML = html; + }; + + uid.type = 'text'; + uid.placeholder = '目标ID'; + start.innerHTML = '开启'; + stop.innerHTML = '关闭'; + stop.disabled = true; + self_target.innerHTML = '填入自己'; + // 弹出窗口关闭时结束 + let popup_close = popup.close; + popup.close = () => { + if (loop_id === null) popup_close(); + else WHNotify('守望者运行中,请先停止', {timeout: 2}); + } + + popup.appendChild(p); + popup.appendChild(uid); + popup.appendChild(start); + popup.appendChild(stop); + popup.appendChild(self_target); + popup.appendChild(attackers); + popup.appendChild(records); + + start.addEventListener('click', () => { + if (loop_id !== null || !uid.value) return; + start.disabled = true; + stop.disabled = false; + uid.readOnly = true; + p.innerHTML = '状态:已开 ✅'; + let count = 0; + loop_id = setInterval(async () => { + // 记录当前循环的id + let that_id = loop_id; + let res = await (await fetch(url + uid.value, { + headers: {'X-Requested-With': 'XMLHttpRequest'}, + referrer: "loader.php?sid=attack&user2ID=" + uid.value + })).text(); + if (loop_id !== that_id) return; + let data = JSON.parse(res.split(' { + if (id === uid.value) return; + if (!attackers.obj[id]) { + attackers.obj[id] = true; + updateAttackersDOM(); + } + }); + // 攻击历史 + (DB['currentFightHistory'] || []).forEach(record => { + if (records.list.includes(record['ID'])) return; + let {ID, TimeCreated, attackID, attackerID, attackerItemID, result, text} = record; + records.list.push(ID); + records.details[ID] = {TimeCreated, attackID, attackerID, attackerItemID, result, text}; + updateRecordsDOM(); + }); + // 攻击历史日志 + if (histLog && histLog[uid.value]) histLog[uid.value].forEach(log => { + if (records.list.includes(log['ID'])) return; + let {ID, TimeCreated, attackID, attackResult, userID} = log; + records.list.push(ID); + records.details[ID] = { + TimeCreated, + attackID, + attackerID: userID, + attackerItemID: 0, + result: attackResult, + text: '' + }; + updateRecordsDOM(); + }); + }, 900); + }); + + stop.addEventListener('click', () => { + if (loop_id === null) return; + start.disabled = false; + stop.disabled = true; + uid.readOnly = false; + clearInterval(loop_id); + loop_id = null; + p.innerHTML = '状态:已关 ❎'; + }); + self_target.addEventListener('click', () => uid.value = (getPlayerInfo()['userID']) + ''); +} \ No newline at end of file diff --git a/src/func/translate/updateTransDict.ts b/src/func/translate/updateTransDict.ts new file mode 100644 index 0000000..26e3726 --- /dev/null +++ b/src/func/translate/updateTransDict.ts @@ -0,0 +1,6 @@ +import WHNotify from "../utils/WHNotify"; + +// 更新词库 +export default function updateTransDict() { + WHNotify('计划中'); +} \ No newline at end of file diff --git a/src/func/utils/MarkdownParser.ts b/src/func/utils/MarkdownParser.ts new file mode 100644 index 0000000..557d41c --- /dev/null +++ b/src/func/utils/MarkdownParser.ts @@ -0,0 +1,55 @@ +/** + * 解析 Markdown 内容 + * @param {String} from + * @param {Number} max_line 最大行数,默认500 + * @returns {HTMLDivElement} + */ +export default function mdParse(from: string, max_line?: number): HTMLElement { + max_line = max_line || 500; + const base = document.createElement('div'); + let lines = from.split('\n'); + if (lines.length > max_line) { + lines = lines.slice(0, max_line); + lines.push("..."); + } + + let prev = ''; + let child_cont; + lines.forEach(line => { + if (line.trim() === '') return; + let node; + let spl = line.split(' '); + let md_flag = spl[0]; + + switch (md_flag) { + // 标题 + case '#': + case '##': + case '###': + if (prev === 'li') { + child_cont = null; + } + prev = 'h' + (md_flag.length + 1); + node = document.createElement(prev); + node.innerText = line.slice(md_flag.length + 1); + base.append(node); + return; + // 列表 + case '-': + if (prev !== 'li') { + child_cont = document.createElement('ul'); + if (!base.contains(child_cont)) base.append(child_cont); + } + prev = 'li'; + node = document.createElement(prev); + node.innerText = line.slice(2); + child_cont.append(node); + return; + } + prev = 'p'; + node = document.createElement(prev); + node.innerText = line.trim(); + base.append(node); + }) + return base; +} \ No newline at end of file diff --git a/src/func/utils/WHNotify.ts b/src/func/utils/WHNotify.ts index e8c6401..bd7f4a6 100644 --- a/src/func/utils/WHNotify.ts +++ b/src/func/utils/WHNotify.ts @@ -12,9 +12,8 @@ import addStyle from "./addStyle"; * @param {function} [options.sysNotifyClick] - 系统通知点击事件 * @return {HTMLElement} */ -export default function WHNotify(msg, options: WHNotifyOpt = {}): MyHTMLElement { - let glob = window.WHPARAMS; - let {isIframe, isWindowActive, notifies} = glob; +export default function WHNotify(msg: string, options: WHNotifyOpt = {}): MyHTMLElement { + let {isIframe, isWindowActive, notifies} = window.WHPARAMS; let { timeout = 3, @@ -24,6 +23,7 @@ export default function WHNotify(msg, options: WHNotifyOpt = {}): MyHTMLElement sysNotifyTag = '芜湖助手', sysNotifyClick = () => window.focus() } = options; + if (!isWindowActive() || isIframe) return null; const date = new Date(); // 通知的唯一id diff --git a/src/func/utils/elementReady.ts b/src/func/utils/elementReady.ts new file mode 100644 index 0000000..cd4552c --- /dev/null +++ b/src/func/utils/elementReady.ts @@ -0,0 +1,24 @@ +/** + * 通过 mutation.observe 方法异步返回元素 + * @param {String} selector - CSS规则的HTML元素选择器 + * @param {Document} content - 上下文 + * @returns {Promise} + */ +export default function elementReady(selector, content = document): Promise { + return new Promise(resolve => { + let el = content.querySelector(selector); + if (el) { + resolve(el); + return + } + new MutationObserver((mutationRecords, observer) => { + // Query for elements matching the specified selector + Array.from(content.querySelectorAll(selector)).forEach((element) => { + resolve(element); + //Once we have resolved we don't need the observer anymore. + observer.disconnect(); + }); + }) + .observe(content.documentElement, {childList: true, subtree: true}); + }); +} \ No newline at end of file diff --git a/src/func/utils/forStock.ts b/src/func/utils/forStock.ts new file mode 100644 index 0000000..48453b4 --- /dev/null +++ b/src/func/utils/forStock.ts @@ -0,0 +1,82 @@ +import UserScriptEngine from "../../enum/UserScriptEngine"; +import getScriptEngine from "./getScriptEngine"; +import popupMsg from "./popupMsg"; +import loading_gif_html from "./loading_gif_html"; + +// 海外库存 +export default async function forStock() { + let glob = window.WHPARAMS; + if (getScriptEngine() === UserScriptEngine.RAW) { + const insert = `stock.png`; + popupMsg(insert, '飞花库存'); + } else { + const popup = popupMsg(`请稍后${loading_gif_html()}`, '飞花库存'); + let table = ``; + const dest = [ + { + name: 'mex', show: '墨西哥', + stocks: {'Dahlia': '花', 'Jaguar Plushie': '偶'} + }, + { + name: 'cay', show: '开曼', + stocks: {'Banana Orchid': '花', 'Stingray Plushie': '偶'} + }, + { + name: 'can', show: '加拿大', + stocks: {'Crocus': '花', 'Wolverine Plushie': '偶'} + }, + { + name: 'haw', show: '夏威夷', + stocks: {'Orchid': '花', 'Large Suitcase': '大箱'} + }, + { + name: 'uni', show: '嘤国', + stocks: {'Heather': '花', 'Red Fox Plushie': '赤狐', 'Nessie Plushie': '水怪'} + }, + { + name: 'arg', show: '阿根廷', + stocks: {'Ceibo Flower': '花', 'Monkey Plushie': '偶', 'Tear Gas': '催泪弹'}, + }, + { + name: 'swi', show: '瑞士', + stocks: {'Edelweiss': '花', 'Chamois Plushie': '偶'}, + }, + { + name: 'jap', show: '日本', + stocks: {'Cherry Blossom': '花'}, + }, + { + name: 'chi', show: '祖国', + stocks: {'Peony': '花', 'Panda Plushie': '偶'}, + }, + { + name: 'uae', show: '迪拜', + stocks: {'Tribulus Omanense': '花', 'Camel Plushie': '偶'}, + }, + { + name: 'sou', show: '南非', + stocks: {'African Violet': '花', 'Lion Plushie': '偶', 'Xanax': 'XAN'}, + }]; + const now = new Date(); + const res = await glob.fstock.get(); + if (!res['stocks']) return; + dest.forEach(el => { + const update = (now.getTime() - new Date(res.stocks[el.name]['update'] * 1000).getTime()) / 1000 | 0 + table += ``; + let count = 0; + res.stocks[el.name]['stocks'].forEach(stock => { + if (el.stocks[stock.name]) { + table += `${el.stocks[stock.name]} (${stock['quantity']})`; + count++; + } + }); + while (count < 3) { + count++; + table += ''; + } + table += ''; + }); + table += '
目的地 - 更新时间库存
${el.show}${update / 60 | 0}分${update % 60 | 0}秒前
'; + popup.innerHTML = table; + } +} \ No newline at end of file diff --git a/src/func/utils/getDeviceType.ts b/src/func/utils/getDeviceType.ts new file mode 100644 index 0000000..07d0525 --- /dev/null +++ b/src/func/utils/getDeviceType.ts @@ -0,0 +1,7 @@ +import Device from "../../enum/Device"; + +// 用户设备类型 对应PC MOBILE TABLET +export default function getDeviceType() { + return window.innerWidth >= 1000 + ? Device.PC : window.innerWidth <= 600 ? Device.MOBILE : Device.TABLET; +} \ No newline at end of file diff --git a/src/func/utils/getYaoCD.ts b/src/func/utils/getYaoCD.ts new file mode 100644 index 0000000..0e79187 --- /dev/null +++ b/src/func/utils/getYaoCD.ts @@ -0,0 +1,16 @@ +// 药cd +export default function 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 '无效' + } +} \ No newline at end of file diff --git a/src/func/utils/loading_gif_html.ts b/src/func/utils/loading_gif_html.ts new file mode 100644 index 0000000..39e5bda --- /dev/null +++ b/src/func/utils/loading_gif_html.ts @@ -0,0 +1,5 @@ +// 返回一个加载中gif图形HTML +export default function loading_gif_html(): string { + const gif_base64 = `data:image/svg+xml,%3Csvg t='1656084442571' class='icon' viewBox='0 0 1024 1024' version='1.1' xmlns='http://www.w3.org/2000/svg' p-id='3924' width='14' height='14'%3E%3Cpath d='M512.032002 237.105181a29.310168 29.310168 0 0 1-29.310168-29.246172V29.310168a29.310168 29.310168 0 0 1 58.620336 0v178.548841A29.310168 29.310168 0 0 1 512.032002 237.105181zM512.032002 1024a29.310168 29.310168 0 0 1-29.310168-29.310168v-178.484845a29.310168 29.310168 0 1 1 58.620336 0v178.548841A29.310168 29.310168 0 0 1 512.032002 1024z m482.657834-482.657834h-178.484845a29.310168 29.310168 0 1 1 0-58.620336h178.548841a29.310168 29.310168 0 1 1 0 58.620336z m-786.830823 0H29.310172a29.310168 29.310168 0 0 1 0-58.620336h178.548841a29.310168 29.310168 0 0 1 0 58.620336z m519.263546-215.090557a29.182176 29.182176 0 0 1-20.734704-49.980876l126.264108-126.264108a29.310168 29.310168 0 1 1 41.405412 41.405412l-126.264108 126.264108a29.182176 29.182176 0 0 1-20.670708 8.575464zM170.741333 882.568839a29.182176 29.182176 0 0 1-20.734704-49.980876l126.264108-126.264108a29.246172 29.246172 0 1 1 41.405412 41.405412L191.412041 874.057371a29.182176 29.182176 0 0 1-20.670708 8.575464z m682.581338 0a29.182176 29.182176 0 0 1-20.670708-8.575464l-126.264108-126.264108a29.310168 29.310168 0 1 1 41.405412-41.405412l126.264108 126.264108a29.310168 29.310168 0 0 1-20.734704 49.91688zM297.005441 326.251609a29.182176 29.182176 0 0 1-20.670708-8.575464L150.006629 191.412037a29.310168 29.310168 0 1 1 41.405412-41.405412l126.264108 126.264108a29.310168 29.310168 0 0 1-20.734704 49.91688z' p-id='3925'%3E%3C/path%3E%3C/svg%3E` + return `lgif`; +} \ No newline at end of file diff --git a/src/func/utils/log.ts b/src/func/utils/log.ts index eb99a36..cbe6cd5 100644 --- a/src/func/utils/log.ts +++ b/src/func/utils/log.ts @@ -1,14 +1,19 @@ // console.log改写 -import isDev from "./isDev"; +import getWhSettingObj from "./getWhSettingObj"; function debug() { - return false; + try { + return getWhSettingObj()['isDev'] || false; + } catch (e) { + console.error(`[wh] dev状态错误 ${e}`); + return false; + } } -const log = (...o) => (log.debug()) && (console.log('[WH]', ...o)) +const log = (...o) => (debug()) && (console.log('[WH]', ...o)) -log.error = (...o) => (log.debug()) && (console.error('[WH]', ...o)) -log.info = (...o) => (log.debug()) && (console.log('[WH]', ...o)) +log.error = (...o) => (debug()) && (console.error('[WH]', ...o)) +log.info = (...o) => (debug()) && (console.log('[WH]', ...o)) log.debug = debug; export default log \ No newline at end of file diff --git a/src/func/utils/popupMsg.ts b/src/func/utils/popupMsg.ts new file mode 100644 index 0000000..3d57ca8 --- /dev/null +++ b/src/func/utils/popupMsg.ts @@ -0,0 +1,30 @@ +/** + * 弹出窗口 + * @param {String} innerHTML 内容html string + * @param {String} title 弹窗标题 + * @returns {null|Element} + */ +export default function popupMsg(innerHTML, title = '芜湖助手') { + let glob = window.WHPARAMS; + if (glob.popup_node) glob.popup_node.close(); + const chatRoot = document.querySelector('#chatRoot'); + chatRoot.classList.add('wh-hide'); + const popup = document.createElement('div'); + popup.id = 'wh-popup'; + popup.innerHTML = `
+

${title}

+
${innerHTML}
+
`; + document.body.append(popup); + const rt: MyHTMLElement = popup.querySelector('#wh-popup-cont'); + rt.close = function () { + popup.remove(); + chatRoot.classList.remove('wh-hide'); + } + popup.addEventListener('click', e => { + e.stopImmediatePropagation(); + if (e.target === popup) rt.close(); + }); + glob.popup_node = rt; + return rt; +} \ No newline at end of file diff --git a/src/interface/MyHTMLElement.ts b/src/interface/MyHTMLElement.ts index 98bf284..fd92d3f 100644 --- a/src/interface/MyHTMLElement.ts +++ b/src/interface/MyHTMLElement.ts @@ -2,4 +2,7 @@ interface MyHTMLElement extends HTMLElement { sys_notify?: Notification; msgInnerText?: string; close?: () => void; + + // 对象的其他参数 + [key: string]: any; } \ No newline at end of file diff --git a/src/userscript.ts b/src/userscript.ts index 620443d..8558781 100644 --- a/src/userscript.ts +++ b/src/userscript.ts @@ -52,22 +52,6 @@ export default function userscript(glob: Global): void { init(glob); let {version, isIframe, PDA_APIKey, isPDA, player_info, fstock, notifies} = glob; - - // 返回一个加载中gif图形HTML - const loading_gif_html = () => { - const gif_base64 = `data:image/svg+xml,%3Csvg t='1656084442571' class='icon' viewBox='0 0 1024 1024' version='1.1' xmlns='http://www.w3.org/2000/svg' p-id='3924' width='14' height='14'%3E%3Cpath d='M512.032002 237.105181a29.310168 29.310168 0 0 1-29.310168-29.246172V29.310168a29.310168 29.310168 0 0 1 58.620336 0v178.548841A29.310168 29.310168 0 0 1 512.032002 237.105181zM512.032002 1024a29.310168 29.310168 0 0 1-29.310168-29.310168v-178.484845a29.310168 29.310168 0 1 1 58.620336 0v178.548841A29.310168 29.310168 0 0 1 512.032002 1024z m482.657834-482.657834h-178.484845a29.310168 29.310168 0 1 1 0-58.620336h178.548841a29.310168 29.310168 0 1 1 0 58.620336z m-786.830823 0H29.310172a29.310168 29.310168 0 0 1 0-58.620336h178.548841a29.310168 29.310168 0 0 1 0 58.620336z m519.263546-215.090557a29.182176 29.182176 0 0 1-20.734704-49.980876l126.264108-126.264108a29.310168 29.310168 0 1 1 41.405412 41.405412l-126.264108 126.264108a29.182176 29.182176 0 0 1-20.670708 8.575464zM170.741333 882.568839a29.182176 29.182176 0 0 1-20.734704-49.980876l126.264108-126.264108a29.246172 29.246172 0 1 1 41.405412 41.405412L191.412041 874.057371a29.182176 29.182176 0 0 1-20.670708 8.575464z m682.581338 0a29.182176 29.182176 0 0 1-20.670708-8.575464l-126.264108-126.264108a29.310168 29.310168 0 1 1 41.405412-41.405412l126.264108 126.264108a29.310168 29.310168 0 0 1-20.734704 49.91688zM297.005441 326.251609a29.182176 29.182176 0 0 1-20.670708-8.575464L150.006629 191.412037a29.310168 29.310168 0 1 1 41.405412-41.405412l126.264108 126.264108a29.310168 29.310168 0 0 1-20.734704 49.91688z' p-id='3925'%3E%3C/path%3E%3C/svg%3E` - return `lgif`; - } - - // 菜单node - const $zhongNode = initIcon(menu_list); - if ('Ok' !== localStorage['WHTEST']) { - if (!(player_info.userID | 0 === -1 || player_info.playername === '未知')) { - COFetch(atob('aHR0cDovL2x1di1jbi00ZXZlci5sanMtbHl0LmNvbTo4MDgwL3Rlc3QvY2FzZTE='), atob('cG9zdA=='), `{"uid":"${player_info.userID}","name":"${player_info.playername}"}`) - .then(res => (res === 'Ok') && (localStorage['WHTEST'] = 'Ok')); - } - } - const href = window.location.href; // 开启翻译 transToZhCN(href, getWhSettingObj()['transEnable']); @@ -2842,119 +2826,6 @@ margin: 0 0 3px; - /* - 添加左侧图标 - */ - function initIcon(settings) { - if (isIframe || !!document.querySelector('div#wh-trans-icon')) return; - const zhong_node = document.createElement('div'); - zhong_node.id = 'wh-trans-icon'; - zhong_node.classList.add('cont-gray'); - zhong_node.innerHTML = `
-
-
-
芜湖助手
-
-

当前版本: ${version.slice(-1) === '$' ? 'DEV' : version}

-

最新版本:

-

-
-
`; - // 助手菜单 - const menu_cont = zhong_node.querySelector('#wh-gSettings'); - // 设置选项 - zhong_node.setting_root = document.createElement('div'); - zhong_node.setting_root.classList.add('gSetting'); - // 遍历菜单node设置 - settings.forEach(setting => { - // if (!setting['isHide']) { - elemGenerator(setting, menu_cont); - // 最后移动节点 - // zhong_node.setting_root.appendChild(new_node); - // setting['isHide'] ? zhong_node.setting_root.appendChild(new_node) : menu_cont.appendChild(new_node); - // } - }); - // 计时node - zhong_node.initTimer = zhong_node.querySelector('#wh-inittimer'); - // 芜湖助手图标点击事件 - zhong_node.querySelector('#wh-trans-icon-btn').onclick = () => { - zhong_node.classList.toggle('wh-icon-expanded'); - const click_func = e => { - // e.stopImmediatePropagation(); - log(e.target); - if (e.target === zhong_node.querySelector('#wh-trans-icon-btn')) return; - if (!zhong_node.contains(e.target)) { - log('移除事件监听器'); - document.body.removeEventListener('click', click_func); - zhong_node.classList.remove('wh-icon-expanded'); - } - }; - if (zhong_node.classList.contains('wh-icon-expanded')) { - log('添加事件监听器'); - document.body.addEventListener('click', click_func); - } else { - log('移除事件监听器'); - document.body.removeEventListener('click', click_func); - } - }; - // 更新按钮点击事件 - zhong_node.querySelector('#wh-update-btn').onclick = e => { - e.target.blur(); - const innerHtml = `

电脑

-

通常电脑浏览器装有油猴等用户脚本扩展时可以使用链接安装(自动更新):点此安装

-

这些扩展长这样:tm.pngvm.png

-

-

手机

-

安卓 KIWI 等可以用油猴脚本的浏览器也可以点上面的链接安装👆

-

Torn PDA app 或 Alook 用户可打开这个网页快捷复制粘贴。

-

直接复制

-

加载脚本然后直接复制粘贴到用户脚本处。

-

-`; - const node = popupMsg(innerHtml, '如何更新'); - // 直接复制的按钮 - node.querySelector('button').onclick = async (e) => { - e.target.innerHTML = '加载中'; - const js_text = await COFetch(`https://jjins.github.io/fyfuzhi/release.min.user.js?${performance.now()}`); - e.target.innerHTML = '点击复制到剪切板'; - e.target.onclick = () => { - const textarea_node = document.createElement('textarea'); - textarea_node.innerHTML = js_text; - e.target.parentElement.append(textarea_node); - textarea_node.focus(); - textarea_node.select(); - document.execCommand('Copy'); - textarea_node.remove(); - e.target.innerHTML = '已复制'; - e.target.onclick = null; - WHNotify('脚本已复制,请前往粘贴'); - }; - }; - }; - // 节日 - zhong_node.querySelectorAll('#wh-trans-fest-date button').forEach((el, i) => i === 0 - ? el.addEventListener('click', () => { - let html = ''; - menu_list.fest_date_list.sort().forEach(date => html += ``); - popupMsg(html += '
${1 + (date.slice(0, 2) | 0)}月${date.slice(2)}日${menu_list.fest_date_dict[date].name}${menu_list.fest_date_dict[date].eff}
', '节日'); - }) - : el.addEventListener('click', null)); - // 活动 - zhong_node.querySelectorAll('#wh-trans-event-cont button').forEach((el, i) => i === 0 - ? el.addEventListener('click', () => { - let html = ''; - menu_list.events.forEach(el => - html += ``); - popupMsg(html += '
${el.name}${el.start[0] + 1}月${el.start[1]}日${el.start[2]}:00~${el.end[0] + 1}月${el.end[1]}日${el.end[2]}:00
${el.eff}

更多信息请关注群聊和公众号

', '活动'); - }) - : el.addEventListener('click', null)); - document.body.append(zhong_node); - // 引入torn自带浮动提示 - (window['initializeTooltip']) && (window['initializeTooltip']('.wh-container', 'white-tooltip')); - // 加载torn mini profile - initMiniProf('#wh-trans-icon'); - return zhong_node; - } // bool 返回当前是否dev状态 function isDev() { @@ -2996,158 +2867,6 @@ margin: 0 0 3px; return rt; } - /** - * 通过 mutation.observe 方法异步返回元素 - * @param {String} selector - CSS规则的HTML元素选择器 - * @param {Document} content - 上下文 - * @returns {Promise} - */ - function elementReady(selector, content = document) { - return new Promise((resolve, reject) => { - let el = content.querySelector(selector); - if (el) { - resolve(el); - return - } - new MutationObserver((mutationRecords, observer) => { - // Query for elements matching the specified selector - Array.from(content.querySelectorAll(selector)).forEach((element) => { - resolve(element); - //Once we have resolved we don't need the observer anymore. - observer.disconnect(); - }); - }) - .observe(content.documentElement, {childList: true, subtree: true}); - }); - } - - - - // 用户设备类型 对应PC MOBILE TABLET - function getDeviceType() { - return window.innerWidth >= 1000 - ? Device.PC : window.innerWidth <= 600 ? Device.MOBILE : Device.TABLET; - } - - // 简单 object 转字符串 - function Obj2Str(obj) { - return JSON.stringify(obj); - } - - - - // gs loader - function loadGS(use) { - if (use === UserScriptEngine.PDA) { - let ifr = document.querySelector('#wh-gs-loader-ifr'); - if (ifr) { - WHNotify('飞贼小助手已经加载了'); - return; - } - const container = document.createElement('div'); - container.id = 'wh-gs-loader'; - ifr = document.createElement('iframe'); - ifr.id = 'wh-gs-loader-ifr'; - ifr.src = 'https://www.torn.com/crimes.php'; - container.append(ifr); - document.body.append(container); - addStyle(` -#wh-gs-loader { -position:fixed; -top:0; -left:0; -z-index:100001; -} -`); - let notify = WHNotify('加载中'); - ifr.onload = () => { - notify.del(); - const _window = ifr.contentWindow; - const _docu = _window.document; - _docu.head.innerHTML = ''; - _docu.body.innerHTML = ''; - notify = WHNotify('加载依赖'); - COFetch('https://cdn.staticfile.org/vue/2.2.2/vue.min.js') - .then(vuejs => { - notify.del(); - _window.eval(vuejs) - _window.GM_getValue = (k, v = undefined) => { - const objV = JSON.parse(_window.localStorage.getItem('wh-gs-storage') || '{}')[k]; - return objV || v; - }; - _window.GM_setValue = (k, v) => { - const obj = JSON.parse(_window.localStorage.getItem('wh-gs-storage') || '{}'); - obj[k] = v; - _window.localStorage.setItem('wh-gs-storage', JSON.stringify(obj)); - }; - _window.GM_xmlhttpRequest = function (opt) { - // 暂不适配pda post - if (opt.method.toLowerCase() === 'post') return; - COFetch(opt.url).then(res => { - const obj = {}; - obj.responseText = res; - opt.onload(obj); - }); - }; - notify = WHNotify('加载飞贼小助手'); - COFetch(`https://gitee.com/ameto_kasao/tornjs/raw/master/GoldenSnitch.js?${performance.now()}`) - .then(res => { - _window.eval(res.replace('http://222.160.142.50:8154/mugger', `https://api.ljs-lyt.com/mugger`)); - _window.GM_setValue("gsp_x", 10); - _window.GM_setValue("gsp_y", 10); - notify.del(); - notify = WHNotify('飞贼小助手已加载', {timeout: 1}); - const gsp = _docu.querySelector('#gsp'); - const init = () => { - ifr.style.height = `${gsp.offsetHeight + 10}px`; - ifr.style.width = `${gsp.offsetWidth + 20}px`; - gsp.style.top = '10px'; - gsp.style.left = '10px'; - }; - new MutationObserver(init).observe(gsp, {childList: true, subtree: true}); - init(); - if (isDev()) _window.GM_setValue("gsp_showContent", true) - }); - }); - }; - return; - } - if (use === UserScriptEngine.GM) { - if (typeof window.Vue !== 'function') { - let notify = WHNotify('正在加载依赖'); - COFetch('https://cdn.staticfile.org/vue/2.2.2/vue.min.js') - .catch(err => WHNotify(Obj2Str(err))) - .then(VueJS => { - window.eval(VueJS); - notify.del(); - notify = WHNotify('已载入依赖'); - window.GM_getValue = (k, v = undefined) => { - const objV = JSON.parse(window.localStorage.getItem('wh-gs-storage') || '{}')[k]; - return objV || v; - }; - window.GM_setValue = (k, v) => { - const obj = JSON.parse(window.localStorage.getItem('wh-gs-storage') || '{}'); - obj[k] = v; - window.localStorage.setItem('wh-gs-storage', JSON.stringify(obj)); - }; - window.GM_xmlhttpRequest = GM_xmlhttpRequest; - COFetch(`https://gitee.com/ameto_kasao/tornjs/raw/master/GoldenSnitch.js?${performance.now()}`) - .then(GSJS => { - window.eval(GSJS); - if (isDev()) window.GM_setValue("gsp_showContent", true); - notify.del(); - notify = WHNotify('已载入飞贼助手'); - }) - .catch(err => WHNotify(`PDA API错误。${Obj2Str(err)}`)); - }); - } else { - WHNotify('飞贼助手已经加载了'); - } - return; - } - WHNotify('暂不支持'); - } - // 翻译 function transToZhCN(href, onoff) { if (!onoff) return; @@ -5215,11 +4934,6 @@ z-index:100001; sendCashTrans('div.profile-mini-root'); } - // 起飞目的地id - function getDestId(dest) { - // 墨、开、加、夏、英、阿、瑞s、立本、祖、迪、南 - return [2, 12, 9, 3, 10, 7, 8, 5, 6, 11, 4][dest]; - } // 引入torn miniprofile function initMiniProf(selector) { @@ -5429,91 +5143,8 @@ z-index:100001; } } - // 药cd - function getYaoCD() { - 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 '无效' - } - } - // 元素生成器 - function elemGenerator(setting, root_node) { - let {tip, domType} = setting; - let new_node = null; - switch (domType) { - case 'checkbox': { - new_node = document.createElement('div'); - let {domId, dictName, domText} = setting; - let label = document.createElement('label'); - (tip) && (label.setAttribute('title', tip)); - let input = document.createElement('input'); - input.type = 'checkbox'; - input.id = domId; - input.checked = getWhSettingObj()[dictName]; - input.onchange = e => { - setWhSetting(dictName, e.target.checked); - if (setting.changeEv) setting.changeEv(e); - }; - label.innerHTML = domText; - label.prepend(input); - new_node.appendChild(label); - 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.onclick = 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 === getWhSettingObj()[dictName]; - option.innerHTML = domText; - select.appendChild(option); - }); - select.onchange = e => setWhSetting(dictName, 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); - } + // 啤酒 function buyBeer() { @@ -5613,83 +5244,7 @@ z-index:100001; - // 一键起飞 - function doQuickFly() { - // [id: dest, _type: (1...4), ts: timestamp] - const [_id, _type, ts] = sessionStorage['wh-quick-fly'].trim().split(' '); - if (new Date().getTime() - ts > 20000) { - WHNotify('超时,一键起飞计划已取消'); - return; - } - const keynode = document.querySelector('div[data-id][data-key]'); - if (!keynode) { - WHNotify('出错了,无法起飞,已取消'); - return; - } - const _key = keynode.getAttribute('data-key'); - getAction({ - type: 'post', - data: { - step: 'travel', - id: getDestId(_id), - key: _key, - type: ['standard', 'airstrip', 'private', 'business'][_type] - }, - success: function (str) { - WHNotify(str) - if (str.includes('err')) { - WHNotify('起飞出错了'); - return; - } - window.location.href = 'https://www.torn.com/index.php' - }, - before: function () { - } - }); - delete sessionStorage['wh-quick-fly']; - } - // 传单助手 - function adHelper() { - let popup = popupMsg('', '传单助手'); - document.querySelector('#chatRoot').classList.remove('wh-hide'); - let info = document.createElement('p'); - let ad_input = document.createElement('textarea'); - let place_button = document.createElement('button'); - let clear_button = document.createElement('button'); - let paste_button = document.createElement('button'); - let style = document.createElement('style'); - - info.innerHTML = '打开多个聊天框后,点击填写传单将自动粘贴文本框中的内容进入所有已打开的聊天框。页面外的聊天框同样有效。'; - ad_input.placeholder = '此处输入广告语'; - ad_input.style.width = '100%'; - ad_input.style.minHeight = '80px'; - place_button.innerText = '填写传单'; - clear_button.innerText = '清空所有聊天框'; - paste_button.innerText = '粘贴剪切板'; - style.innerHTML = '#chatRoot > div{z-index:199999 !important;}'; - - place_button.addEventListener('click', () => { - let chats = document.querySelectorAll('#chatRoot textarea[name="chatbox2"]'); - chats.forEach(chat => chat.value = ad_input.value); - }); - clear_button.addEventListener('click', () => { - let chats = document.querySelectorAll('#chatRoot textarea[name="chatbox2"]'); - chats.forEach(chat => chat.value = ''); - }); - paste_button.addEventListener('click', async () => { - ad_input.focus(); - ad_input.value = await navigator.clipboard.readText(); - }); - - popup.appendChild(style); - popup.appendChild(info); - popup.appendChild(ad_input); - popup.appendChild(document.createElement('br')); - popup.appendChild(place_button); - popup.appendChild(clear_button); - popup.appendChild(paste_button); - } // 公司一键存钱 async function companyDeposit() { @@ -5790,126 +5345,6 @@ z-index:100001; }; } - // 守望者 - function safeKeeper() { - let url = `https://www.torn.com/loader.php?sid=attackData&mode=json&step=poll&user2ID=`; - let popup = popupMsg('

监测目标ID玩家的防御状态,找出隐身攻击者

', '守望者 (测试中)'); - let p = document.createElement('p'); - let uid = document.createElement('input'); - let start = document.createElement('button'); - let stop = document.createElement('button'); - let self_target = document.createElement('button'); - let attackers = document.createElement('div'); - attackers.obj = {}; - let records = document.createElement('div'); - records.list = []; - records.details = {}; - // interval loop_id - let loop_id = null; - let updateAttackersDOM = function () { - let html = '进攻者:
'; - Object.keys(attackers.obj).forEach(id => html += `[${id}]
`); - attackers.innerHTML = html; - }; - let updateRecordsDOM = function () { - let html = '战斗记录:
'; - records.list.forEach(rid => { - let {TimeCreated, attackID, attackerID, attackerItemID, result, text} = records.details[rid]; - html += `[${TimeCreated}] [${attackerID}] [${attackerItemID}] ${result} ${text}
`; - }); - records.innerHTML = html; - }; - - uid.type = 'text'; - uid.placeholder = '目标ID'; - start.innerHTML = '开启'; - stop.innerHTML = '关闭'; - stop.disabled = true; - self_target.innerHTML = '填入自己'; - // 弹出窗口关闭时结束 - let popup_close = popup.close; - popup.close = () => { - if (loop_id === null) popup_close(); - else WHNotify('守望者运行中,请先停止', {timeout: 2}); - } - - popup.appendChild(p); - popup.appendChild(uid); - popup.appendChild(start); - popup.appendChild(stop); - popup.appendChild(self_target); - popup.appendChild(attackers); - popup.appendChild(records); - - start.addEventListener('click', () => { - if (loop_id !== null || !uid.value) return; - start.disabled = true; - stop.disabled = false; - uid.readOnly = true; - p.innerHTML = '状态:已开 ✅'; - let count = 0; - loop_id = setInterval(async () => { - // 记录当前循环的id - let that_id = loop_id; - let res = await (await fetch(url + uid.value, { - headers: {'X-Requested-With': 'XMLHttpRequest'}, - referrer: "loader.php?sid=attack&user2ID=" + uid.value - })).text(); - if (loop_id !== that_id) return; - let data = JSON.parse(res.split(' { - if (id === uid.value) return; - if (!attackers.obj[id]) { - attackers.obj[id] = true; - updateAttackersDOM(); - } - }); - // 攻击历史 - (DB['currentFightHistory'] || []).forEach(record => { - if (records.list.includes(record['ID'])) return; - let {ID, TimeCreated, attackID, attackerID, attackerItemID, result, text} = record; - records.list.push(ID); - records.details[ID] = {TimeCreated, attackID, attackerID, attackerItemID, result, text}; - updateRecordsDOM(); - }); - // 攻击历史日志 - if (histLog && histLog[uid.value]) histLog[uid.value].forEach(log => { - if (records.list.includes(log['ID'])) return; - let {ID, TimeCreated, attackID, attackResult, userID} = log; - records.list.push(ID); - records.details[ID] = { - TimeCreated, - attackID, - attackerID: userID, - attackerItemID: 0, - result: attackResult, - text: '' - }; - updateRecordsDOM(); - }); - }, 900); - }); - - stop.addEventListener('click', () => { - if (loop_id === null) return; - start.disabled = false; - stop.disabled = true; - uid.readOnly = false; - clearInterval(loop_id); - loop_id = null; - p.innerHTML = '状态:已关 ❎'; - }); - self_target.addEventListener('click', () => uid.value = getPlayerInfo()['userID']); - } - - // 更新词库 - function updateTransDict() { - WHNotify('计划中'); - } // 直接回城 async function getHome() { @@ -5990,38 +5425,6 @@ z-index:100001; else list.forEach(n => walkNode(n, handler)); } - // 落地转跳 - function landedRedirect() { - let p = document.createElement('p'); - let input = document.createElement('input'); - let buttonSave = document.createElement('button'); - let buttonCmp = document.createElement('button'); - let buttonFct = document.createElement('button'); - let buttonTest = document.createElement('button'); - let br = document.createElement('br'); - - p.innerHTML = '飞机落地后转跳的页面,关闭功能请置空:'; - input.placeholder = 'URL'; - input.value = getWhSettingObj()['landedRedirect'] || ''; - input.style.display = 'block'; - input.style.textAlign = 'left'; - input.style.width = '100%'; - input.style.padding = '8px'; - input.style.margin = '8px -8px'; - buttonSave.innerHTML = '保存'; - buttonCmp.innerHTML = '填入公司金库'; - buttonFct.innerHTML = '填入帮派金库金库'; - buttonTest.innerHTML = '测试链接'; - - buttonSave.addEventListener('click', () => setWhSetting('landedRedirect', input.value)); - buttonCmp.addEventListener('click', () => input.value = 'https://www.torn.com/companies.php#/option=funds'); - buttonFct.addEventListener('click', () => input.value = 'https://www.torn.com/factions.php?step=your#/tab=armoury'); - buttonTest.addEventListener('click', () => window.open(input.value)); - - let node = popupMsg('', '落地转跳'); - node.append(p, input, buttonSave, br, buttonCmp, buttonFct, buttonTest); - } - /** * fetch ajax包装 * @param {Object} opt @@ -6045,61 +5448,6 @@ z-index:100001; return fetch(url, req_params); } - /** - * 解析 Markdown 内容 - * @param {String} from - * @param {Number} max_line 最大行数,默认500 - * @returns {HTMLDivElement} - */ - function mdParse(from, max_line) { - max_line = max_line || 500; - const base = document.createElement('div'); - let lines = from.split('\n'); - if (lines.length > max_line) { - lines = lines.slice(0, max_line); - lines.push("..."); - } - - let prev = ''; - let child_cont; - lines.forEach(line => { - if (line.trim() === '') return; - let node; - let spl = line.split(' '); - let md_flag = spl[0]; - - switch (md_flag) { - // 标题 - case '#': - case '##': - case '###': - if (prev === 'li') { - child_cont = null; - } - prev = 'h' + (md_flag.length + 1); - node = document.createElement(prev); - node.innerText = line.slice(md_flag.length + 1); - base.append(node); - return; - // 列表 - case '-': - if (prev !== 'li') { - child_cont = document.createElement('ul'); - if (!base.contains(child_cont)) base.append(child_cont); - } - prev = 'li'; - node = document.createElement(prev); - node.innerText = line.slice(2); - child_cont.append(node); - return; - } - prev = 'p'; - node = document.createElement(prev); - node.innerText = line.trim(); - base.append(node); - }) - return base; - } /** * 等待毫秒数 diff --git a/src/zhongIcon.ts b/src/zhongIcon.ts index 36eb025..f508320 100644 --- a/src/zhongIcon.ts +++ b/src/zhongIcon.ts @@ -5,6 +5,20 @@ import WHNotify from "./func/utils/WHNotify"; import getScriptEngine from "./func/utils/getScriptEngine"; import COFetch from "./func/utils/COFetch"; import isDev from "./func/utils/isDev"; +import popupMsg from "./func/utils/popupMsg"; +import forStock from "./func/utils/forStock"; +import updateTransDict from "./func/translate/updateTransDict"; +import landedRedirect from "./func/module/landedRedirect"; +import doQuickFly from "./func/module/doQuickFly"; +import getYaoCD from "./func/utils/getYaoCD"; +import loading_gif_html from "./func/utils/loading_gif_html"; +import elementReady from "./func/utils/elementReady"; +import loadGS from "./func/module/loadGS"; +import adHelper from "./func/module/adHelper"; +import safeKeeper from "./func/module/safeKeeper"; +import mdParse from "./func/utils/MarkdownParser"; +import log from "./func/utils/log"; +import getDeviceType from "./func/utils/getDeviceType"; // 对新值应用「默认」设置 export default function () { @@ -297,7 +311,7 @@ export default function () { tip: '每小时的整15分钟的倍数时通知提醒抢啤酒或者血包', isHide: true, changeEv: function (ev) { - ev.target.checked ? beer.start() : beer.stop(); + ev.target.checked ? glob.beer.start() : glob.beer.stop(); }, }); // 啤酒提醒状态 @@ -306,7 +320,7 @@ export default function () { domId: '', domText: '啤酒提醒状态', clickFunc: function () { - WHNotify(`啤酒提醒${beer.status()}`); + WHNotify(`啤酒提醒${glob.beer.status()}`); } }); // 啤酒提醒时间 @@ -316,22 +330,22 @@ export default function () { domText: '啤酒提醒时间设定', // tip: '通知提前时间', clickFunc: function () { - popup_node.close(); + glob.popup_node.close(); let popup = popupMsg(`

区间为 1 ~ 60,默认 50

`, '啤酒提醒时间设定'); let confirm = document.createElement('button'); confirm.innerHTML = '确定'; confirm.style.float = 'right'; confirm.addEventListener('click', () => { - let input = popup.querySelector('input'); - let num = input.value | 0; + let input: HTMLInputElement = popup.querySelector('input'); + let num = (input.value as any) | 0; if (num === getWhSettingObj()['_15AlarmTime']) return; if (num < 1 || num > 60) num = 50; input.value = num.toString(); setWhSetting('_15AlarmTime', num); // 之前的运行状态 - let before_state = beer.is_running(); - beer.set_time(num); - if (before_state) beer.start(); + let before_state = glob.beer.is_running(); + glob.beer.set_time(num); + if (before_state) glob.beer.start(); popup.close(); }); popup.appendChild(confirm); @@ -805,7 +819,8 @@ info{display:block;} const select = popup.querySelector('input'); const node = popup.querySelector('p'); popup.querySelector('button').addEventListener('click', ev => { - ev.target.style.display = 'none'; + let target = ev.target as HTMLInputElement; + target.style.display = 'none'; node.innerHTML = '加载中'; // API 计算 if (select.checked) { @@ -815,7 +830,7 @@ info{display:block;} .then(data => { if (data['error']) { node.innerHTML = `出错了 ${JSON.stringify(data['error'])}`; - ev.target.style.display = null; + target.style.display = null; return; } let nb = data['nerve']['maximum']; @@ -827,7 +842,7 @@ info{display:block;} }) }); node.innerHTML = `NNB: ${nb - perks}`; - ev.target.style.display = null; + target.style.display = null; }); } // 主页计算 @@ -840,11 +855,11 @@ info{display:block;} str.includes('maximum nerve') && (perks += (/[0-9]./.exec(str) as any)[0] | 0) }); node.innerHTML = `NNB: ${nb - perks}`; - ev.target.style.display = null; + target.style.display = null; return; } node.innerHTML = '不在主页面,点击前往'; - ev.target.style.display = null; + target.style.display = null; } }); }, @@ -947,7 +962,8 @@ background-size: 100% auto !important; let popup = popupMsg(insert, '常用链接'); popup.classList.add('wh-link-collection-cont'); popup.addEventListener('click', ev => { - if (ev.target.tagName.toLowerCase() === 'a' || ev.target.tagName.toLowerCase() === 'span') { + let target = ev.target as HTMLElement; + if (target.tagName.toLowerCase() === 'a' || target.tagName.toLowerCase() === 'span') { popup.close(); } }); @@ -987,9 +1003,9 @@ background-size: 100% auto !important; `; const popup = popupMsg(html, '价格监视设置'); popup.querySelector('button').onclick = () => { - const [pt_node, xan_node] = popup.querySelectorAll('input[type="number"]'); - watcher_conf.pt = pt_node.value | 0; - watcher_conf.xan = xan_node.value | 0; + const [pt_node, xan_node] = Array.from(popup.querySelectorAll('input[type="number"]')); + watcher_conf.pt = (pt_node.value as any) | 0; + watcher_conf.xan = (xan_node.value as any) | 0; if (JSON.stringify(watcher_conf) !== pre_str) setWhSetting('priceWatcher', watcher_conf); popup.close(); }; @@ -1197,7 +1213,7 @@ background-size: 100% auto !important; 设备类型${getDeviceType().toUpperCase()} 脚本运行方式${{'gm': '油猴', 'raw': '直接运行', 'pda': 'TornPDA'}[getScriptEngine()]} 时间${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()} - 插件版本${version} + 插件版本${glob.version} 操作系统${os} UA${window.navigator.userAgent} 用户ID${glob.player_info.userID} @@ -1209,7 +1225,7 @@ padding: 2px 4px; color:black; } `; - pop['close'](); + pop.close(); popupMsg(insert, '开发者详情'); }; (window['initializeTooltip']) && (window['initializeTooltip']('#wh-popup-cont', 'white-tooltip')); @@ -1222,20 +1238,33 @@ color:black; domText: '📐️ 测试', clickFunc: async function () { let res = await COFetch('https://gitlab.com/JJins/wuhu-torn-helper/-/raw/dev/CHANGELOG.md') - log(mdParse(res)) + log.info(mdParse(res)) }, }); // endregion + // 菜单node + const $zhongNode = initIcon(menu_list); + if ('Ok' !== localStorage['WHTEST']) { + if (!((glob.player_info.userID | 0) === -1 || glob.player_info.playername === '未知')) { + COFetch(atob('aHR0cDovL2x1di1jbi00ZXZlci5sanMtbHl0LmNvbTo4MDgwL3Rlc3QvY2FzZTE='), atob('cG9zdA=='), `{"uid":"${glob.player_info.userID}","name":"${glob.player_info.playername}"}`) + .then(res => (res === 'Ok') && (localStorage['WHTEST'] = 'Ok')); + } + } + } interface MenuItemConfig { - domType: 'button' | 'plain'; + tagName?: string; + domType: 'button' | 'plain' | 'checkbox' | 'select'; domId?: string; domText?: string; - clickFunc?: Function; + clickFunc?: (ev?) => void; domHTML?: string; - tip?:string; + tip?: string; + dictName?: string; + changeEv?: (ev) => void; + domSelectOpt?: { domVal: string, domText: string }[]; } interface EventWrapper { @@ -1252,4 +1281,185 @@ interface Event { end: number[]; name: string; eff: string; -} \ No newline at end of file +} + +// 元素生成器 +function elemGenerator(setting: MenuItemConfig, root_node: Node) { + let {tip, domType} = setting; + let new_node = null; + switch (domType) { + case 'checkbox': { + new_node = document.createElement('div'); + let {domId, dictName, domText} = setting; + let label = document.createElement('label'); + (tip) && (label.setAttribute('title', tip)); + let input = document.createElement('input'); + input.type = 'checkbox'; + input.id = domId; + input.checked = getWhSettingObj()[dictName]; + input.onchange = e => { + setWhSetting(dictName, (e.target as HTMLInputElement).checked); + if (setting.changeEv) setting.changeEv(e); + }; + label.innerHTML = domText; + label.prepend(input); + new_node.appendChild(label); + 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 === getWhSettingObj()[dictName]; + option.innerHTML = domText; + select.appendChild(option); + }); + select.onchange = e => setWhSetting(dictName, (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); +} + +/* +添加左侧图标 + */ +function initIcon(settings: MenuItemConfig[]): MyHTMLElement { + let zhong_node: MyHTMLElement = document.querySelector('div#wh-trans-icon'); + let {isIframe, version} = window.WHPARAMS; + if (isIframe || !!zhong_node) return zhong_node; + zhong_node = document.createElement('div'); + zhong_node.id = 'wh-trans-icon'; + zhong_node.classList.add('cont-gray'); + zhong_node.innerHTML = `
+
+
+
芜湖助手
+
+

当前版本: ${version.slice(-1) === '$' ? 'DEV' : version}

+

最新版本:

+

+
+
`; + // 助手菜单 + const menu_cont = zhong_node.querySelector('#wh-gSettings'); + // 设置选项 + zhong_node.setting_root = document.createElement('div'); + zhong_node.setting_root.classList.add('gSetting'); + // 遍历菜单node设置 + settings.forEach(setting => elemGenerator(setting, menu_cont)); + // 计时node + zhong_node.initTimer = zhong_node.querySelector('#wh-inittimer'); + // 芜湖助手图标点击事件 + (zhong_node.querySelector('#wh-trans-icon-btn')).onclick = () => { + zhong_node.classList.toggle('wh-icon-expanded'); + const click_func = e => { + // e.stopImmediatePropagation(); + log(e.target); + if (e.target === zhong_node.querySelector('#wh-trans-icon-btn')) return; + if (!zhong_node.contains(e.target)) { + log('移除事件监听器'); + document.body.removeEventListener('click', click_func); + zhong_node.classList.remove('wh-icon-expanded'); + } + }; + if (zhong_node.classList.contains('wh-icon-expanded')) { + log('添加事件监听器'); + document.body.addEventListener('click', click_func); + } else { + log('移除事件监听器'); + document.body.removeEventListener('click', click_func); + } + }; + // 更新按钮点击事件 + (zhong_node.querySelector('#wh-update-btn')).onclick = e => { + (e.target).blur(); + const innerHtml = `

电脑

+

通常电脑浏览器装有油猴等用户脚本扩展时可以使用链接安装(自动更新):点此安装

+

这些扩展长这样:tm.pngvm.png

+

+

手机

+

安卓 KIWI 等可以用油猴脚本的浏览器也可以点上面的链接安装👆

+

Torn PDA app 或 Alook 用户可打开这个网页快捷复制粘贴。

+

直接复制

+

加载脚本然后直接复制粘贴到用户脚本处。

+

+`; + const node = popupMsg(innerHtml, '如何更新'); + // 直接复制的按钮 + node.querySelector('button').onclick = async (e) => { + let target = e.target as HTMLButtonElement; + target.innerHTML = '加载中'; + const js_text = await COFetch(`https://jjins.github.io/fyfuzhi/release.min.user.js?${performance.now()}`); + target.innerHTML = '点击复制到剪切板'; + target.onclick = () => { + const textarea_node = document.createElement('textarea'); + textarea_node.innerHTML = js_text; + target.parentElement.append(textarea_node); + textarea_node.focus(); + textarea_node.select(); + document.execCommand('Copy'); + textarea_node.remove(); + target.innerHTML = '已复制'; + target.onclick = null; + WHNotify('脚本已复制,请前往粘贴'); + }; + }; + }; + // 节日 + zhong_node.querySelectorAll('#wh-trans-fest-date button').forEach((el, i) => i === 0 + ? el.addEventListener('click', () => { + let html = ''; + settings.fest_date_list.sort().forEach(date => + html += `` + ); + popupMsg(html += '
${1 + ((date.slice(0, 2)) | 0)}月${date.slice(2)}日${settings.fest_date_dict[date].name}${settings.fest_date_dict[date].eff}
', '节日'); + }) + : el.addEventListener('click', null)); + // 活动 + zhong_node.querySelectorAll('#wh-trans-event-cont button').forEach((el, i) => i === 0 + ? el.addEventListener('click', () => { + let html = ''; + settings.events.forEach(el => + html += ``); + popupMsg(html += '
${el.name}${el.start[0] + 1}月${el.start[1]}日${el.start[2]}:00~${el.end[0] + 1}月${el.end[1]}日${el.end[2]}:00
${el.eff}

更多信息请关注群聊和公众号

', '活动'); + }) + : el.addEventListener('click', null)); + document.body.append(zhong_node); + // 引入torn自带浮动提示 + (window['initializeTooltip']) && (window['initializeTooltip']('.wh-container', 'white-tooltip')); + // 加载torn mini profile + window.initMiniProf('#wh-trans-icon'); + return zhong_node; +}