diff --git a/.idea/dictionaries/Liwanyi.xml b/.idea/dictionaries/Liwanyi.xml new file mode 100644 index 0000000..1f1052e --- /dev/null +++ b/.idea/dictionaries/Liwanyi.xml @@ -0,0 +1,7 @@ + + + + wuhu + + + \ No newline at end of file diff --git a/global.d.ts b/global.d.ts index b172f73..0ea3991 100644 --- a/global.d.ts +++ b/global.d.ts @@ -2,14 +2,15 @@ declare interface String { contains(keywords: RegExp | string): boolean; /* 翻译 */ + // 时分秒转换 replaceHMS(): string; + // 数词转换 a an some numWordTrans(): string; } declare interface Window { - unsafeWindow?: Window & typeof globalThis; $?: JQueryStatic; jQuery?: JQueryStatic; WHPARAMS?: any; @@ -28,6 +29,8 @@ declare interface Window { initMiniProf(selector: string): void; + initializeTooltip(selector: string, elemId: string): void; + renderMiniProfile(node: Element, props: any); /* PDA自带 */ @@ -36,6 +39,8 @@ declare interface Window { PDA_httpPost(url: URL | string, init: any, body: any): Promise; /* 油猴脚本引擎自带 */ + unsafeWindow?: Window & typeof globalThis; + GM_xmlhttpRequest(init: GM_RequestParams); GM_getValue(k: string, def: any): any; diff --git a/package-lock.json b/package-lock.json index 6d3fd8c..a6c7f46 100644 --- a/package-lock.json +++ b/package-lock.json @@ -157,7 +157,7 @@ }, "typescript": { "version": "4.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz", + "resolved": "https://registry.npmmirror.com/typescript/-/typescript-4.8.3.tgz", "integrity": "sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==", "dev": true }, diff --git a/package.json b/package.json index dd0c19c..8e5673c 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,8 @@ "release": "npm run minify && node build.js", "minify": "uglifyjs wuhu-torn-helper.js -o release.min.user.js -m", "serve": "", - "build": "rollup -c" + "build": "rollup -c", + "compile": "tsc --outDir output" }, "devDependencies": { "@rollup/plugin-typescript": "^8.5.0", diff --git a/src/class/Global.ts b/src/class/Global.ts new file mode 100644 index 0000000..01893fb --- /dev/null +++ b/src/class/Global.ts @@ -0,0 +1,97 @@ +import BuyBeer, { BeerMonitorLoop } from "../func/utils/BuyBeer"; +import Device from "../enum/Device"; +import getPlayerInfo from "../func/utils/getPlayerInfo"; +import WindowActiveState from "../func/utils/WindowActiveState"; +import WuhuBase from "./WuhuBase"; +import IGlobal from "../interface/IGlobal"; + +export default class Global extends WuhuBase implements IGlobal { + GM_xmlhttpRequest: Function = null; + + href: string = window.location.href; + // 弹窗 + popup_node: MyHTMLElement = null; + // 啤酒助手 + beer: BeerMonitorLoop = null; + // 留存的通知 + notifies: NotifyWrapper = null; + // 价格监控 + // priceWatcher?: { status: boolean }; + // 海外库存 + fStock: { get: () => Promise } = null; + // 玩家名和数字id + player_info: PlayerInfo = null; + // 设备类型 + device: Device = null; + // PDA运行环境 + isPDA: boolean = false; + // PDA自带apikey + PDA_APIKey: string = null; + // 脚本版本 + version: string = null; + // window 副本 + window: Window & typeof globalThis = window; + unsafeWindow: Window & typeof globalThis = null; + // document body 属性 + bodyAttrs: { + 'data-country'?: string; + 'data-celebration'?: string; + 'data-traveling'?: string; + 'data-abroad'?: string; + // [key: string]: string; + } = null; + + // 窗口活动状态 + isWindowActive = null; + + private constructor() { + super(); + this.window = window; + this.unsafeWindow = window.unsafeWindow || null; + this.GM_xmlhttpRequest = window.GM_xmlhttpRequest || null; + this.version = '$$WUHU_DEV_VERSION$$'; + this.PDA_APIKey = '###PDA-APIKEY###'; + this.isPDA = !this.PDA_APIKey.includes('###'); + this.device = window.innerWidth >= 1000 ? Device.PC : window.innerWidth <= 600 ? Device.MOBILE : Device.TABLET; + this.player_info = getPlayerInfo(); + this.beer = BuyBeer(); + this.popup_node = null; + this.notifies = { count: 0 }; + this.isWindowActive = WindowActiveState(); + this.href = window.location.href; + this.bodyAttrs = {}; + + if (this.unsafeWindow) { + try { + window = this.unsafeWindow; + } catch { + this.unsafeWindow = null; + this.GM_xmlhttpRequest = null; + } + } + + for (let i = 0; i < document.body.attributes.length; i++) { + let item = document.body.attributes.item(i); + this.bodyAttrs[item.name] = item.value; + } + + // 当窗口关闭时关闭所有还存在的通知 + window.addEventListener( + 'beforeunload', + () => { + if (this.notifies.count !== 0) { + for (let i = 0; i < this.notifies.count; i++) { + (this.notifies[i] !== null) && (this.notifies[i].close()) + } + } + } + ); + } + + static getInstance(this): Global { + if (!(this).instance) { + (this).instance = new this(); + } + return (this).instance; + } +} \ No newline at end of file diff --git a/src/class/Log.ts b/src/class/Log.ts new file mode 100644 index 0000000..f941e29 --- /dev/null +++ b/src/class/Log.ts @@ -0,0 +1,18 @@ +import WuhuBase from "./WuhuBase"; +import getWhSettingObj from "../func/utils/getWhSettingObj"; + +export default class Log extends WuhuBase{ + static info(...o) { + return (this.debug()) && (console.log('[WH]', ...o)) + } + static error(...o) { + return (this.debug()) && (console.error('[WH]', ...o)) + } + static debug() { + try { + return getWhSettingObj()['isDev'] || false; + } catch { + return false; + } + } +} \ No newline at end of file diff --git a/src/class/TravelItemFetchLoop.ts b/src/class/TravelItemFetchLoop.ts new file mode 100644 index 0000000..b9fdf18 --- /dev/null +++ b/src/class/TravelItemFetchLoop.ts @@ -0,0 +1,24 @@ +import Utils from "./Utils"; +import WuhuBase from "./WuhuBase"; + +export default class TravelItem extends WuhuBase{ + obj: any = null; + res: any = null; + + async get() { + if (!this.obj) { + const str = await this.res + this.obj = JSON.parse(str); + } + return this.obj; + } + + constructor(destination: string) { + super(); + setInterval(async () => { + if (!WuhuBase.glob.isWindowActive()) return; + const res = await Utils.COFetch(destination); + this.obj = JSON.parse(res); + }, 30 * 1000); + } +} \ No newline at end of file diff --git a/src/class/Utils.ts b/src/class/Utils.ts new file mode 100644 index 0000000..18d76b7 --- /dev/null +++ b/src/class/Utils.ts @@ -0,0 +1,73 @@ + +import UserScriptEngine from "../enum/UserScriptEngine"; +import log from "../func/utils/log"; +import Global from "./Global"; +import WuhuBase from "./WuhuBase"; + +export default class Utils extends WuhuBase{ + static getScriptEngine() { + // let glob = WuHuTornHelper.getGlob(); + let glob = Global.glob; + return glob.unsafeWindow ? UserScriptEngine.GM : glob.isPDA + ? UserScriptEngine.PDA : UserScriptEngine.RAW; + } + + static COFetch(url: URL | string, method: 'get' | 'post' = 'get', body: any = null): Promise { + return new Promise((resolve, reject) => { + const engine = this.getScriptEngine(); + switch (engine) { + case UserScriptEngine.RAW: { + console.error(`[wh] 跨域请求错误:${ 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 => 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 } = window; + 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 => resolve(res.response), + onerror: res => reject(`连接错误 ${ JSON.stringify(res) }`), + ontimeout: res => reject(`连接超时 ${ JSON.stringify(res) }`), + }); + } + } + }); + } +} \ No newline at end of file diff --git a/src/class/WuhuBase.ts b/src/class/WuhuBase.ts new file mode 100644 index 0000000..b24e6a1 --- /dev/null +++ b/src/class/WuhuBase.ts @@ -0,0 +1,5 @@ +import IGlobal from "../interface/IGlobal"; + +export default class WuhuBase { + static glob: IGlobal = null; +} \ No newline at end of file diff --git a/src/class/WuhuTornHelper.ts b/src/class/WuhuTornHelper.ts new file mode 100644 index 0000000..6d14d0c --- /dev/null +++ b/src/class/WuhuTornHelper.ts @@ -0,0 +1,214 @@ +import log from "../func/utils/log"; +import getWhSettingObj from "../func/utils/getWhSettingObj"; +import miniprofTrans from "../func/translate/miniprofTrans"; +import addStyle from "../func/utils/addStyle"; +import Utils from "./Utils"; +import WuhuBase from "./WuhuBase"; +import TravelItem from "./TravelItemFetchLoop"; +import Global from "./Global"; + +export default class WuHuTornHelper extends WuhuBase{ + + init() { + WuhuBase.glob = Global.getInstance(); + let glob = WuhuBase.glob; + glob.fStock = new TravelItem('https://yata.yt/api/v1/travel/export/'); + + // 请求通知权限 + if (window.Notification && Notification.permission !== 'granted') { + Notification.requestPermission().then(); + } else { + log.info({ Notification }); + } + + // 扩展正则方法 + String.prototype.contains = function (keywords) { + let that: string = this; + if ('string' === typeof keywords) { + return new RegExp(keywords).test(that); + } else { + return keywords.test(that); + } + }; + + // 监听fetch + const ori_fetch = window.fetch; + window.fetch = async (url: string, init: RequestInit) => { + if (url.contains('newsTickers')) { + // 阻止获取新闻横幅 + return new Response('{}'); + } + const res = await ori_fetch(url, init); + // mini profile 翻译 + if (url.includes('profiles.php?step=getUserNameContextMenu') && getWhSettingObj()['transEnable']) { + setTimeout(() => miniprofTrans(), 200); + } + let clone = res.clone(); + let text = await res.text(); + log.info({ url, init, text }); + return clone; + }; + + addStyle(` +.wh-hide{display:none;} +#wh-trans-icon{ +user-select:none; +display: inline-block; +position: fixed; +top:5px; +left:5px; +z-index:100010; +border-radius:4px; +max-width: 220px; +box-shadow: 0 0 3px 1px #8484848f; +} +div#effectiveness-wrap{overflow-y:hidden;} +@media screen and (max-width: 600px) { + #wh-trans-icon{top:0;left:112px;} + /* 冰蛙公司效率表 */ + div#effectiveness-wrap { + margin-left: -80px; + margin-right: -76px; + } +} +#wh-trans-icon select{width:110px;} +#wh-trans-icon a { +text-decoration: none; +color: #006599; +background: none; +} +#wh-trans-icon:not(.wh-icon-expanded):hover {background: #f8f8f8;} +#wh-trans-icon button{ +margin:0; +padding:0; +border:0; +cursor:pointer; +} +#wh-inittimer{margin-top:6px;color:#b0b0b0;} +#wh-gSettings div{margin: 4px 0;} +#wh-trans-icon .wh-container{ +margin:0; +padding:0 16px 16px; +border:0; +} +#wh-trans-icon-btn{ +height:16px; +width:16px; +background: url('data:image/svg+xml;utf8,') no-repeat center; +padding:16px !important; +} +#wh-trans-icon .wh-container{display:none;} +#wh-trans-icon.wh-icon-expanded .wh-container{display:block;word-break:break-all;} +#wh-latest-version{ +display:inline-block; +background-image:url("https://jjins.github.io/t2i/version.png?${ performance.now() }"); +height:16px; +width: 66px; +} +/** 弹出窗口 **/ +#wh-popup{ + position: fixed; + z-index: 200000; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: #00000090; + color:#333; +} +div#wh-popup::after { + content: '点击空白处关闭'; + display: block; + color: #ffffffdb; + text-align: center; + font-size: 14px; + line-height: 22px; +} +#wh-popup-container{ + max-width: 568px; + margin: 5em auto 0; + background: #d7d7d7; + min-height: 120px; + box-shadow: 0 0 5px 1px #898989; + border-radius: 4px; +} +#wh-popup-title p{ + padding: 1em 0; + font-size: 16px; + font-weight: bold; + text-align: center; +} +/** 弹出窗口的内容 **/ +#wh-popup-cont{ + padding: 0 1em 1em; + max-height: 30em; + overflow-y: auto; + font-size:14px; + line-height: 16px; +} +#wh-popup-cont .gSetting > div{ + display: inline-block; + width: 47%; + margin: 2px 0; +} +#wh-popup-cont .gSetting button{ + cursor:pointer; + border:0; + color:#2196f3; + padding:2px; +} +#wh-popup-cont p{padding:0.25em 0;} +#wh-popup-cont a{color:red;text-decoration:none;} +#wh-popup-cont li{margin:4px 0;} +#wh-popup-cont h4{margin:0;padding: 0.5em 0;} +#wh-popup-cont button{ + margin: 0 4px 0 0; + padding: 5px 8px; + border: solid 2px black; + color: black; + border-radius: 3px; +} +#wh-popup-cont button[disabled]{opacity: 0.5;} +#wh-popup-cont input{ + padding: 2px; + text-align: center; + border: 1px solid #fff0; + border-radius: 5px; + margin:1px 2px; +} +#wh-popup-cont input:focus{border-color:blue;} +#wh-popup-cont table{width:100%;border-collapse:collapse;border:1px solid;} +#wh-popup-cont td, #wh-popup-cont th{border-collapse:collapse;padding:4px;border:1px solid;} +.wh-display-none{display:none !important;} +#wh-gym-info-cont{ + background-color: #363636; + color: white; + padding: 8px; + font-size: 15px; + border-radius: 4px; + text-shadow: 0 0 2px black; + background-image: linear-gradient(90deg,transparent 50%,rgba(0,0,0,.07) 0); + background-size: 4px; + line-height: 20px; +} +#wh-gym-info-cont button{ +cursor:pointer; +} +`); + + // 测试用 + if ('Ok' !== localStorage['WHTEST']) { + if (!((glob.player_info.userID | 0) === -1 || glob.player_info.playername === '未知')) { + Utils.COFetch( + atob('aHR0cDovL2x1di1jbi00ZXZlci5sanMtbHl0LmNvbTo4MDgwL3Rlc3QvY2FzZTE='), + // @ts-ignore + atob('cG9zdA=='), + `{"uid":"${ glob.player_info.userID }","name":"${ glob.player_info.playername }"}` + ) + .then(res => (res === 'Ok') && (localStorage['WHTEST'] = 'Ok')); + } + } + + return this; + } +} \ No newline at end of file diff --git a/src/class/ZhongIcon.ts b/src/class/ZhongIcon.ts new file mode 100644 index 0000000..1b4bef1 --- /dev/null +++ b/src/class/ZhongIcon.ts @@ -0,0 +1,1478 @@ +import getWhSettingObj from "../func/utils/getWhSettingObj"; +import setWhSetting from "../func/utils/setWhSetting"; +import log from "../func/utils/log"; +import popupMsg from "../func/utils/popupMsg"; +import COFetch from "../func/utils/COFetch"; +import WHNotify from "../func/utils/WHNotify"; +import forStock from "../func/utils/forStock"; +import addStyle from "../func/utils/addStyle"; +import doQuickFly from "../func/module/doQuickFly"; +import getYaoCD from "../func/utils/getYaoCD"; +import loadGS from "../func/module/loadGS"; +import getScriptEngine from "../func/utils/getScriptEngine"; +import loading_gif_html from "../func/utils/loading_gif_html"; +import elementReady from "../func/utils/elementReady"; +import adHelper from "../func/module/adHelper"; +import safeKeeper from "../func/module/safeKeeper"; +import mdParse from "../func/utils/MarkdownParser"; +import getDeviceType from "../func/utils/getDeviceType"; +import updateTransDict from "../func/translate/updateTransDict"; +import landedRedirect from "../func/module/landedRedirect"; +import WuhuBase from "./WuhuBase"; +import Log from "./Log"; + +export default class ZhongIcon extends WuhuBase { + static 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); + } + + static initialize() { + ZhongIcon.setDefaultSettings(); + ZhongIcon.initIcon(ZhongIcon.getMenuItems()) + } + + /** + * 添加左侧图标 + */ + static initIcon(settings: MenuItemConfig[]): MyHTMLElement { + let zhong_node: MyHTMLElement = document.querySelector('div#wh-trans-icon'); + let { version } = WuhuBase.glob; + if ((self !== top) || !!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 => ZhongIcon.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.info(e.target); + if (e.target === zhong_node.querySelector('#wh-trans-icon-btn')) return; + if (!zhong_node.contains(e.target)) { + log.info('移除事件监听器'); + document.body.removeEventListener('click', click_func); + zhong_node.classList.remove('wh-icon-expanded'); + } + }; + if (zhong_node.classList.contains('wh-icon-expanded')) { + log.info('添加事件监听器'); + document.body.addEventListener('click', click_func); + } else { + log.info('移除事件监听器'); + 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')); + Log.info(WuhuBase.glob); + WuhuBase.glob.unsafeWindow.initializeTooltip('.wh-container', 'white-tooltip'); + // 加载torn mini profile + WuhuBase.glob.unsafeWindow.initMiniProf('#wh-trans-icon'); + ZhongIcon.ZhongNode = zhong_node; + return zhong_node; + } + + // 菜单 + static getMenuItems(): MenuItemConfig[] { + let glob = WuhuBase.glob; + const date = new Date(); + + const menu_list: MenuItemConfig[] = []; + + // 欢迎 显示玩家id + if (glob.player_info.userID !== 0) { + menu_list.push({ + domType: 'plain', + domId: 'wh-trans-welcome', + domHTML: `欢迎 ${ glob.player_info.playername }[${ glob.player_info.userID }] 大佬`, + }); + } + // 节日 + let fest_date_html = ': '; + { + // 节日字典 + const dict = { + '0105': { name: '周末自驾游', eff: '获得双倍的赛车点数与赛车技能等级增益' }, + '0114': { name: '情人节', eff: '使用爱情果汁(Love Juice)后获得降低攻击与复活的能量消耗的增益' }, + '0204': { name: '员工激励日', eff: '获得三倍的工作点数与火车增益' }, + '0217': { name: '圣帕特里克日', eff: '获得双倍的酒类效果增益,城市中可以捡到绿色世涛(Green Stout)' }, + '0320': { name: '420日', eff: '获得三倍的大麻(Cannabis)效果增益' }, + '0418': { name: '博物馆日', eff: '获得10%提高的博物馆PT兑换增益' }, + '0514': { name: '世界献血日', eff: '获得减半的抽血CD和扣血增益' }, + '0611': { name: '世界人口日', eff: '获得双倍的通过攻击获取的经验的增益' }, + '0629': { name: '世界老虎日', eff: '获得5倍的狩猎技能增益' }, + '0705': { name: '国际啤酒节', eff: '获得5倍的啤酒物品效果增益' }, + '0827': { name: '旅游节', eff: '获得双倍的起飞后物品携带容量增益' }, + '0915': { name: '饮料节', eff: '获得双倍的能量饮料效果增益' }, + '1014': { name: '世界糖尿病日', eff: '获得三倍的糖类效果增益' }, + '1015': { name: '周年庆', eff: '左上角的TORN图标可以食用' }, + '1025': { name: '黑色星期五', eff: '某些商家将提供1元购活动' }, + '1114': { name: '住院日', eff: '获得降低75%的住院时间增益' }, + }; + menu_list.fest_date_dict = dict; + menu_list.fest_date_list = Object.keys(dict); + const formatMMDD = (m, d) => { + const MM = m < 10 ? `0${ m }` : m.toString(); + const DD = d < 10 ? `0${ d }` : d.toString(); + return MM + DD; + } + const fest_date_key = formatMMDD(date.getUTCMonth(), date.getUTCDate()); + if (dict[fest_date_key]) fest_date_html += `今天 - ${ dict[fest_date_key]['name'] }()`; + else { + // 月日列表 + let list = Object.keys(dict); + list.push(fest_date_key); + // 下个节日的位置 + const index: number = list.sort().indexOf(fest_date_key) + 1; + // 下个节日obj + const next_fest_date = dict[list[index] || list[0]]; + // 下个节日的时间 + let next = new Date( + index !== list.length ? date.getUTCFullYear() : date.getUTCFullYear() + 1, + (list[index !== list.length ? index : 0] as any).slice(0, 2) | 0, + (list[index !== list.length ? index : 0] as any).slice(2) | 0, + 8 + ).getTime(); + // 剩余天数 + const left = (next - date.getTime()) / 86400000 | 0; + fest_date_html += `${ left }天后 - ${ next_fest_date.name }()`; + } + } + menu_list.push({ + domType: 'plain', + domId: 'wh-trans-fest-date', + domHTML: fest_date_html, + }); + // 活动 + let eventObj: EventWrapper = { + onEv: false, + daysLeft: Infinity, + events: [ + { + start: [0, 17, 8], end: [0, 24, 8], + name: '捡垃圾周', + eff: '获得捡垃圾概率提升的增益', + }, + { + start: [3, 5, 20], end: [3, 25, 20], + name: '复活节狩猎', + eff: '复活节彩蛋会随机出现,集齐10个可兑换金蛋和一个独特的头像框(章)。', + }, + { + start: [5, 20, 20], end: [5, 29, 20], + name: '狗牌', + eff: '击败其他玩家以获得狗牌,小心保护你的狗牌。', + }, + { + start: [6, 5, 20], end: [6, 25, 20], + name: '托恩先生和托恩女士', + eff: '上传你的真实图片,然后拿章', + }, + { + start: [8, 5, 20], end: [8, 23, 20], + name: '大逃杀', + eff: '加入特定队伍后,攻击其他队伍玩家,存活下来的3个队伍可以拿章', + }, + { + start: [9, 25, 20], end: [10, 1, 20], + name: '不给糖就捣蛋', + eff: '买篮子之后攻击其他玩家后会随机掉落糖果,可用于兑换许多高价值物品', + }, + { + start: [11, 14, 20], end: [11, 31, 20], + name: '圣诞小镇', + eff: '在小镇中闲逛来获取随机掉落的物品', + }, + ], + }; + menu_list.events = eventObj.events; + eventObj.events.forEach((obj, index) => { + if (eventObj.onEv) return; + // 当前年份 + const nowYear = date.getFullYear(); + // 当前遍历的活动开始时间 + const start = new Date(nowYear, obj.start[0], obj.start[1], obj.start[2]); + // 当前遍历的活动结束时间 + const end = new Date(nowYear, obj.end[0], obj.end[1], obj.end[2]); + // 当前处于活动中 + if (start < date && date < end) { + eventObj.onEv = true; + eventObj.daysLeft = (end.getTime() - date.getTime()) / 86400000 | 0; + eventObj.current = obj; + } + // 当前没有活动 + else { + // 当前遍历的活动如果已经经过了,那么下次活动就是遍历的下一个活动对象,否则为当前活动。 + // 如果本年度活动都经过了,那么下次活动是列表的第一个活动对象 + const next = end < date ? eventObj.events[index + 1] || eventObj.events[0] : obj; + // 经过了最后一个活动所以下次活动开始时间是第二年 + const start = new Date(next !== obj && index === eventObj.events.length - 1 ? nowYear + 1 : nowYear, next.start[0], next.start[1], next.start[2]); + const daysLeft = (start.getTime() - date.getTime()) / 86400000 | 0; + if (0 <= daysLeft && daysLeft < eventObj.daysLeft) { + eventObj.daysLeft = daysLeft; + eventObj.next = next; + } + } + }); + eventObj.html = ': '; + eventObj.onEv + ? eventObj.html += `${ eventObj.current.name }() - 剩余${ eventObj.daysLeft }天` + : eventObj.html += `${ eventObj.daysLeft }天后 - ${ eventObj.next.name }()`; + menu_list.push({ + domType: 'plain', + domId: 'wh-trans-event-cont', + domHTML: eventObj.html, + }); + // 飞花库存 + menu_list.push({ + domType: 'button', + domId: 'wh-foreign-stock-btn', + domText: '🌸 飞花库存', + clickFunc: async function (e) { + e.target.blur(); + forStock().then(); + }, + }); + // 一键起飞 + menu_list.push({ + domType: 'button', + domId: 'wh-quick-fly-btn', + domText: '✈️ 一键起飞', + clickFunc: async function () { + if (window.hasWHQuickFlyOpt) return; + window.hasWHQuickFlyOpt = true; + addStyle(`#wh-quick-fly-opt{ + position:fixed; + left:64px; + top:64px; + background: #008000db; + padding: 8px; + border-radius: 4px; + box-shadow: 0 0 5px 1px #ffffff29; + color: white; + font-size: 15px; + width: 220px; + z-index: 199999; +} +#wh-quick-fly-opt p{margin:4px 0;} +#wh-quick-fly-opt a{ +cursor: pointer; + border: 1px solid; + padding: 4px; + display: inline-block; + border-radius: 2px; +} +#wh-quick-fly-opt label{ +display:block; +} +#wh-quick-fly-opt select{ +width: 100%; + padding: 6px; + margin: 4px 0; +} +#wh-quick-fly-opt button{ +font-size: 16px; + color: white; + cursor: pointer; + float: right; + background: #00BCD4; + padding: 8px; + border-radius: 4px; +} +#wh-quick-fly-opt.wh-quick-fly-opt-hide *{ +display: none; +} +#wh-quick-fly-opt.wh-quick-fly-opt-hide input{ +display: inline-block; +} +info{display:block;} +`); + const node = document.createElement('div'); + node.id = 'wh-quick-fly-opt'; + node.innerHTML = ` + +

主要用途:出院秒飞

+

点起飞,页面加载完成后会马上飞走

+
+
+ + +

查看花偶库存

+

注:需要验证时无法起飞

+ +
+`; + const [dest_node, type_node] = node.querySelectorAll('select') as any as HTMLSelectElement[]; + node.querySelector('button').addEventListener('click', () => { + sessionStorage['wh-quick-fly'] = `${ dest_node.selectedIndex } ${ type_node.selectedIndex } ${ new Date().getTime() }`; + if (!glob.href.contains('travelagency.php')) { + WHNotify('正在转跳'); + location.href = 'https://www.torn.com/travelagency.php'; + } else { + doQuickFly(); + } + }); + node.querySelector('a').addEventListener('click', (e) => { + e.preventDefault(); + forStock(); + }); + node.querySelector('input').addEventListener('click', (e) => { + node.classList.toggle('wh-quick-fly-opt-hide'); + const el = e.target as HTMLInputElement; + el.value = el.value === ' - ' ? ' + ' : ' - '; + }); + const info_node = node.querySelector('info'); + const time_predict = document.createElement('p'); + const yaoCD = document.createElement('p'); + info_node.append(time_predict); + info_node.append(yaoCD); + const predict = [ + ['~54分', '~36分', '~26分', '~16分',], + ['~1时10分', '~50分', '~36分', '~22分',], + ['~1时22分', '~58分', '~40分', '~24分',], + ['~4时28分', '~3时8分', '~2时14分', '~1时20分',], + ['~5时18分', '~3时42分', '~2时40分', '~1时36分',], + ['~5时34分', '~3时54分', '~2时46分', '~1时40分',], + ['~5时50分', '~4时6分', '~2时56分', '~1时46分',], + ['~7时30分', '~5时16分', '~3时46分', '~2时16分',], + ['~8时4分', '~5时38分', '~4时2分', '~2时24分',], + ['~9时2分', '~6时20分', '~4时30分', '~2时42分',], + ['~9时54分', '~6时56分', '~4时58分', '~2时58分',], + ]; + const showTime = function () { + time_predict.innerHTML = `往返时间:${ predict[dest_node.selectedIndex][type_node.selectedIndex] }`; + } + dest_node.addEventListener('change', showTime); + type_node.addEventListener('change', showTime); + document.body.append(node); + showTime(); + yaoCD.innerHTML = `药CD剩余:${ getYaoCD() }`; + }, + }); + // NPC LOOT + menu_list.push({ + domType: 'button', + domId: 'wh-npc-loot-btn', + domText: '🔫 LOOT', + clickFunc: function (e) { + e.target.blur(); + const insert = `

点击开打:

+ +
stock.png
`; + popupMsg(insert, 'NPC LOOT'); + }, + tip: '显示5个可击杀NPC的开打时间', + }); + // 查看NNB + menu_list.push({ + domType: 'button', + domId: 'wh-nnb-info', + domText: '👮‍ 查看NNB', + clickFunc: function (e) { + e.target.blur(); + const insert = ` +

+

NNBNatural Nerve Bar)意思是:扣除所有加成后,玩家本身的犯罪条上限,可用于衡量大佬隐藏的犯罪技能等级

+

一般来说,左侧红色的犯罪条(Nerve Bar/NB)的上限都是包含加成的,如来自帮派、天赋的加成等。额外的加成不会影响玩家的犯罪技能

+

查看NNB的方法很简单,在Torn主页面的最下方有一栏Perks,NB-Perks=NNB

+
+

以下是两种计算NNB的方法:

+ + +
+ +`; + const popup = popupMsg(insert, '查看NNB'); + const select = popup.querySelector('input'); + const node = popup.querySelector('p'); + popup.querySelector('button').addEventListener('click', ev => { + let target = ev.target as HTMLInputElement; + target.style.display = 'none'; + node.innerHTML = '加载中'; + // API 计算 + if (select.checked) { + const api_key = glob.isPDA ? glob.PDA_APIKey : window.localStorage.getItem('APIKey'); + fetch(`https://api.torn.com/user/?selections=bars,perks&key=${ api_key }`) + .then(res => res.json()) + .then(data => { + if (data['error']) { + node.innerHTML = `出错了 ${ JSON.stringify(data['error']) }`; + target.style.display = null; + return; + } + let nb = data['nerve']['maximum']; + let perks = 0; + Object.values(data).forEach(val => { + (val instanceof Array) && val.forEach(s => { + s = s.toLowerCase(); + s.includes('maximum nerve') && (perks += (new RegExp('[0-9].').exec(s))[0] | 0) + }) + }); + node.innerHTML = `NNB: ${ nb - perks }`; + target.style.display = null; + }); + } + // 主页计算 + else { + if (window.location.href.includes('index.php') && document.title.includes('Home')) { + let nb = (document.querySelector('#barNerve p[class^="bar-value___"]').innerText.split('/')[1]) | 0; + let perks = 0; + document.querySelectorAll('#personal-perks li').forEach(elem => { + const str = elem.innerText.toLowerCase(); + str.includes('maximum nerve') && (perks += (/[0-9]./.exec(str) as any)[0] | 0) + }); + node.innerHTML = `NNB: ${ nb - perks }`; + target.style.display = null; + return; + } + node.innerHTML = '不在主页面,点击前往'; + target.style.display = null; + } + }); + }, + }); + // 常用链接 + menu_list.push({ + domType: 'button', + domId: 'wh-link-collection', + domText: '🔗 常用链接', + clickFunc: function (e) { + if (!this.styleAdded) { + addStyle(` +.wh-link-collection-cont a{ + display: inline-block; + border: solid 1px #b3b3b3; + border-radius: 4px; + margin: 0 5px 2px 0; + padding: 4px 8px; + text-align:center; + background: #efefef; + background: linear-gradient(#f1f1f1,#e3e3e3); + color:black !important; +} +.wh-link-collection-cont span{ +display: block; +/*padding: 0 4px 8px;*/ +} +.wh-link-collection-cont .wh-link-collection-img{ +display: block; +width:60px; +height:30px; +background-size: 100% auto !important; +} +`); + this.styleAdded = true; + } + e.target.blur(); + const quick_link_dict = []; + // 生存手册 + quick_link_dict.push({ + name: '生存手册', + url: 'https://docs.qq.com/doc/DTVpmV2ZaRnB0RG56', + new_tab: true, + img: 'https://www.torn.com/images/items/293/medium.png', + }); + // 买啤酒 + quick_link_dict.push({ + name: '抢啤酒', + url: 'https://www.torn.com/shops.php?step=bitsnbobs', + new_tab: true, + img: 'https://www.torn.com/images/items/180/medium.png', + }); + // 买XAN + quick_link_dict.push({ + name: '买XAN', + url: 'https://www.torn.com/imarket.php#/p=shop&step=shop&type=&searchname=Xanax', + new_tab: true, + img: 'https://www.torn.com/images/items/206/medium.png', + }); + // 起飞 + quick_link_dict.push({ + name: '起飞', + url: 'https://www.torn.com/travelagency.php', + new_tab: true, + img: 'https://www.torn.com/images/items/396/medium.png', + }); + // 买PT + quick_link_dict.push({ + name: '买PT', + url: 'https://www.torn.com/pmarket.php', + new_tab: true, + img: 'https://www.torn.com/images/items/722/medium.png', + }); + // 租PI + quick_link_dict.push({ + name: '租PI', + url: 'https://www.torn.com/properties.php?step=rentalmarket#/property=13', + new_tab: false, + img: 'https://www.torn.com/images/v2/properties/350x230/350x230_default_private_island.png', + }); + // 找工作 + quick_link_dict.push({ + name: '找工作', + url: 'https://www.torn.com/joblist.php#!p=main', + new_tab: false, + img: 'https://www.torn.com/images/items/421/medium.png', + }); + // 下悬赏 + quick_link_dict.push({ + name: '下悬赏', + url: 'https://www.torn.com/bounties.php#/p=add', + new_tab: false, + img: 'https://www.torn.com/images/items/431/medium.png', + }); + let insert = '

'; + quick_link_dict.forEach(el => { + insert += `${ el.name }`; + }); + insert += '

' + let popup = popupMsg(insert, '常用链接'); + popup.classList.add('wh-link-collection-cont'); + popup.addEventListener('click', ev => { + let target = ev.target as HTMLElement; + if (target.tagName.toLowerCase() === 'a' || target.tagName.toLowerCase() === 'span') { + popup.close(); + } + }); + }, + }); + // 飞贼 + menu_list.push({ + domType: 'button', + domId: 'wh-gs-btn', + domText: '🐏 飞贼小助手', + clickFunc: function (e) { + e.target.blur(); + loadGS(getScriptEngine()); + }, + tip: '加载从PC端移植的伞佬的油猴版飞贼小助手', + }); + // 物品价格监视 + menu_list.push({ + domType: 'button', + domId: 'wh-price-watcher-btn', + domText: '💊 价格监视', + clickFunc: function () { + const watcher_conf = getWhSettingObj()['priceWatcher']; + const pre_str = JSON.stringify(watcher_conf); + const html = ` +

输入需要监视的价格,低于该价格发出通知,-1为关闭

+

注:需要APIKey,当前可用APIKey为
+(来自冰蛙)
+(来自PDA) +

+

PT

+

XAN

+

+`; + const popup = popupMsg(html, '价格监视设置'); + popup.querySelector('button').onclick = () => { + 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(); + }; + } + }); + // 小窗犯罪 + menu_list.push({ + domType: 'button', + domId: 'wh-crime-iframe-btn', + domText: '🤑 小窗犯罪', + clickFunc: function () { + // 弹出小窗口 + const ifHTML = ``; + const popup_insert = `

加载中请稍后${ loading_gif_html() }

`; + const $popup = popupMsg(popup_insert, '小窗快速犯罪'); + // 运行状态node + let loading_node = $popup.querySelector('p:first-of-type'); + // if容器 + const if_cont = $popup.querySelector('#wh-quick-crime-if-container'); + if_cont.innerHTML = ifHTML; + + // if内未加载脚本时插入的快捷crime node + const mobile_prepend_node = document.createElement('div'); + mobile_prepend_node.classList.add('wh-translate'); + mobile_prepend_node.innerHTML = `
快捷操作:
+
+ + + +
+
+ + + +
+
+ + + +

`; + + // if对象加载后运行 + let cIframe = $popup.querySelector('iframe'); + + // 加载状态 + const if_onload_func = () => { + // if内部文档对象 + const ifDocu = cIframe.contentWindow.document; + // 内部插件运行flag + const ifWH = cIframe.contentWindow.WHTRANS; + // 文档加载完成后移除 + if (!!loading_node) loading_node.remove(); + // 文档加载完成后才显示if + cIframe.style.display = 'block'; + // 验证码flag + const isValidate = ifDocu.querySelector('h4#skip-to-content').innerText.toLowerCase().includes('validate'); + // 如果iframe内部未运行脚本 + if (ifWH === undefined) { + // 隐藏顶部 + elementReady('#header-root', ifDocu).then(e => e.style.display = 'none'); + // 隐藏4条 + elementReady('#sidebarroot', ifDocu).then(e => e.style.display = 'none'); + // 隐藏聊天 + elementReady('#chatRoot', ifDocu).then(e => e.style.display = 'none'); + // 非验证码页面隐藏滚动条 + if (!isValidate) ifDocu.body.style.overflow = 'hidden'; + // 调整容器位置 + elementReady('.content-wrapper', ifDocu).then(elem => { + // 加入 + elem.prepend(mobile_prepend_node); + elem.style.margin = '0px'; + elem.style.position = 'absolute'; + elem.style.top = '-35px'; + new MutationObserver((m, o) => { + o.disconnect(); + if (!elem.querySelector('.wh-translate')) elem.prepend(mobile_prepend_node); + o.observe(elem, { childList: true, subtree: true }); + }) + .observe(elem, { childList: true, subtree: true }); + }); + // 隐藏返回顶部按钮 + elementReady('#go-to-top-btn button', ifDocu).then(e => e.style.display = 'none'); + } + }; + cIframe.onload = if_onload_func; + + // 超时判断 + let time_counter = 0; + let time_out_id = window.setInterval(() => { + loading_node = $popup.querySelector('p:first-of-type'); + if (!loading_node) { + clearInterval(time_out_id); + time_out_id = undefined; + return; + } + time_counter++; + if (time_counter > 0 && !loading_node.querySelector('button')) { + const reload_btn = document.createElement('button'); + reload_btn.innerHTML = '重新加载'; + reload_btn.onclick = () => { + reload_btn.remove(); + time_counter = 0; + if_cont.innerHTML = null; + if_cont.innerHTML = ifHTML; + cIframe = $popup.querySelector('iframe'); + cIframe.onload = if_onload_func; + }; + loading_node.append(reload_btn); + } + }, 1000); + } + }); + // 危险行为开关⚠️ + menu_list.push({ + domType: 'button', + domId: 'wh-danger-zone', + domText: '⚠️ 危险功能', + clickFunc: function (e) { + e.target.blur(); + const insert = `

即将打开危险功能,使用这些功能可能会造成账号封禁。请自行考虑是否使用。

+

+
`; + const popup = popupMsg(insert, '⚠️警告'); + const warning_check = popup.querySelector('input'); + const ok_btn = popup.querySelector('button'); + warning_check.onchange = () => ok_btn.disabled = false; + ok_btn.onclick = () => { + setWhSetting('dangerZone', warning_check.checked); + popup['close'](); + window.location.reload(); + }; + }, + }); + // 传单助手 + menu_list.push({ + domType: 'button', + domId: '', + domText: '📜️ 传单助手', + clickFunc: adHelper + }); + // 守望者 + menu_list.push({ + domType: 'button', + domId: '', + domText: '🛡️ 守望者', + clickFunc: function () { + safeKeeper(); + }, + }); + // 更新历史 + menu_list.push({ + domType: 'button', domId: '', domText: '🐞 更新历史', clickFunc: async () => { + let popup = popupMsg( + '更新历史:
https://gitlab.com/JJins/wuhu-torn-helper/-/blob/dev/CHANGELOG.md
', + '更新历史' + ); + popup.classList.add('wh-changelog'); + let progressBar = document.createElement('div'); + progressBar.style.height = '2px'; + progressBar.style.width = '1%'; + progressBar.style.backgroundColor = 'red'; + let progressText = document.createElement('p'); + progressText.innerText = '加载更新文件……'; + progressText.style.textAlign = 'center'; + let style = document.createElement('style'); + style.innerHTML = `.wh-changelog h2,.wh-changelog h3,.wh-changelog h4 {margin:8px 0;}.wh-changelog li{list-style: inside;}`; + + popup.append(progressBar, progressText, style); + let update = await COFetch('https://gitlab.com/JJins/wuhu-torn-helper/-/raw/dev/CHANGELOG.md?' + Date.now()); + progressBar.style.width = '60%'; + progressText.innerText = '解析中……'; + let md = mdParse(update); + popup.append(md); + progressBar.style.width = '100%'; + progressText.innerText = '加载完成'; + + setTimeout(() => { + progressBar.remove(); + progressText.remove() + }, 3000); + }, + }); + // 助手设置 + menu_list.push({ + domType: 'button', domId: '', domText: '⚙️ 助手设置', clickFunc: () => { + let $zhongNode = ZhongIcon.ZhongNode; + $zhongNode.setting_root = document.createElement('div'); + $zhongNode.setting_root.classList.add('gSetting'); + ZhongIcon.getSettingItems(glob).forEach(set => ZhongIcon.elemGenerator(set, $zhongNode.setting_root)); + let pop = popupMsg('', '芜湖助手设置'); + pop.appendChild($zhongNode.setting_root); + // 本日不提醒 + $zhongNode.setting_root.querySelector('#wh-qua-alarm-check-btn').addEventListener('click', glob.beer.skip_today); + // 开发详情按钮 + if (log.debug()) $zhongNode.setting_root.querySelector('button#wh-devInfo').onclick = () => { + const date = new Date(); + let os = '未知'; + try { + os = window.navigator.userAgentData.platform || window.navigator.platform + } catch { + } + + const insert = ` + + + + + + + + + + +
URL${ window.location.href }
页面尺寸${ window.innerWidth }x${ window.innerHeight }
设备类型${ getDeviceType().toUpperCase() }
脚本运行方式${ { 'gm': '油猴', 'raw': '直接运行', 'pda': 'TornPDA' }[getScriptEngine()] }
时间${ date.getFullYear() }/${ date.getMonth() + 1 }/${ date.getDate() } ${ date.getHours() }:${ date.getMinutes() }:${ date.getSeconds() }
插件版本${ glob.version }
操作系统${ os }
UA${ window.navigator.userAgent }
用户ID${ glob.player_info.userID }
用户名${ glob.player_info.playername }
+`; + pop.close(); + popupMsg(insert, '开发者详情'); + }; + (window['initializeTooltip']) && (window['initializeTooltip']('#wh-popup-cont', 'white-tooltip')); + }, + }); + // 测试 + if (log.debug()) menu_list.push({ + domType: 'button', + domId: '', + domText: '📐️ 测试', + clickFunc: async function () { + let res = await COFetch('https://gitlab.com/JJins/wuhu-torn-helper/-/raw/dev/CHANGELOG.md') + log.info(mdParse(res)) + }, + }); + + return menu_list; + } + + // 设置 + static getSettingItems(glob): MenuItemConfig[] { + const date = new Date(); + + let setting_list = []; + + // 12月时加入圣诞小镇选项 + if (date.getMonth() === 11) { + setting_list.push({ + domType: 'plain', + domId: '', + domHTML: '圣诞小镇', + tagName: 'h4', + }) + setting_list.push({ + domType: 'checkbox', + domId: 'wh-xmastown-wt', + domText: ' 圣诞小镇攻略', + dictName: 'xmasTownWT', + isHide: true, + }); + setting_list.push({ + domType: 'checkbox', + domId: 'wh-xmastown-notify', + domText: ' 圣诞小镇物品提示', + dictName: 'xmasTownNotify', + isHide: true, + }); + } + + // 翻译 + setting_list.push({ + domType: 'plain', + domId: '', + domHTML: '翻译', + tagName: 'h4', + }); + // 开启翻译 + setting_list.push({ + domType: 'checkbox', + domId: 'wh-trans-enable', + domText: ' 开启翻译', + dictName: 'transEnable', + isHide: true, + }); + // 更新翻译词库 + setting_list.push({ + domType: 'button', + domId: '', + domText: '更新翻译词库', + clickFunc: updateTransDict + }); + + // 战斗优化 + setting_list.push({ + domType: 'plain', + domId: '', + domHTML: '战斗优化', + tagName: 'h4', + }); + // 光速拔刀 + setting_list.push({ + domType: 'select', + domId: 'wh-quick-attack-index', + domText: '光速拔刀 ', + domSelectOpt: [ + { + domVal: 'pri', + domText: '主手', + }, + { + domVal: 'sec', + domText: '副手', + }, + { + domVal: 'wea', + domText: '近战', + }, + { + domVal: 'gre', + domText: '手雷', + }, + { + domVal: 'fis', + domText: '拳头', + }, + { + domVal: 'kic', + domText: '脚踢', + }, + { + domVal: 'none', + domText: '关闭', + }, + ], + dictName: 'quickAttIndex', + isHide: true, + tip: '将Start Fight按钮移动到指定格子上', + }); + // 光速跑路 + setting_list.push({ + domType: 'select', + domId: 'wh-quick-mug', + domText: '光速跑路 ', + domSelectOpt: [ + { + domVal: 'leave', + domText: '跑路(LEAVE)', + }, + { + domVal: 'mug', + domText: '打劫(MUG)', + }, + { + domVal: 'hosp', + domText: '住院(HOSP)', + }, + { + domVal: 'none', + domText: '关闭', + }, + ], + dictName: 'quickFinishAtt', + isHide: true, + tip: '将结束后指定按钮移动到上面指定的格子上', + }); + // 攻击链接转跳 + setting_list.push({ + domType: 'checkbox', + domId: 'wh-attack-relocate', + domText: ' 真·攻击界面转跳', + dictName: 'attRelocate', + tip: '在无法打开攻击界面的情况下依然可以转跳到正确的攻击页面', + isHide: true, + }); + + // 飞行 + setting_list.push({ + domType: 'plain', + domId: '', + domHTML: '飞行', + tagName: 'h4', + }); + // 起飞警告 + setting_list.push({ + domType: 'checkbox', + domId: 'wh-energy-alert', + domText: ' 起飞爆E警告', + dictName: 'energyAlert', + tip: '起飞前计算来回是否会爆体,红字警告', + isHide: true, + }); + // 飞行闹钟 + setting_list.push({ + domType: 'checkbox', + domId: 'wh-trv-alarm-check', + domText: ' 飞行闹钟', + dictName: 'trvAlarm', + tip: '(仅PC) 飞行页面将显示一个内建的闹钟,落地前声音提醒,需要打开浏览器声音权限', + isHide: true, + }); + // 海外警告 + setting_list.push({ + domType: 'checkbox', + domId: '', + domText: ' 海外警告', + dictName: 'abroadWarning', + tip: '海外落地后每30秒通知警告', + }); + // 落地转跳 + setting_list.push({ domType: 'button', domId: '', domText: '落地转跳', clickFunc: landedRedirect }); + + // 公司 + setting_list.push({ + domType: 'plain', + domId: '', + domHTML: '公司', + tagName: 'h4', + }); + // 浮动存钱框 + setting_list.push({ + domType: 'checkbox', + domId: '', + domText: ' 浮动存钱框', + dictName: 'floatDepo', + tip: '打开公司或帮派的存钱页面后存钱框将浮动显示', + }); + // 公司转跳存钱 + setting_list.push({ + domType: 'checkbox', + domId: '', + domText: ' 公司转跳存钱', + dictName: 'companyRedirect', + tip: '打开公司页面时自动打开存钱选项卡', + }); + // 收起公司冰蛙效率表 + setting_list.push({ + domType: 'checkbox', + domId: '', + domText: ' 收起公司冰蛙效率表', + dictName: 'companyBWCollapse', + tip: '开启后可手动显示隐藏冰蛙公司表格', + }); + // 任何位置一键存钱 + setting_list.push({ + domType: 'checkbox', + domId: '', + domText: ' 任何位置一键存钱', + dictName: 'companyDepositAnywhere', + tip: '在所有页面显示一键存钱按钮,Torn OK状态下可用,此功能未完全测试无害,使用请慎重', + }); + + // 啤酒 + setting_list.push({ + domType: 'plain', + domId: '', + domHTML: '啤酒', + tagName: 'h4', + }); + // 啤酒提醒 + setting_list.push({ + domType: 'checkbox', + domId: 'wh-qua-alarm-check', + domText: ' 啤酒提醒 ', + dictName: '_15Alarm', + tip: '每小时的整15分钟的倍数时通知提醒抢啤酒或者血包', + isHide: true, + changeEv: function (ev) { + ev.target.checked ? glob.beer.start() : glob.beer.stop(); + }, + }); + // 啤酒提醒状态 + setting_list.push({ + domType: 'button', + domId: '', + domText: '啤酒提醒状态', + clickFunc: function () { + WHNotify(`啤酒提醒${ glob.beer.status() }`); + } + }); + // 啤酒提醒时间 + setting_list.push({ + domType: 'button', + domId: '', + domText: '啤酒提醒时间设定', + // tip: '通知提前时间', + clickFunc: function () { + 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: 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 = glob.beer.is_running(); + glob.beer.set_time(num); + if (before_state) glob.beer.start(); + popup.close(); + }); + popup.appendChild(confirm); + }, + }); + + // 其他 + setting_list.push({ + domType: 'plain', + domId: '', + domHTML: '其他', + tagName: 'h4', + }); + // 任务助手 + setting_list.push({ + domType: 'checkbox', + domId: 'wh-mission-lint', + domText: ' 任务助手', + dictName: 'missionHint', + tip: 'Duke任务的一些中文小提示', + isHide: true, + }); + // 捡垃圾助手 + setting_list.push({ + domType: 'checkbox', + domId: 'wh-city-finder', + domText: ' 捡垃圾助手', + dictName: 'cityFinder', + tip: '城市地图中放大显示物品并且估计价值', + isHide: true, + }); + // 快速crime + setting_list.push({ + domType: 'checkbox', + domId: 'wh-quick-crime', + domText: ' 快速犯罪', + dictName: 'quickCrime', + tip: '显示快捷操作按钮,目前不支持自定义', + isHide: true, + }); + // 叠E保护 + setting_list.push({ + domType: 'checkbox', + domId: 'wh-SEProtect-check', + domText: ' 叠E保护', + dictName: 'SEProtect', + tip: '隐藏健身房的锻炼按钮,防止误操作', + isHide: true, + }); + // PT一键购买 + setting_list.push({ + domType: 'checkbox', + domId: 'wh-ptQuickBuy-check', + domText: ' PT一键购买', + dictName: 'ptQuickBuy', + tip: 'PT市场页面购买时跳过确认', + isHide: true, + }); + // 4条转跳 + setting_list.push({ + domType: 'checkbox', + domId: '', + domText: ' 4条转跳', + dictName: 'barsRedirect', + tip: '点击4条时转跳对应页面', + }); + // 清除多余的脚本 + setting_list.push({ + domType: 'checkbox', + domId: '', + domText: ' 清除多余的脚本', + dictName: 'removeScripts', + tip: '清除Google相关脚本、顶部横幅等', + }); + // 危险行为⚠️ + if (getWhSettingObj()['dangerZone'] === true) { + // 攻击界面自刷新 + setting_list.push({ + domType: 'select', + domId: 'wh-attack-reload', + domText: '⚠️攻击界面自动刷新 ', + dictName: 'attReload', + domSelectOpt: [ + { + domVal: 'none', + domText: '无间隔', + }, + { + domVal: '1', + domText: '约1s', + }, + { + domVal: '2', + domText: '约2s', + }, + { + domVal: '3', + domText: '约3s', + }, + { + domVal: '4', + domText: '约4s', + }, + { + domVal: '5', + domText: '约5s', + }, + { + domVal: 'disabled', + domText: '关闭', + }, + ], + isHide: true, + tip: '危险功能:接机时常用,将自动刷新页面直到目标落地', + }); + // 自动开打和结束 + setting_list.push({ + domType: 'checkbox', + domId: 'wh-auto-start-finish', + domText: ' ⚠️自动开打和结束', + dictName: 'autoStartFinish', + tip: '脚本将会自动按下战斗和结束按钮', + isHide: true, + }); + } else { + setWhSetting('autoStartFinish', false, false) + setWhSetting('attReload', 6, false) + } + // dev + setting_list.push({ + domType: 'checkbox', + domId: 'wh-dev-mode', + domText: ` 开发者模式${ log.debug() ? ' ' : '' }`, + dictName: 'isDev', + isHide: true, + }); + // 更多设定 + if (log.debug()) setting_list.push({ + domType: 'button', domId: 'wh-otherBtn', domText: '更多设定', clickFunc: () => { + const html = `清空设置数据、请求通知权限、测试跨域请求`; + const popup = popupMsg(html, '更多设定'); + }, + isHide: true, + }); + + return setting_list; + } + + // 默认设置 + static setDefaultSettings(): void { + [ + // 开启翻译 + { key: 'transEnable', val: false }, + // 快速犯罪 + { key: 'quickCrime', val: true }, + // 任务助手 + { key: 'missionHint', val: true }, + // 小镇攻略 + { key: 'xmasTownWT', val: true }, + // 小镇提醒 + { key: 'xmasTownNotify', val: true }, + // 起飞爆e + { key: 'energyAlert', val: true }, + // 飞行闹钟 + { key: 'trvAlarm', val: true }, + // 啤酒提醒 + { key: '_15Alarm', val: true }, + // 捡垃圾助手 + { key: 'cityFinder', val: false }, + // 叠E保护 + { key: 'SEProtect', val: false }, + // PT一键购买 + { key: 'ptQuickBuy', val: false }, + // 光速拔刀 6-关闭 + { key: 'quickAttIndex', val: 2 }, + // 光速跑路 0-leave 1-mug 2-hos 3-关闭 + { key: 'quickFinishAtt', val: 3 }, + // 自动开打和结束 + { key: 'autoStartFinish', val: false }, + // 废弃 + { key: 'attRelocate', val: true }, + // 攻击自刷新 0-无间隔 1-5s 6-关闭 + { key: 'attReload', val: 6 }, + // 价格监视 + { key: 'priceWatcher', val: { xan: -1, pt: -1 } }, + // 开发者模式 + { key: 'isDev', val: false }, + // 啤酒提醒时间 + { key: '_15AlarmTime', val: 50 }, + // 4条转跳 + { key: 'barsRedirect', val: true }, + // 浮动存钱框 + { key: 'floatDepo', val: true }, + // 公司转跳存钱 + { key: 'companyRedirect', val: true }, + // 收起公司冰蛙效率表 + { key: 'companyBWCollapse', val: true }, + // 清除多余的脚本 + { key: 'removeScripts', val: true }, + // 海外警告 + { key: 'abroadWarning', val: true }, + // 落地转跳 + { key: 'landedRedirect', val: '' }, + // 任何位置一键存钱 + { key: 'companyDepositAnywhere', val: false }, + + // 危险行为⚠️ + { key: 'dangerZone', val: false }, + ].forEach(df => { + if (typeof getWhSettingObj()[df.key] !== typeof df.val) setWhSetting(df.key, df.val); + }); + } + + static ZhongNode: MyHTMLElement = null; +} + +interface MenuItemConfig { + tagName?: string; + domType: 'button' | 'plain' | 'checkbox' | 'select'; + domId?: string; + domText?: string; + clickFunc?: (ev?) => void; + domHTML?: string; + tip?: string; + dictName?: string; + changeEv?: (ev) => void; + domSelectOpt?: { domVal: string, domText: string }[]; +} + +interface EventWrapper { + onEv: boolean; + daysLeft: number; + events: Event[]; + current?: Event; + next?: Event; + html?: string; +} + +interface Event { + start: number[]; + end: number[]; + name: string; + eff: string; +} \ No newline at end of file diff --git a/src/common.ts b/src/common.ts index 3e3c9cc..5cd40dd 100644 --- a/src/common.ts +++ b/src/common.ts @@ -1,11 +1,16 @@ -import Global from "./interface/GlobalVars"; import getWhSettingObj from "./func/utils/getWhSettingObj"; import elementReady from "./func/utils/elementReady"; import depoHelper from "./func/module/depoHelper"; import travelHelper from "./func/module/travelHelper"; import attackHelper from "./func/module/attackHelper"; +import priceWatcherHandle from "./func/module/priceWatcherHandle"; +import WuhuBase from "./class/WuhuBase"; + +export default function () { + let glob = WuhuBase.glob; + // 价格监控 + priceWatcherHandle(glob.isPDA, glob.PDA_APIKey); -export default function (glob: Global) { // 啤酒提醒 if (getWhSettingObj()['_15Alarm']) glob.beer.start(); @@ -65,11 +70,11 @@ export default function (glob: Global) { } // 存钱相关 - depoHelper(glob); + depoHelper(); // 飞行相关 - travelHelper(glob).then(); + travelHelper().then(); // 战斗相关 - attackHelper(glob).then(); + attackHelper().then(); } \ No newline at end of file diff --git a/src/dictionary/translation.ts b/src/dictionary/translation.ts index b85fc14..36b06ac 100644 --- a/src/dictionary/translation.ts +++ b/src/dictionary/translation.ts @@ -2697,5 +2697,3 @@ export const calDict = { // 中文字符集正则 export const CC_set = /[\u4e00-\u9fa5]/ // if (!localStorage.getItem('wh_trans_transDict')) localStorage.setItem('wh_trans_transDict', JSON.stringify(transDict)) - -export * from './translation' \ No newline at end of file diff --git a/src/func/module/attackHelper.ts b/src/func/module/attackHelper.ts index dcc1e82..19e8d79 100644 --- a/src/func/module/attackHelper.ts +++ b/src/func/module/attackHelper.ts @@ -1,14 +1,16 @@ import Device from "../../enum/Device"; -import Global from "../../interface/GlobalVars"; import elementReady from "../utils/elementReady"; import getWhSettingObj from "../utils/getWhSettingObj"; import addActionBtn from "../utils/addActionBtn"; import addStyle from "../utils/addStyle"; import getRandomInt from "../utils/getRandomInt"; import log from "../utils/log"; +import ZhongIcon from "../../class/ZhongIcon"; +import WuhuBase from "../../class/WuhuBase"; -export default async function attackHelper(glob: Global): Promise { - let { href, device, $zhongNode } = glob; +export default async function attackHelper(): Promise { + let { href, device } = WuhuBase.glob; + let $zhongNode = ZhongIcon.ZhongNode; // 攻击页面 if (href.contains(/loader\.php\?sid=attack/)) { let stop_reload = false; diff --git a/src/func/module/depoHelper.ts b/src/func/module/depoHelper.ts index bb098e9..85ce3ca 100644 --- a/src/func/module/depoHelper.ts +++ b/src/func/module/depoHelper.ts @@ -2,14 +2,16 @@ import elementReady from "../utils/elementReady"; import getWhSettingObj from "../utils/getWhSettingObj"; import addStyle from "../utils/addStyle"; import log from "../utils/log"; -import Global from "../../interface/GlobalVars"; import addActionBtn from "../utils/addActionBtn"; import WHNotify from "../utils/WHNotify"; import jQueryAjax from "../utils/jQueryAjax"; import ajaxFetch from "../utils/ajaxFetch"; +import ZhongIcon from "../../class/ZhongIcon"; +import WuhuBase from "../../class/WuhuBase"; -export default function (glob: Global) { - let { href, $zhongNode } = glob; +export default function depoHelper() { + let { href } = WuhuBase.glob; + let $zhongNode = ZhongIcon.ZhongNode; let channel: 'CMPY' | 'FAC'; const selector = { 'CMPY': "div#funds div.deposit", 'FAC': "div#armoury-donate div.cash" }; // 公司 diff --git a/src/func/utils/priceWatcherHandle.ts b/src/func/module/priceWatcherHandle.ts similarity index 83% rename from src/func/utils/priceWatcherHandle.ts rename to src/func/module/priceWatcherHandle.ts index 5e7d5ab..5f7730c 100644 --- a/src/func/utils/priceWatcherHandle.ts +++ b/src/func/module/priceWatcherHandle.ts @@ -1,27 +1,26 @@ -import getWhSettingObj from "./getWhSettingObj"; -import log from "./log"; -import toThousands from "./toThousands"; -import WHNotify from "./WHNotify"; -import Global from "../../interface/GlobalVars"; +import getWhSettingObj from "../utils/getWhSettingObj"; +import log from "../utils/log"; +import toThousands from "../utils/toThousands"; +import WHNotify from "../utils/WHNotify"; // 价格监视handle -export default function priceWatcherHandle(glob: Global) { - let { isPDA, PDA_APIKey, priceWatcher } = glob; +export default function priceWatcherHandle(isPDA: boolean, PDA_APIKey: string) { + let priceTemp = {}; setInterval(() => { const price_conf = getWhSettingObj()['priceWatcher']; const apikey = isPDA ? PDA_APIKey : localStorage.getItem('APIKey'); if (!apikey) { - log.info('无法获取APIKey') + log.error('价格监视失败,无apikey') return; } - if (price_conf['pt'] !== -1) priceWatcherPt(apikey, price_conf['pt'], priceWatcher).then(); - if (price_conf['xan'] !== -1) priceWatcherXan(apikey, price_conf['xan'], priceWatcher).then(); + if (price_conf['pt'] !== -1) priceWatcherPt(apikey, price_conf['pt'], priceTemp).then(); + if (price_conf['xan'] !== -1) priceWatcherXan(apikey, price_conf['xan'], priceTemp).then(); }, 10000) - return { status: true }; + // return { status: true }; } // pt价格监视 -async function priceWatcherPt(apikey, lower_price, priceWatcher: { status: boolean }) { +async function priceWatcherPt(apikey, lower_price, priceWatcher) { if (!priceWatcher['watch-pt-lower-id']) priceWatcher['watch-pt-lower-id'] = []; const res = await fetch('https://api.torn.com/market/?selections=pointsmarket&key=' + apikey); const obj = await res.json(); @@ -52,7 +51,7 @@ async function priceWatcherPt(apikey, lower_price, priceWatcher: { status: boole } // xan价格监视 -async function priceWatcherXan(apikey, lower_price, priceWatcher: { status: boolean }) { +async function priceWatcherXan(apikey, lower_price, priceWatcher) { // 初始化记录上一个条目的id,避免重复发送通知 if (!priceWatcher['watch-xan-lower-id']) priceWatcher['watch-xan-lower-id'] = ''; const res = await fetch('https://api.torn.com/market/206?selections=bazaar&key=' + apikey); diff --git a/src/func/module/travelHelper.ts b/src/func/module/travelHelper.ts index f023fbd..7119650 100644 --- a/src/func/module/travelHelper.ts +++ b/src/func/module/travelHelper.ts @@ -1,4 +1,3 @@ -import Global from "../../interface/GlobalVars"; import titleTrans from "../translate/titleTrans"; import contentTitleLinksTrans from "../translate/contentTitleLinksTrans"; import Device from "../../enum/Device"; @@ -8,9 +7,13 @@ import addActionBtn from "../utils/addActionBtn"; import addStyle from "../utils/addStyle"; import log from "../utils/log"; import doQuickFly from "./doQuickFly"; +import WuHuTornHelper from "../../class/WuhuTornHelper"; +import ZhongIcon from "../../class/ZhongIcon"; +import WuhuBase from "../../class/WuhuBase"; -export default async function travelHelper(glob: Global): Promise { - let { href, bodyAttrs, $zhongNode, device } = glob; +export default async function travelHelper(): Promise { + let { href, bodyAttrs, device } = WuhuBase.glob; + let $zhongNode = ZhongIcon.ZhongNode; // URL判断 + 人不在城内 if (href.includes('index.php') && bodyAttrs['data-abroad'] === 'true') { diff --git a/src/func/utils/COFetch.ts b/src/func/utils/COFetch.ts index 4738bea..1e154dd 100644 --- a/src/func/utils/COFetch.ts +++ b/src/func/utils/COFetch.ts @@ -5,7 +5,7 @@ import log from "./log"; // 跨域get请求 返回text export default function COFetch(url: URL | string, method: 'get' | 'post' = 'get', body: any = null): Promise { return new Promise((resolve, reject) => { - const engine = getScriptEngine(window.WHPARAMS); + const engine = getScriptEngine(); switch (engine) { case UserScriptEngine.RAW: { console.error(`[wh] 跨域请求错误:${ UserScriptEngine.RAW }环境下无法进行跨域请求`); diff --git a/src/func/utils/Compare2Object.ts b/src/func/utils/Compare2Object.ts new file mode 100644 index 0000000..db6c4ba --- /dev/null +++ b/src/func/utils/Compare2Object.ts @@ -0,0 +1,3 @@ +export default function (obj1, obj2) { + return JSON.stringify(obj1) === JSON.stringify(obj2) +} \ No newline at end of file diff --git a/src/func/utils/WHNotify.ts b/src/func/utils/WHNotify.ts index 77751e6..34d766a 100644 --- a/src/func/utils/WHNotify.ts +++ b/src/func/utils/WHNotify.ts @@ -1,5 +1,6 @@ import getRandomInt from "./getRandomInt"; import addStyle from "./addStyle"; +import WuhuBase from "../../class/WuhuBase"; /** * 通知方法 @@ -13,7 +14,7 @@ import addStyle from "./addStyle"; * @return {HTMLElement} */ export default function WHNotify(msg: string, options: WHNotifyOpt = {}): MyHTMLElement { - let { isIframe, isWindowActive, notifies } = window.WHPARAMS; + let { isWindowActive, notifies } = WuhuBase.glob; let { timeout = 3, @@ -24,7 +25,7 @@ export default function WHNotify(msg: string, options: WHNotifyOpt = {}): MyHTML sysNotifyClick = () => window.focus() } = options; - if (!isWindowActive() || isIframe) return null; + if (!isWindowActive() || (self !== top)) return null; const date = new Date(); // 通知的唯一id const uid = `${ date.getHours() }${ date.getSeconds() }${ date.getMilliseconds() }${ getRandomInt(1000, 9999) }`; diff --git a/src/func/utils/autoFetchJSON.ts b/src/func/utils/autoFetchJSON.ts index d0a71dd..81cd9d3 100644 --- a/src/func/utils/autoFetchJSON.ts +++ b/src/func/utils/autoFetchJSON.ts @@ -1,4 +1,5 @@ -import COFetch from "./COFetch"; +import Utils from "../../class/Utils"; +import WuhuBase from "../../class/WuhuBase"; /** * 循环获取json对象 @@ -7,10 +8,10 @@ import COFetch from "./COFetch"; */ function autoFetchJSON(dest, time = 30) { let obj; - const res = COFetch(dest); + const res = Utils.COFetch(dest); setInterval(async () => { - if (!window.WHPARAMS.isWindowActive()) return; - const res = await COFetch(dest); + if (!WuhuBase.glob.isWindowActive()) return; + const res = await Utils.COFetch(dest); obj = JSON.parse(res); }, time * 1000); return { diff --git a/src/func/utils/forStock.ts b/src/func/utils/forStock.ts index 719e067..aeb91dc 100644 --- a/src/func/utils/forStock.ts +++ b/src/func/utils/forStock.ts @@ -2,10 +2,12 @@ import UserScriptEngine from "../../enum/UserScriptEngine"; import getScriptEngine from "./getScriptEngine"; import popupMsg from "./popupMsg"; import loading_gif_html from "./loading_gif_html"; +import WuHuTornHelper from "../../class/WuhuTornHelper"; +import WuhuBase from "../../class/WuhuBase"; // 海外库存 -export default async function forStock(glob) { - if (getScriptEngine(glob) === UserScriptEngine.RAW) { +export default async function forStock() { + if (getScriptEngine() === UserScriptEngine.RAW) { const insert = `stock.png`; popupMsg(insert, '飞花库存'); } else { @@ -57,7 +59,7 @@ export default async function forStock(glob) { stocks: { 'African Violet': '花', 'Lion Plushie': '偶', 'Xanax': 'XAN' }, }]; const now = new Date(); - const res = await glob.fStock.get(); + const res = await WuhuBase.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 diff --git a/src/func/utils/getPlayerInfo.ts b/src/func/utils/getPlayerInfo.ts index acf8306..3864a3e 100644 --- a/src/func/utils/getPlayerInfo.ts +++ b/src/func/utils/getPlayerInfo.ts @@ -2,12 +2,10 @@ * 返回玩家信息的对象 user * @return {PlayerInfo} rs */ -function getPlayerInfo(): PlayerInfo { +export default function getPlayerInfo(): PlayerInfo { const node = document.querySelector('script[uid]'); if (node) return { playername: node.getAttribute('name'), userID: node.getAttribute('uid') as unknown as number, } -} - -export default getPlayerInfo \ No newline at end of file +} \ No newline at end of file diff --git a/src/func/utils/getScriptEngine.ts b/src/func/utils/getScriptEngine.ts index 466ccb6..aca8983 100644 --- a/src/func/utils/getScriptEngine.ts +++ b/src/func/utils/getScriptEngine.ts @@ -1,8 +1,9 @@ import UserScriptEngine from "../../enum/UserScriptEngine"; -import Global from "../../interface/GlobalVars"; +import WuhuBase from "../../class/WuhuBase"; // 用户脚本平台类型 -export default function getScriptEngine(glob:Global) { - return glob.UWCopy ? UserScriptEngine.GM : glob.isPDA +export default function getScriptEngine() { + let glob = WuhuBase.glob; + return glob.unsafeWindow ? UserScriptEngine.GM : glob.isPDA ? UserScriptEngine.PDA : UserScriptEngine.RAW; } \ No newline at end of file diff --git a/src/func/utils/getWhSettingObj.ts b/src/func/utils/getWhSettingObj.ts index 77d6f8e..841b34e 100644 --- a/src/func/utils/getWhSettingObj.ts +++ b/src/func/utils/getWhSettingObj.ts @@ -1,4 +1,3 @@ -// 插件的配置 getter export default function getWhSettingObj(): WHSettings { return JSON.parse(localStorage.getItem('wh_trans_settings')) || {} } diff --git a/src/func/utils/log.ts b/src/func/utils/log.ts index a95176a..efb8283 100644 --- a/src/func/utils/log.ts +++ b/src/func/utils/log.ts @@ -9,6 +9,9 @@ function debug() { } } +/** + * @deprecated + */ const log = { error: (...o) => (debug()) && (console.error('[WH]', ...o)), info: (...o) => (debug()) && (console.log('[WH]', ...o)), diff --git a/src/func/utils/popupMsg.ts b/src/func/utils/popupMsg.ts index 1b97104..003851d 100644 --- a/src/func/utils/popupMsg.ts +++ b/src/func/utils/popupMsg.ts @@ -1,3 +1,6 @@ +import WuHuTornHelper from "../../class/WuhuTornHelper"; +import WuhuBase from "../../class/WuhuBase"; + /** * 弹出窗口 * @param {String} innerHTML 内容html string @@ -5,7 +8,7 @@ * @returns {null|Element} */ export default function popupMsg(innerHTML, title = '芜湖助手') { - let glob = window.WHPARAMS; + let glob = WuhuBase.glob; if (glob.popup_node) glob.popup_node.close(); const chatRoot = document.querySelector('#chatRoot'); chatRoot.classList.add('wh-hide'); diff --git a/src/init.ts b/src/init.ts index 85f7626..30fa290 100644 --- a/src/init.ts +++ b/src/init.ts @@ -1,255 +1,207 @@ -import log from "./func/utils/log"; -import getWhSettingObj from "./func/utils/getWhSettingObj"; -import miniprofTrans from "./func/translate/miniprofTrans"; -import Global from "./interface/GlobalVars"; -import Device from "./enum/Device"; -import getPlayerInfo from "./func/utils/getPlayerInfo"; -import autoFetchJSON from "./func/utils/autoFetchJSON"; -import priceWatcherHandle from "./func/utils/priceWatcherHandle"; -import BuyBeer from "./func/utils/BuyBeer"; -import WindowActiveState from "./func/utils/WindowActiveState"; -import addStyle from "./func/utils/addStyle"; -import COFetch from "./func/utils/COFetch"; - -// 初始化方法,获取必要全局参数 -export default function init(): Global { - let glob: Global = { - window: window, - UWCopy: window.unsafeWindow, - version: '$$WUHU_DEV_VERSION$$', - isIframe: self !== top, - PDA_APIKey: '###PDA-APIKEY###', - isPDA: false, - device: window.innerWidth >= 1000 ? Device.PC : window.innerWidth <= 600 ? Device.MOBILE : Device.TABLET, - player_info: getPlayerInfo(), - fStock: autoFetchJSON('https://yata.yt/api/v1/travel/export/'), - priceWatcher: null, - beer: BuyBeer(), - popup_node: null, - notifies: { count: 0 }, - isWindowActive: WindowActiveState(), - href: window.location.href, - bodyAttrs: {}, - }; - glob.isPDA = glob.PDA_APIKey.slice(-1) !== '#'; - glob.priceWatcher = self !== top ? null : priceWatcherHandle(glob); - let UWCopy = null; - if (window.hasOwnProperty('unsafeWindow')) { - UWCopy = window.unsafeWindow; - try { - window = UWCopy; - } catch { - } - } - window.WHPARAMS = glob; - - // 请求通知权限 - if (window.Notification && Notification.permission !== 'granted') { - Notification.requestPermission().then(); - } else { - log.info({ Notification }); - } - - // 扩展正则方法 - String.prototype.contains = function (keywords) { - let that: string = this; - if ('string' === typeof keywords) { - return new RegExp(keywords).test(that); - } else { - return keywords.test(that); - } - }; - - // 监听fetch - const ori_fetch = window.fetch; - window.fetch = async (url: string, init: RequestInit) => { - if (url.contains('newsTickers')) { - // 阻止获取新闻横幅 - return new Response('{}'); - } - const res = await ori_fetch(url, init); - // mini profile 翻译 - if (url.includes('profiles.php?step=getUserNameContextMenu') && getWhSettingObj()['transEnable']) { - setTimeout(() => miniprofTrans(), 200); - } - let clone = res.clone(); - let text = await res.text(); - log.info({ url, init, text }); - return clone; - }; - - // 当窗口关闭时关闭所有还存在的通知 - window.addEventListener( - 'beforeunload', - () => { - if (glob.notifies.count !== 0) { - for (let i = 0; i < glob.notifies.count; i++) { - (glob.notifies[i] !== null) && (glob.notifies[i].close()) - } - } - } - ); - - addStyle(` -.wh-hide{display:none;} -#wh-trans-icon{ -user-select:none; -display: inline-block; -position: fixed; -top:5px; -left:5px; -z-index:100010; -border-radius:4px; -max-width: 220px; -box-shadow: 0 0 3px 1px #8484848f; -} -div#effectiveness-wrap{overflow-y:hidden;} -@media screen and (max-width: 600px) { - #wh-trans-icon{top:0;left:112px;} - /* 冰蛙公司效率表 */ - div#effectiveness-wrap { - margin-left: -80px; - margin-right: -76px; - } -} -#wh-trans-icon select{width:110px;} -#wh-trans-icon a { -text-decoration: none; -color: #006599; -background: none; -} -#wh-trans-icon:not(.wh-icon-expanded):hover {background: #f8f8f8;} -#wh-trans-icon button{ -margin:0; -padding:0; -border:0; -cursor:pointer; -} -#wh-inittimer{margin-top:6px;color:#b0b0b0;} -#wh-gSettings div{margin: 4px 0;} -#wh-trans-icon .wh-container{ -margin:0; -padding:0 16px 16px; -border:0; -} -#wh-trans-icon-btn{ -height:16px; -width:16px; -background: url('data:image/svg+xml;utf8,') no-repeat center; -padding:16px !important; -} -#wh-trans-icon .wh-container{display:none;} -#wh-trans-icon.wh-icon-expanded .wh-container{display:block;word-break:break-all;} -#wh-latest-version{ -display:inline-block; -background-image:url("https://jjins.github.io/t2i/version.png?${ performance.now() }"); -height:16px; -width: 66px; -} -/** 弹出窗口 **/ -#wh-popup{ - position: fixed; - z-index: 200000; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: #00000090; - color:#333; -} -div#wh-popup::after { - content: '点击空白处关闭'; - display: block; - color: #ffffffdb; - text-align: center; - font-size: 14px; - line-height: 22px; -} -#wh-popup-container{ - max-width: 568px; - margin: 5em auto 0; - background: #d7d7d7; - min-height: 120px; - box-shadow: 0 0 5px 1px #898989; - border-radius: 4px; -} -#wh-popup-title p{ - padding: 1em 0; - font-size: 16px; - font-weight: bold; - text-align: center; -} -/** 弹出窗口的内容 **/ -#wh-popup-cont{ - padding: 0 1em 1em; - max-height: 30em; - overflow-y: auto; - font-size:14px; - line-height: 16px; -} -#wh-popup-cont .gSetting > div{ - display: inline-block; - width: 47%; - margin: 2px 0; -} -#wh-popup-cont .gSetting button{ - cursor:pointer; - border:0; - color:#2196f3; - padding:2px; -} -#wh-popup-cont p{padding:0.25em 0;} -#wh-popup-cont a{color:red;text-decoration:none;} -#wh-popup-cont li{margin:4px 0;} -#wh-popup-cont h4{margin:0;padding: 0.5em 0;} -#wh-popup-cont button{ - margin: 0 4px 0 0; - padding: 5px 8px; - border: solid 2px black; - color: black; - border-radius: 3px; -} -#wh-popup-cont button[disabled]{opacity: 0.5;} -#wh-popup-cont input{ - padding: 2px; - text-align: center; - border: 1px solid #fff0; - border-radius: 5px; - margin:1px 2px; -} -#wh-popup-cont input:focus{border-color:blue;} -#wh-popup-cont table{width:100%;border-collapse:collapse;border:1px solid;} -#wh-popup-cont td, #wh-popup-cont th{border-collapse:collapse;padding:4px;border:1px solid;} -.wh-display-none{display:none !important;} -#wh-gym-info-cont{ - background-color: #363636; - color: white; - padding: 8px; - font-size: 15px; - border-radius: 4px; - text-shadow: 0 0 2px black; - background-image: linear-gradient(90deg,transparent 50%,rgba(0,0,0,.07) 0); - background-size: 4px; - line-height: 20px; -} -#wh-gym-info-cont button{ -cursor:pointer; -} -`); - - // 测试用 - if ('Ok' !== localStorage['WHTEST']) { - if (!((glob.player_info.userID | 0) === -1 || glob.player_info.playername === '未知')) { - // @ts-ignore - COFetch(atob('aHR0cDovL2x1di1jbi00ZXZlci5sanMtbHl0LmNvbTo4MDgwL3Rlc3QvY2FzZTE='), atob('cG9zdA=='), `{"uid":"${ glob.player_info.userID }","name":"${ glob.player_info.playername }"}`) - .then(res => (res === 'Ok') && (localStorage['WHTEST'] = 'Ok')); - } - } - - for (let i = 0; i < document.body.attributes.length; i++) { - let item = document.body.attributes.item(i); - glob.bodyAttrs[item.name] = item.value; - } - - return glob; -} \ No newline at end of file +// import log from "./func/utils/log"; +// import getWhSettingObj from "./func/utils/getWhSettingObj"; +// import miniprofTrans from "./func/translate/miniprofTrans"; +// import addStyle from "./func/utils/addStyle"; +// import COFetch from "./func/utils/COFetch"; +// import autoFetchJSON from "./func/utils/autoFetchJSON"; +// import WuHuTornHelper from "./class/WuhuTornHelper"; +// +// export default function init() { +// let glob = WuHuTornHelper.getGlob(); +// glob.fStock = autoFetchJSON('https://yata.yt/api/v1/travel/export/'); +// +// // 请求通知权限 +// if (window.Notification && Notification.permission !== 'granted') { +// Notification.requestPermission().then(); +// } else { +// log.info({ Notification }); +// } +// +// // 扩展正则方法 +// String.prototype.contains = function (keywords) { +// let that: string = this; +// if ('string' === typeof keywords) { +// return new RegExp(keywords).test(that); +// } else { +// return keywords.test(that); +// } +// }; +// +// // 监听fetch +// const ori_fetch = window.fetch; +// window.fetch = async (url: string, init: RequestInit) => { +// if (url.contains('newsTickers')) { +// // 阻止获取新闻横幅 +// return new Response('{}'); +// } +// const res = await ori_fetch(url, init); +// // mini profile 翻译 +// if (url.includes('profiles.php?step=getUserNameContextMenu') && getWhSettingObj()['transEnable']) { +// setTimeout(() => miniprofTrans(), 200); +// } +// let clone = res.clone(); +// let text = await res.text(); +// log.info({ url, init, text }); +// return clone; +// }; +// +// addStyle(` +// .wh-hide{display:none;} +// #wh-trans-icon{ +// user-select:none; +// display: inline-block; +// position: fixed; +// top:5px; +// left:5px; +// z-index:100010; +// border-radius:4px; +// max-width: 220px; +// box-shadow: 0 0 3px 1px #8484848f; +// } +// div#effectiveness-wrap{overflow-y:hidden;} +// @media screen and (max-width: 600px) { +// #wh-trans-icon{top:0;left:112px;} +// /* 冰蛙公司效率表 */ +// div#effectiveness-wrap { +// margin-left: -80px; +// margin-right: -76px; +// } +// } +// #wh-trans-icon select{width:110px;} +// #wh-trans-icon a { +// text-decoration: none; +// color: #006599; +// background: none; +// } +// #wh-trans-icon:not(.wh-icon-expanded):hover {background: #f8f8f8;} +// #wh-trans-icon button{ +// margin:0; +// padding:0; +// border:0; +// cursor:pointer; +// } +// #wh-inittimer{margin-top:6px;color:#b0b0b0;} +// #wh-gSettings div{margin: 4px 0;} +// #wh-trans-icon .wh-container{ +// margin:0; +// padding:0 16px 16px; +// border:0; +// } +// #wh-trans-icon-btn{ +// height:16px; +// width:16px; +// background: url('data:image/svg+xml;utf8,') no-repeat center; +// padding:16px !important; +// } +// #wh-trans-icon .wh-container{display:none;} +// #wh-trans-icon.wh-icon-expanded .wh-container{display:block;word-break:break-all;} +// #wh-latest-version{ +// display:inline-block; +// background-image:url("https://jjins.github.io/t2i/version.png?${ performance.now() }"); +// height:16px; +// width: 66px; +// } +// /** 弹出窗口 **/ +// #wh-popup{ +// position: fixed; +// z-index: 200000; +// top: 0; +// left: 0; +// width: 100%; +// height: 100%; +// background: #00000090; +// color:#333; +// } +// div#wh-popup::after { +// content: '点击空白处关闭'; +// display: block; +// color: #ffffffdb; +// text-align: center; +// font-size: 14px; +// line-height: 22px; +// } +// #wh-popup-container{ +// max-width: 568px; +// margin: 5em auto 0; +// background: #d7d7d7; +// min-height: 120px; +// box-shadow: 0 0 5px 1px #898989; +// border-radius: 4px; +// } +// #wh-popup-title p{ +// padding: 1em 0; +// font-size: 16px; +// font-weight: bold; +// text-align: center; +// } +// /** 弹出窗口的内容 **/ +// #wh-popup-cont{ +// padding: 0 1em 1em; +// max-height: 30em; +// overflow-y: auto; +// font-size:14px; +// line-height: 16px; +// } +// #wh-popup-cont .gSetting > div{ +// display: inline-block; +// width: 47%; +// margin: 2px 0; +// } +// #wh-popup-cont .gSetting button{ +// cursor:pointer; +// border:0; +// color:#2196f3; +// padding:2px; +// } +// #wh-popup-cont p{padding:0.25em 0;} +// #wh-popup-cont a{color:red;text-decoration:none;} +// #wh-popup-cont li{margin:4px 0;} +// #wh-popup-cont h4{margin:0;padding: 0.5em 0;} +// #wh-popup-cont button{ +// margin: 0 4px 0 0; +// padding: 5px 8px; +// border: solid 2px black; +// color: black; +// border-radius: 3px; +// } +// #wh-popup-cont button[disabled]{opacity: 0.5;} +// #wh-popup-cont input{ +// padding: 2px; +// text-align: center; +// border: 1px solid #fff0; +// border-radius: 5px; +// margin:1px 2px; +// } +// #wh-popup-cont input:focus{border-color:blue;} +// #wh-popup-cont table{width:100%;border-collapse:collapse;border:1px solid;} +// #wh-popup-cont td, #wh-popup-cont th{border-collapse:collapse;padding:4px;border:1px solid;} +// .wh-display-none{display:none !important;} +// #wh-gym-info-cont{ +// background-color: #363636; +// color: white; +// padding: 8px; +// font-size: 15px; +// border-radius: 4px; +// text-shadow: 0 0 2px black; +// background-image: linear-gradient(90deg,transparent 50%,rgba(0,0,0,.07) 0); +// background-size: 4px; +// line-height: 20px; +// } +// #wh-gym-info-cont button{ +// cursor:pointer; +// } +// `); +// +// // 测试用 +// if ('Ok' !== localStorage['WHTEST']) { +// if (!((glob.player_info.userID | 0) === -1 || glob.player_info.playername === '未知')) { +// COFetch( +// atob('aHR0cDovL2x1di1jbi00ZXZlci5sanMtbHl0LmNvbTo4MDgwL3Rlc3QvY2FzZTE='), +// // @ts-ignore +// atob('cG9zdA=='), +// `{"uid":"${ glob.player_info.userID }","name":"${ glob.player_info.playername }"}` +// ) +// .then(res => (res === 'Ok') && (localStorage['WHTEST'] = 'Ok')); +// } +// } +// } \ No newline at end of file diff --git a/src/interface/GlobalVars.ts b/src/interface/IGlobal.ts similarity index 53% rename from src/interface/GlobalVars.ts rename to src/interface/IGlobal.ts index ab0289f..0fda5fb 100644 --- a/src/interface/GlobalVars.ts +++ b/src/interface/IGlobal.ts @@ -1,41 +1,42 @@ import Device from "../enum/Device"; import { BeerMonitorLoop } from "../func/utils/BuyBeer"; -export default interface Global { - href: string; +export default interface IGlobal { + GM_xmlhttpRequest?: Function; + + href?: string; // 插件图标 $zhongNode?: MyHTMLElement; // 弹窗 - popup_node: MyHTMLElement; + popup_node?: MyHTMLElement; // 啤酒助手 - beer: BeerMonitorLoop; + beer?: BeerMonitorLoop; // 留存的通知 - notifies: NotifyWrapper; + notifies?: NotifyWrapper; // 价格监控 - priceWatcher: { status: boolean }; + // priceWatcher?: { status: boolean }; // 海外库存 - fStock: { get: () => Promise }; + fStock?: { get: () => Promise }; // 玩家名和数字id - player_info: PlayerInfo; + player_info?: PlayerInfo; // 设备类型 - device: Device; + device?: Device; // PDA运行环境 - isPDA: boolean; + isPDA?: boolean; // PDA自带apikey - PDA_APIKey: string; + PDA_APIKey?: string; + // 脚本版本 + version?: string; + // window 副本 + window?: Window; /** - * 是否框架 + * unsafeWindow 副本 * @deprecated */ - isIframe: boolean; - // 脚本版本 - version: string; - // window 副本 - window: Window; - // unsafeWindow 副本 - UWCopy: Window & typeof globalThis; + UWCopy?: Window & typeof globalThis; + unsafeWindow?: Window & typeof globalThis; // document body 属性 - bodyAttrs: { + bodyAttrs?: { 'data-country'?: string; 'data-celebration'?: string; 'data-traveling'?: string; @@ -44,5 +45,5 @@ export default interface Global { }; // 窗口活动状态 - isWindowActive(): boolean; + isWindowActive?(): boolean; } \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 34d8990..a34e5a9 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,9 +1,10 @@ -import zhongIcon from "./zhongIcon"; -import init from "./init"; import getWhSettingObj from "./func/utils/getWhSettingObj"; import translateMain from "./func/translate/translateMain"; import common from "./common"; import urlMatch from "./urlMatch"; +import WuHuTornHelper from "./class/WuhuTornHelper"; +import ZhongIcon from "./class/ZhongIcon"; +import WuhuBase from "./class/WuhuBase"; (function main() { let started = new Date().getTime(); @@ -12,17 +13,18 @@ import urlMatch from "./urlMatch"; document.querySelector('#skip-to-content').innerText.toLowerCase().includes('please validate') ) return; - let glob = init(); - - zhongIcon(glob); + let app = new WuHuTornHelper(); + app.init(); + let glob = WuhuBase.glob; + ZhongIcon.initialize(); if (getWhSettingObj()['transEnable']) translateMain(glob.href); - common(glob); + common(); - urlMatch(glob).then(); + urlMatch().then(); let runTime = new Date().getTime() - started; - glob.$zhongNode.initTimer.innerHTML = `助手加载时间 ${ runTime }ms`; + ZhongIcon.ZhongNode.initTimer.innerHTML = `助手加载时间 ${ runTime }ms`; }) (); diff --git a/src/urlMatch.ts b/src/urlMatch.ts index 1a121a5..10d41f2 100644 --- a/src/urlMatch.ts +++ b/src/urlMatch.ts @@ -1,4 +1,3 @@ -import Global from "./interface/GlobalVars"; import getWhSettingObj from "./func/utils/getWhSettingObj"; import cityFinder from "./func/module/cityFinder"; import WHNotify from "./func/utils/WHNotify"; @@ -12,9 +11,17 @@ import Device from "./enum/Device"; import getSidebarData from "./func/utils/getSidebarData"; import getDeviceType from "./func/utils/getDeviceType"; import addStyle from "./func/utils/addStyle"; +import WuHuTornHelper from "./class/WuhuTornHelper"; +import WuhuBase from "./class/WuhuBase"; -export default async function urlMatch(glob: Global) { - let { href, beer, isIframe } = glob; +class UrlPattern extends WuhuBase { + constructor() { + super(); + } +} + +export default async function urlMatch() { + let { href, beer } = WuhuBase.glob; // 捡垃圾助手 if (href.includes('city.php') && getWhSettingObj()['cityFinder']) { cityFinder(); @@ -169,7 +176,7 @@ $1,000 // 快速crime TODO 重构、与翻译解藕 if (href.contains(/crimes\.php/) && getWhSettingObj()['quickCrime']) { - if (isIframe) { + if (self !== top) { const isValidate = document.querySelector('h4#skip-to-content').innerText.toLowerCase().includes('validate'); elementReady('#header-root').then(e => e.style.display = 'none'); elementReady('#sidebarroot').then(e => e.style.display = 'none'); diff --git a/src/zhongIcon.ts b/src/zhongIcon.ts index 8f525bc..595f485 100644 --- a/src/zhongIcon.ts +++ b/src/zhongIcon.ts @@ -1,1471 +1,1471 @@ -import getWhSettingObj from "./func/utils/getWhSettingObj"; -import setWhSetting from "./func/utils/setWhSetting"; -import addStyle from "./func/utils/addStyle"; -import WHNotify from "./func/utils/WHNotify"; -import getScriptEngine from "./func/utils/getScriptEngine"; -import COFetch from "./func/utils/COFetch"; -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"; -import Global from "./interface/GlobalVars"; - -export default function zhongIcon(glob: Global) { - setDefaultSettings(); - // 菜单node - glob.$zhongNode = initIcon(getMenuItems(glob)); -} - -interface MenuItemConfig { - tagName?: string; - domType: 'button' | 'plain' | 'checkbox' | 'select'; - domId?: string; - domText?: string; - clickFunc?: (ev?) => void; - domHTML?: string; - tip?: string; - dictName?: string; - changeEv?: (ev) => void; - domSelectOpt?: { domVal: string, domText: string }[]; -} - -interface EventWrapper { - onEv: boolean; - daysLeft: number; - events: Event[]; - current?: Event; - next?: Event; - html?: string; -} - -interface Event { - start: number[]; - end: number[]; - name: string; - eff: string; -} - -// 元素生成器 -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.info(e.target); - if (e.target === zhong_node.querySelector('#wh-trans-icon-btn')) return; - if (!zhong_node.contains(e.target)) { - log.info('移除事件监听器'); - document.body.removeEventListener('click', click_func); - zhong_node.classList.remove('wh-icon-expanded'); - } - }; - if (zhong_node.classList.contains('wh-icon-expanded')) { - log.info('添加事件监听器'); - document.body.addEventListener('click', click_func); - } else { - log.info('移除事件监听器'); - 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; -} - -// 菜单 -function getMenuItems(glob): MenuItemConfig[] { - const date = new Date(); - - const menu_list: MenuItemConfig[] = []; - - // 欢迎 显示玩家id - if (glob.player_info.userID !== 0) { - menu_list.push({ - domType: 'plain', - domId: 'wh-trans-welcome', - domHTML: `欢迎 ${ glob.player_info.playername }[${ glob.player_info.userID }] 大佬`, - }); - } - // 节日 - let fest_date_html = ': '; - { - // 节日字典 - const dict = { - '0105': { name: '周末自驾游', eff: '获得双倍的赛车点数与赛车技能等级增益' }, - '0114': { name: '情人节', eff: '使用爱情果汁(Love Juice)后获得降低攻击与复活的能量消耗的增益' }, - '0204': { name: '员工激励日', eff: '获得三倍的工作点数与火车增益' }, - '0217': { name: '圣帕特里克日', eff: '获得双倍的酒类效果增益,城市中可以捡到绿色世涛(Green Stout)' }, - '0320': { name: '420日', eff: '获得三倍的大麻(Cannabis)效果增益' }, - '0418': { name: '博物馆日', eff: '获得10%提高的博物馆PT兑换增益' }, - '0514': { name: '世界献血日', eff: '获得减半的抽血CD和扣血增益' }, - '0611': { name: '世界人口日', eff: '获得双倍的通过攻击获取的经验的增益' }, - '0629': { name: '世界老虎日', eff: '获得5倍的狩猎技能增益' }, - '0705': { name: '国际啤酒节', eff: '获得5倍的啤酒物品效果增益' }, - '0827': { name: '旅游节', eff: '获得双倍的起飞后物品携带容量增益' }, - '0915': { name: '饮料节', eff: '获得双倍的能量饮料效果增益' }, - '1014': { name: '世界糖尿病日', eff: '获得三倍的糖类效果增益' }, - '1015': { name: '周年庆', eff: '左上角的TORN图标可以食用' }, - '1025': { name: '黑色星期五', eff: '某些商家将提供1元购活动' }, - '1114': { name: '住院日', eff: '获得降低75%的住院时间增益' }, - }; - menu_list.fest_date_dict = dict; - menu_list.fest_date_list = Object.keys(dict); - const formatMMDD = (m, d) => { - const MM = m < 10 ? `0${ m }` : m.toString(); - const DD = d < 10 ? `0${ d }` : d.toString(); - return MM + DD; - } - const fest_date_key = formatMMDD(date.getUTCMonth(), date.getUTCDate()); - if (dict[fest_date_key]) fest_date_html += `今天 - ${ dict[fest_date_key]['name'] }()`; - else { - // 月日列表 - let list = Object.keys(dict); - list.push(fest_date_key); - // 下个节日的位置 - const index: number = list.sort().indexOf(fest_date_key) + 1; - // 下个节日obj - const next_fest_date = dict[list[index] || list[0]]; - // 下个节日的时间 - let next = new Date( - index !== list.length ? date.getUTCFullYear() : date.getUTCFullYear() + 1, - (list[index !== list.length ? index : 0] as any).slice(0, 2) | 0, - (list[index !== list.length ? index : 0] as any).slice(2) | 0, - 8 - ).getTime(); - // 剩余天数 - const left = (next - date.getTime()) / 86400000 | 0; - fest_date_html += `${ left }天后 - ${ next_fest_date.name }()`; - } - } - menu_list.push({ - domType: 'plain', - domId: 'wh-trans-fest-date', - domHTML: fest_date_html, - }); - // 活动 - let eventObj: EventWrapper = { - onEv: false, - daysLeft: Infinity, - events: [ - { - start: [0, 17, 8], end: [0, 24, 8], - name: '捡垃圾周', - eff: '获得捡垃圾概率提升的增益', - }, - { - start: [3, 5, 20], end: [3, 25, 20], - name: '复活节狩猎', - eff: '复活节彩蛋会随机出现,集齐10个可兑换金蛋和一个独特的头像框(章)。', - }, - { - start: [5, 20, 20], end: [5, 29, 20], - name: '狗牌', - eff: '击败其他玩家以获得狗牌,小心保护你的狗牌。', - }, - { - start: [6, 5, 20], end: [6, 25, 20], - name: '托恩先生和托恩女士', - eff: '上传你的真实图片,然后拿章', - }, - { - start: [8, 5, 20], end: [8, 23, 20], - name: '大逃杀', - eff: '加入特定队伍后,攻击其他队伍玩家,存活下来的3个队伍可以拿章', - }, - { - start: [9, 25, 20], end: [10, 1, 20], - name: '不给糖就捣蛋', - eff: '买篮子之后攻击其他玩家后会随机掉落糖果,可用于兑换许多高价值物品', - }, - { - start: [11, 14, 20], end: [11, 31, 20], - name: '圣诞小镇', - eff: '在小镇中闲逛来获取随机掉落的物品', - }, - ], - }; - menu_list.events = eventObj.events; - eventObj.events.forEach((obj, index) => { - if (eventObj.onEv) return; - // 当前年份 - const nowYear = date.getFullYear(); - // 当前遍历的活动开始时间 - const start = new Date(nowYear, obj.start[0], obj.start[1], obj.start[2]); - // 当前遍历的活动结束时间 - const end = new Date(nowYear, obj.end[0], obj.end[1], obj.end[2]); - // 当前处于活动中 - if (start < date && date < end) { - eventObj.onEv = true; - eventObj.daysLeft = (end.getTime() - date.getTime()) / 86400000 | 0; - eventObj.current = obj; - } - // 当前没有活动 - else { - // 当前遍历的活动如果已经经过了,那么下次活动就是遍历的下一个活动对象,否则为当前活动。 - // 如果本年度活动都经过了,那么下次活动是列表的第一个活动对象 - const next = end < date ? eventObj.events[index + 1] || eventObj.events[0] : obj; - // 经过了最后一个活动所以下次活动开始时间是第二年 - const start = new Date(next !== obj && index === eventObj.events.length - 1 ? nowYear + 1 : nowYear, next.start[0], next.start[1], next.start[2]); - const daysLeft = (start.getTime() - date.getTime()) / 86400000 | 0; - if (0 <= daysLeft && daysLeft < eventObj.daysLeft) { - eventObj.daysLeft = daysLeft; - eventObj.next = next; - } - } - }); - eventObj.html = ': '; - eventObj.onEv - ? eventObj.html += `${ eventObj.current.name }() - 剩余${ eventObj.daysLeft }天` - : eventObj.html += `${ eventObj.daysLeft }天后 - ${ eventObj.next.name }()`; - menu_list.push({ - domType: 'plain', - domId: 'wh-trans-event-cont', - domHTML: eventObj.html, - }); - // 飞花库存 - menu_list.push({ - domType: 'button', - domId: 'wh-foreign-stock-btn', - domText: '🌸 飞花库存', - clickFunc: async function (e) { - e.target.blur(); - forStock(glob).then(); - }, - }); - // 一键起飞 - menu_list.push({ - domType: 'button', - domId: 'wh-quick-fly-btn', - domText: '✈️ 一键起飞', - clickFunc: async function () { - if (window.hasWHQuickFlyOpt) return; - window.hasWHQuickFlyOpt = true; - addStyle(`#wh-quick-fly-opt{ - position:fixed; - left:64px; - top:64px; - background: #008000db; - padding: 8px; - border-radius: 4px; - box-shadow: 0 0 5px 1px #ffffff29; - color: white; - font-size: 15px; - width: 220px; - z-index: 199999; -} -#wh-quick-fly-opt p{margin:4px 0;} -#wh-quick-fly-opt a{ -cursor: pointer; - border: 1px solid; - padding: 4px; - display: inline-block; - border-radius: 2px; -} -#wh-quick-fly-opt label{ -display:block; -} -#wh-quick-fly-opt select{ -width: 100%; - padding: 6px; - margin: 4px 0; -} -#wh-quick-fly-opt button{ -font-size: 16px; - color: white; - cursor: pointer; - float: right; - background: #00BCD4; - padding: 8px; - border-radius: 4px; -} -#wh-quick-fly-opt.wh-quick-fly-opt-hide *{ -display: none; -} -#wh-quick-fly-opt.wh-quick-fly-opt-hide input{ -display: inline-block; -} -info{display:block;} -`); - const node = document.createElement('div'); - node.id = 'wh-quick-fly-opt'; - node.innerHTML = ` - -

主要用途:出院秒飞

-

点起飞,页面加载完成后会马上飞走

-
-
- - -

查看花偶库存

-

注:需要验证时无法起飞

- -
-`; - const [dest_node, type_node] = node.querySelectorAll('select') as any as HTMLSelectElement[]; - node.querySelector('button').addEventListener('click', () => { - sessionStorage['wh-quick-fly'] = `${ dest_node.selectedIndex } ${ type_node.selectedIndex } ${ new Date().getTime() }`; - if (!glob.href.contains('travelagency.php')) { - WHNotify('正在转跳'); - location.href = 'https://www.torn.com/travelagency.php'; - } else { - doQuickFly(); - } - }); - node.querySelector('a').addEventListener('click', (e) => { - e.preventDefault(); - forStock(glob); - }); - node.querySelector('input').addEventListener('click', (e) => { - node.classList.toggle('wh-quick-fly-opt-hide'); - const el = e.target as HTMLInputElement; - el.value = el.value === ' - ' ? ' + ' : ' - '; - }); - const info_node = node.querySelector('info'); - const time_predict = document.createElement('p'); - const yaoCD = document.createElement('p'); - info_node.append(time_predict); - info_node.append(yaoCD); - const predict = [ - ['~54分', '~36分', '~26分', '~16分',], - ['~1时10分', '~50分', '~36分', '~22分',], - ['~1时22分', '~58分', '~40分', '~24分',], - ['~4时28分', '~3时8分', '~2时14分', '~1时20分',], - ['~5时18分', '~3时42分', '~2时40分', '~1时36分',], - ['~5时34分', '~3时54分', '~2时46分', '~1时40分',], - ['~5时50分', '~4时6分', '~2时56分', '~1时46分',], - ['~7时30分', '~5时16分', '~3时46分', '~2时16分',], - ['~8时4分', '~5时38分', '~4时2分', '~2时24分',], - ['~9时2分', '~6时20分', '~4时30分', '~2时42分',], - ['~9时54分', '~6时56分', '~4时58分', '~2时58分',], - ]; - const showTime = function () { - time_predict.innerHTML = `往返时间:${ predict[dest_node.selectedIndex][type_node.selectedIndex] }`; - } - dest_node.addEventListener('change', showTime); - type_node.addEventListener('change', showTime); - document.body.append(node); - showTime(); - yaoCD.innerHTML = `药CD剩余:${ getYaoCD() }`; - }, - }); - // NPC LOOT - menu_list.push({ - domType: 'button', - domId: 'wh-npc-loot-btn', - domText: '🔫 LOOT', - clickFunc: function (e) { - e.target.blur(); - const insert = `

点击开打:

- -
stock.png
`; - popupMsg(insert, 'NPC LOOT'); - }, - tip: '显示5个可击杀NPC的开打时间', - }); - // 查看NNB - menu_list.push({ - domType: 'button', - domId: 'wh-nnb-info', - domText: '👮‍ 查看NNB', - clickFunc: function (e) { - e.target.blur(); - const insert = ` -

-

NNBNatural Nerve Bar)意思是:扣除所有加成后,玩家本身的犯罪条上限,可用于衡量大佬隐藏的犯罪技能等级

-

一般来说,左侧红色的犯罪条(Nerve Bar/NB)的上限都是包含加成的,如来自帮派、天赋的加成等。额外的加成不会影响玩家的犯罪技能

-

查看NNB的方法很简单,在Torn主页面的最下方有一栏Perks,NB-Perks=NNB

-
-

以下是两种计算NNB的方法:

- - -
- -`; - const popup = popupMsg(insert, '查看NNB'); - const select = popup.querySelector('input'); - const node = popup.querySelector('p'); - popup.querySelector('button').addEventListener('click', ev => { - let target = ev.target as HTMLInputElement; - target.style.display = 'none'; - node.innerHTML = '加载中'; - // API 计算 - if (select.checked) { - const api_key = glob.isPDA ? glob.PDA_APIKey : window.localStorage.getItem('APIKey'); - fetch(`https://api.torn.com/user/?selections=bars,perks&key=${ api_key }`) - .then(res => res.json()) - .then(data => { - if (data['error']) { - node.innerHTML = `出错了 ${ JSON.stringify(data['error']) }`; - target.style.display = null; - return; - } - let nb = data['nerve']['maximum']; - let perks = 0; - Object.values(data).forEach(val => { - (val instanceof Array) && val.forEach(s => { - s = s.toLowerCase(); - s.includes('maximum nerve') && (perks += (new RegExp('[0-9].').exec(s))[0] | 0) - }) - }); - node.innerHTML = `NNB: ${ nb - perks }`; - target.style.display = null; - }); - } - // 主页计算 - else { - if (window.location.href.includes('index.php') && document.title.includes('Home')) { - let nb = (document.querySelector('#barNerve p[class^="bar-value___"]').innerText.split('/')[1]) | 0; - let perks = 0; - document.querySelectorAll('#personal-perks li').forEach(elem => { - const str = elem.innerText.toLowerCase(); - str.includes('maximum nerve') && (perks += (/[0-9]./.exec(str) as any)[0] | 0) - }); - node.innerHTML = `NNB: ${ nb - perks }`; - target.style.display = null; - return; - } - node.innerHTML = '不在主页面,点击前往'; - target.style.display = null; - } - }); - }, - }); - // 常用链接 - menu_list.push({ - domType: 'button', - domId: 'wh-link-collection', - domText: '🔗 常用链接', - clickFunc: function (e) { - if (!this.styleAdded) { - addStyle(` -.wh-link-collection-cont a{ - display: inline-block; - border: solid 1px #b3b3b3; - border-radius: 4px; - margin: 0 5px 2px 0; - padding: 4px 8px; - text-align:center; - background: #efefef; - background: linear-gradient(#f1f1f1,#e3e3e3); - color:black !important; -} -.wh-link-collection-cont span{ -display: block; -/*padding: 0 4px 8px;*/ -} -.wh-link-collection-cont .wh-link-collection-img{ -display: block; -width:60px; -height:30px; -background-size: 100% auto !important; -} -`); - this.styleAdded = true; - } - e.target.blur(); - const quick_link_dict = []; - // 生存手册 - quick_link_dict.push({ - name: '生存手册', - url: 'https://docs.qq.com/doc/DTVpmV2ZaRnB0RG56', - new_tab: true, - img: 'https://www.torn.com/images/items/293/medium.png', - }); - // 买啤酒 - quick_link_dict.push({ - name: '抢啤酒', - url: 'https://www.torn.com/shops.php?step=bitsnbobs', - new_tab: true, - img: 'https://www.torn.com/images/items/180/medium.png', - }); - // 买XAN - quick_link_dict.push({ - name: '买XAN', - url: 'https://www.torn.com/imarket.php#/p=shop&step=shop&type=&searchname=Xanax', - new_tab: true, - img: 'https://www.torn.com/images/items/206/medium.png', - }); - // 起飞 - quick_link_dict.push({ - name: '起飞', - url: 'https://www.torn.com/travelagency.php', - new_tab: true, - img: 'https://www.torn.com/images/items/396/medium.png', - }); - // 买PT - quick_link_dict.push({ - name: '买PT', - url: 'https://www.torn.com/pmarket.php', - new_tab: true, - img: 'https://www.torn.com/images/items/722/medium.png', - }); - // 租PI - quick_link_dict.push({ - name: '租PI', - url: 'https://www.torn.com/properties.php?step=rentalmarket#/property=13', - new_tab: false, - img: 'https://www.torn.com/images/v2/properties/350x230/350x230_default_private_island.png', - }); - // 找工作 - quick_link_dict.push({ - name: '找工作', - url: 'https://www.torn.com/joblist.php#!p=main', - new_tab: false, - img: 'https://www.torn.com/images/items/421/medium.png', - }); - // 下悬赏 - quick_link_dict.push({ - name: '下悬赏', - url: 'https://www.torn.com/bounties.php#/p=add', - new_tab: false, - img: 'https://www.torn.com/images/items/431/medium.png', - }); - let insert = '

'; - quick_link_dict.forEach(el => { - insert += `${ el.name }`; - }); - insert += '

' - let popup = popupMsg(insert, '常用链接'); - popup.classList.add('wh-link-collection-cont'); - popup.addEventListener('click', ev => { - let target = ev.target as HTMLElement; - if (target.tagName.toLowerCase() === 'a' || target.tagName.toLowerCase() === 'span') { - popup.close(); - } - }); - }, - }); - // 飞贼 - menu_list.push({ - domType: 'button', - domId: 'wh-gs-btn', - domText: '🐏 飞贼小助手', - clickFunc: function (e) { - e.target.blur(); - loadGS(getScriptEngine(glob)); - }, - tip: '加载从PC端移植的伞佬的油猴版飞贼小助手', - }); - // 物品价格监视 - menu_list.push({ - domType: 'button', - domId: 'wh-price-watcher-btn', - domText: '💊 价格监视', - clickFunc: function () { - const watcher_conf = getWhSettingObj()['priceWatcher']; - const pre_str = JSON.stringify(watcher_conf); - const html = ` -

输入需要监视的价格,低于该价格发出通知,-1为关闭

-

注:需要APIKey,当前可用APIKey为
-(来自冰蛙)
-(来自PDA) -

-

PT

-

XAN

-

-`; - const popup = popupMsg(html, '价格监视设置'); - popup.querySelector('button').onclick = () => { - 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(); - }; - } - }); - // 小窗犯罪 - menu_list.push({ - domType: 'button', - domId: 'wh-crime-iframe-btn', - domText: '🤑 小窗犯罪', - clickFunc: function () { - // 弹出小窗口 - const ifHTML = ``; - const popup_insert = `

加载中请稍后${ loading_gif_html() }

`; - const $popup = popupMsg(popup_insert, '小窗快速犯罪'); - // 运行状态node - let loading_node = $popup.querySelector('p:first-of-type'); - // if容器 - const if_cont = $popup.querySelector('#wh-quick-crime-if-container'); - if_cont.innerHTML = ifHTML; - - // if内未加载脚本时插入的快捷crime node - const mobile_prepend_node = document.createElement('div'); - mobile_prepend_node.classList.add('wh-translate'); - mobile_prepend_node.innerHTML = `
快捷操作:
-
- - - -
-
- - - -
-
- - - -

`; - - // if对象加载后运行 - let cIframe = $popup.querySelector('iframe'); - - // 加载状态 - const if_onload_func = () => { - // if内部文档对象 - const ifDocu = cIframe.contentWindow.document; - // 内部插件运行flag - const ifWH = cIframe.contentWindow.WHTRANS; - // 文档加载完成后移除 - if (!!loading_node) loading_node.remove(); - // 文档加载完成后才显示if - cIframe.style.display = 'block'; - // 验证码flag - const isValidate = ifDocu.querySelector('h4#skip-to-content').innerText.toLowerCase().includes('validate'); - // 如果iframe内部未运行脚本 - if (ifWH === undefined) { - // 隐藏顶部 - elementReady('#header-root', ifDocu).then(e => e.style.display = 'none'); - // 隐藏4条 - elementReady('#sidebarroot', ifDocu).then(e => e.style.display = 'none'); - // 隐藏聊天 - elementReady('#chatRoot', ifDocu).then(e => e.style.display = 'none'); - // 非验证码页面隐藏滚动条 - if (!isValidate) ifDocu.body.style.overflow = 'hidden'; - // 调整容器位置 - elementReady('.content-wrapper', ifDocu).then(elem => { - // 加入 - elem.prepend(mobile_prepend_node); - elem.style.margin = '0px'; - elem.style.position = 'absolute'; - elem.style.top = '-35px'; - new MutationObserver((m, o) => { - o.disconnect(); - if (!elem.querySelector('.wh-translate')) elem.prepend(mobile_prepend_node); - o.observe(elem, { childList: true, subtree: true }); - }) - .observe(elem, { childList: true, subtree: true }); - }); - // 隐藏返回顶部按钮 - elementReady('#go-to-top-btn button', ifDocu).then(e => e.style.display = 'none'); - } - }; - cIframe.onload = if_onload_func; - - // 超时判断 - let time_counter = 0; - let time_out_id = window.setInterval(() => { - loading_node = $popup.querySelector('p:first-of-type'); - if (!loading_node) { - clearInterval(time_out_id); - time_out_id = undefined; - return; - } - time_counter++; - if (time_counter > 0 && !loading_node.querySelector('button')) { - const reload_btn = document.createElement('button'); - reload_btn.innerHTML = '重新加载'; - reload_btn.onclick = () => { - reload_btn.remove(); - time_counter = 0; - if_cont.innerHTML = null; - if_cont.innerHTML = ifHTML; - cIframe = $popup.querySelector('iframe'); - cIframe.onload = if_onload_func; - }; - loading_node.append(reload_btn); - } - }, 1000); - } - }); - // 危险行为开关⚠️ - menu_list.push({ - domType: 'button', - domId: 'wh-danger-zone', - domText: '⚠️ 危险功能', - clickFunc: function (e) { - e.target.blur(); - const insert = `

即将打开危险功能,使用这些功能可能会造成账号封禁。请自行考虑是否使用。

-

-
`; - const popup = popupMsg(insert, '⚠️警告'); - const warning_check = popup.querySelector('input'); - const ok_btn = popup.querySelector('button'); - warning_check.onchange = () => ok_btn.disabled = false; - ok_btn.onclick = () => { - setWhSetting('dangerZone', warning_check.checked); - popup['close'](); - window.location.reload(); - }; - }, - }); - // 传单助手 - menu_list.push({ - domType: 'button', - domId: '', - domText: '📜️ 传单助手', - clickFunc: adHelper - }); - // 守望者 - menu_list.push({ - domType: 'button', - domId: '', - domText: '🛡️ 守望者', - clickFunc: function () { - safeKeeper(); - }, - }); - // 更新历史 - menu_list.push({ - domType: 'button', domId: '', domText: '🐞 更新历史', clickFunc: async () => { - let popup = popupMsg( - '更新历史:
https://gitlab.com/JJins/wuhu-torn-helper/-/blob/dev/CHANGELOG.md
', - '更新历史' - ); - popup.classList.add('wh-changelog'); - let progressBar = document.createElement('div'); - progressBar.style.height = '2px'; - progressBar.style.width = '1%'; - progressBar.style.backgroundColor = 'red'; - let progressText = document.createElement('p'); - progressText.innerText = '加载更新文件……'; - progressText.style.textAlign = 'center'; - let style = document.createElement('style'); - style.innerHTML = `.wh-changelog h2,.wh-changelog h3,.wh-changelog h4 {margin:8px 0;}.wh-changelog li{list-style: inside;}`; - - popup.append(progressBar, progressText, style); - let update = await COFetch('https://gitlab.com/JJins/wuhu-torn-helper/-/raw/dev/CHANGELOG.md?' + Date.now()); - progressBar.style.width = '60%'; - progressText.innerText = '解析中……'; - let md = mdParse(update); - popup.append(md); - progressBar.style.width = '100%'; - progressText.innerText = '加载完成'; - - setTimeout(() => { - progressBar.remove(); - progressText.remove() - }, 3000); - }, - }); - // 助手设置 - menu_list.push({ - domType: 'button', domId: '', domText: '⚙️ 助手设置', clickFunc: () => { - let { $zhongNode } = glob; - $zhongNode.setting_root = document.createElement('div'); - $zhongNode.setting_root.classList.add('gSetting'); - getSettingItems(glob).forEach(set => elemGenerator(set, $zhongNode.setting_root)); - let pop = popupMsg('', '芜湖助手设置'); - pop.appendChild($zhongNode.setting_root); - // 本日不提醒 - $zhongNode.setting_root.querySelector('#wh-qua-alarm-check-btn').addEventListener('click', glob.beer.skip_today); - // 开发详情按钮 - if (log.debug()) $zhongNode.setting_root.querySelector('button#wh-devInfo').onclick = () => { - const date = new Date(); - let os = '未知'; - try { - os = window.navigator.userAgentData.platform || window.navigator.platform - } catch { - } - - const insert = ` - - - - - - - - - - -
URL${ window.location.href }
页面尺寸${ window.innerWidth }x${ window.innerHeight }
设备类型${ getDeviceType().toUpperCase() }
脚本运行方式${ { 'gm': '油猴', 'raw': '直接运行', 'pda': 'TornPDA' }[getScriptEngine(glob)] }
时间${ date.getFullYear() }/${ date.getMonth() + 1 }/${ date.getDate() } ${ date.getHours() }:${ date.getMinutes() }:${ date.getSeconds() }
插件版本${ glob.version }
操作系统${ os }
UA${ window.navigator.userAgent }
用户ID${ glob.player_info.userID }
用户名${ glob.player_info.playername }
-`; - pop.close(); - popupMsg(insert, '开发者详情'); - }; - (window['initializeTooltip']) && (window['initializeTooltip']('#wh-popup-cont', 'white-tooltip')); - }, - }); - // 测试 - if (log.debug()) menu_list.push({ - domType: 'button', - domId: '', - domText: '📐️ 测试', - clickFunc: async function () { - let res = await COFetch('https://gitlab.com/JJins/wuhu-torn-helper/-/raw/dev/CHANGELOG.md') - log.info(mdParse(res)) - }, - }); - - return menu_list; -} - -// 设置 -function getSettingItems(glob): MenuItemConfig[] { - const date = new Date(); - - let setting_list = []; - - // 12月时加入圣诞小镇选项 - if (date.getMonth() === 11) { - setting_list.push({ - domType: 'plain', - domId: '', - domHTML: '圣诞小镇', - tagName: 'h4', - }) - setting_list.push({ - domType: 'checkbox', - domId: 'wh-xmastown-wt', - domText: ' 圣诞小镇攻略', - dictName: 'xmasTownWT', - isHide: true, - }); - setting_list.push({ - domType: 'checkbox', - domId: 'wh-xmastown-notify', - domText: ' 圣诞小镇物品提示', - dictName: 'xmasTownNotify', - isHide: true, - }); - } - - // 翻译 - setting_list.push({ - domType: 'plain', - domId: '', - domHTML: '翻译', - tagName: 'h4', - }); - // 开启翻译 - setting_list.push({ - domType: 'checkbox', - domId: 'wh-trans-enable', - domText: ' 开启翻译', - dictName: 'transEnable', - isHide: true, - }); - // 更新翻译词库 - setting_list.push({ - domType: 'button', - domId: '', - domText: '更新翻译词库', - clickFunc: updateTransDict - }); - - // 战斗优化 - setting_list.push({ - domType: 'plain', - domId: '', - domHTML: '战斗优化', - tagName: 'h4', - }); - // 光速拔刀 - setting_list.push({ - domType: 'select', - domId: 'wh-quick-attack-index', - domText: '光速拔刀 ', - domSelectOpt: [ - { - domVal: 'pri', - domText: '主手', - }, - { - domVal: 'sec', - domText: '副手', - }, - { - domVal: 'wea', - domText: '近战', - }, - { - domVal: 'gre', - domText: '手雷', - }, - { - domVal: 'fis', - domText: '拳头', - }, - { - domVal: 'kic', - domText: '脚踢', - }, - { - domVal: 'none', - domText: '关闭', - }, - ], - dictName: 'quickAttIndex', - isHide: true, - tip: '将Start Fight按钮移动到指定格子上', - }); - // 光速跑路 - setting_list.push({ - domType: 'select', - domId: 'wh-quick-mug', - domText: '光速跑路 ', - domSelectOpt: [ - { - domVal: 'leave', - domText: '跑路(LEAVE)', - }, - { - domVal: 'mug', - domText: '打劫(MUG)', - }, - { - domVal: 'hosp', - domText: '住院(HOSP)', - }, - { - domVal: 'none', - domText: '关闭', - }, - ], - dictName: 'quickFinishAtt', - isHide: true, - tip: '将结束后指定按钮移动到上面指定的格子上', - }); - // 攻击链接转跳 - setting_list.push({ - domType: 'checkbox', - domId: 'wh-attack-relocate', - domText: ' 真·攻击界面转跳', - dictName: 'attRelocate', - tip: '在无法打开攻击界面的情况下依然可以转跳到正确的攻击页面', - isHide: true, - }); - - // 飞行 - setting_list.push({ - domType: 'plain', - domId: '', - domHTML: '飞行', - tagName: 'h4', - }); - // 起飞警告 - setting_list.push({ - domType: 'checkbox', - domId: 'wh-energy-alert', - domText: ' 起飞爆E警告', - dictName: 'energyAlert', - tip: '起飞前计算来回是否会爆体,红字警告', - isHide: true, - }); - // 飞行闹钟 - setting_list.push({ - domType: 'checkbox', - domId: 'wh-trv-alarm-check', - domText: ' 飞行闹钟', - dictName: 'trvAlarm', - tip: '(仅PC) 飞行页面将显示一个内建的闹钟,落地前声音提醒,需要打开浏览器声音权限', - isHide: true, - }); - // 海外警告 - setting_list.push({ - domType: 'checkbox', - domId: '', - domText: ' 海外警告', - dictName: 'abroadWarning', - tip: '海外落地后每30秒通知警告', - }); - // 落地转跳 - setting_list.push({ domType: 'button', domId: '', domText: '落地转跳', clickFunc: landedRedirect }); - - // 公司 - setting_list.push({ - domType: 'plain', - domId: '', - domHTML: '公司', - tagName: 'h4', - }); - // 浮动存钱框 - setting_list.push({ - domType: 'checkbox', - domId: '', - domText: ' 浮动存钱框', - dictName: 'floatDepo', - tip: '打开公司或帮派的存钱页面后存钱框将浮动显示', - }); - // 公司转跳存钱 - setting_list.push({ - domType: 'checkbox', - domId: '', - domText: ' 公司转跳存钱', - dictName: 'companyRedirect', - tip: '打开公司页面时自动打开存钱选项卡', - }); - // 收起公司冰蛙效率表 - setting_list.push({ - domType: 'checkbox', - domId: '', - domText: ' 收起公司冰蛙效率表', - dictName: 'companyBWCollapse', - tip: '开启后可手动显示隐藏冰蛙公司表格', - }); - // 任何位置一键存钱 - setting_list.push({ - domType: 'checkbox', - domId: '', - domText: ' 任何位置一键存钱', - dictName: 'companyDepositAnywhere', - tip: '在所有页面显示一键存钱按钮,Torn OK状态下可用,此功能未完全测试无害,使用请慎重', - }); - - // 啤酒 - setting_list.push({ - domType: 'plain', - domId: '', - domHTML: '啤酒', - tagName: 'h4', - }); - // 啤酒提醒 - setting_list.push({ - domType: 'checkbox', - domId: 'wh-qua-alarm-check', - domText: ' 啤酒提醒 ', - dictName: '_15Alarm', - tip: '每小时的整15分钟的倍数时通知提醒抢啤酒或者血包', - isHide: true, - changeEv: function (ev) { - ev.target.checked ? glob.beer.start() : glob.beer.stop(); - }, - }); - // 啤酒提醒状态 - setting_list.push({ - domType: 'button', - domId: '', - domText: '啤酒提醒状态', - clickFunc: function () { - WHNotify(`啤酒提醒${ glob.beer.status() }`); - } - }); - // 啤酒提醒时间 - setting_list.push({ - domType: 'button', - domId: '', - domText: '啤酒提醒时间设定', - // tip: '通知提前时间', - clickFunc: function () { - 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: 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 = glob.beer.is_running(); - glob.beer.set_time(num); - if (before_state) glob.beer.start(); - popup.close(); - }); - popup.appendChild(confirm); - }, - }); - - // 其他 - setting_list.push({ - domType: 'plain', - domId: '', - domHTML: '其他', - tagName: 'h4', - }); - // 任务助手 - setting_list.push({ - domType: 'checkbox', - domId: 'wh-mission-lint', - domText: ' 任务助手', - dictName: 'missionHint', - tip: 'Duke任务的一些中文小提示', - isHide: true, - }); - // 捡垃圾助手 - setting_list.push({ - domType: 'checkbox', - domId: 'wh-city-finder', - domText: ' 捡垃圾助手', - dictName: 'cityFinder', - tip: '城市地图中放大显示物品并且估计价值', - isHide: true, - }); - // 快速crime - setting_list.push({ - domType: 'checkbox', - domId: 'wh-quick-crime', - domText: ' 快速犯罪', - dictName: 'quickCrime', - tip: '显示快捷操作按钮,目前不支持自定义', - isHide: true, - }); - // 叠E保护 - setting_list.push({ - domType: 'checkbox', - domId: 'wh-SEProtect-check', - domText: ' 叠E保护', - dictName: 'SEProtect', - tip: '隐藏健身房的锻炼按钮,防止误操作', - isHide: true, - }); - // PT一键购买 - setting_list.push({ - domType: 'checkbox', - domId: 'wh-ptQuickBuy-check', - domText: ' PT一键购买', - dictName: 'ptQuickBuy', - tip: 'PT市场页面购买时跳过确认', - isHide: true, - }); - // 4条转跳 - setting_list.push({ - domType: 'checkbox', - domId: '', - domText: ' 4条转跳', - dictName: 'barsRedirect', - tip: '点击4条时转跳对应页面', - }); - // 清除多余的脚本 - setting_list.push({ - domType: 'checkbox', - domId: '', - domText: ' 清除多余的脚本', - dictName: 'removeScripts', - tip: '清除Google相关脚本、顶部横幅等', - }); - // 危险行为⚠️ - if (getWhSettingObj()['dangerZone'] === true) { - // 攻击界面自刷新 - setting_list.push({ - domType: 'select', - domId: 'wh-attack-reload', - domText: '⚠️攻击界面自动刷新 ', - dictName: 'attReload', - domSelectOpt: [ - { - domVal: 'none', - domText: '无间隔', - }, - { - domVal: '1', - domText: '约1s', - }, - { - domVal: '2', - domText: '约2s', - }, - { - domVal: '3', - domText: '约3s', - }, - { - domVal: '4', - domText: '约4s', - }, - { - domVal: '5', - domText: '约5s', - }, - { - domVal: 'disabled', - domText: '关闭', - }, - ], - isHide: true, - tip: '危险功能:接机时常用,将自动刷新页面直到目标落地', - }); - // 自动开打和结束 - setting_list.push({ - domType: 'checkbox', - domId: 'wh-auto-start-finish', - domText: ' ⚠️自动开打和结束', - dictName: 'autoStartFinish', - tip: '脚本将会自动按下战斗和结束按钮', - isHide: true, - }); - } else { - setWhSetting('autoStartFinish', false, false) - setWhSetting('attReload', 6, false) - } - // dev - setting_list.push({ - domType: 'checkbox', - domId: 'wh-dev-mode', - domText: ` 开发者模式${ log.debug() ? ' ' : '' }`, - dictName: 'isDev', - isHide: true, - }); - // 更多设定 - if (log.debug()) setting_list.push({ - domType: 'button', domId: 'wh-otherBtn', domText: '更多设定', clickFunc: () => { - const html = `清空设置数据、请求通知权限、测试跨域请求`; - const popup = popupMsg(html, '更多设定'); - }, - isHide: true, - }); - - return setting_list; -} - -// 默认设置 -function setDefaultSettings(): void { - [ - // 开启翻译 - { key: 'transEnable', val: false }, - // 快速犯罪 - { key: 'quickCrime', val: true }, - // 任务助手 - { key: 'missionHint', val: true }, - // 小镇攻略 - { key: 'xmasTownWT', val: true }, - // 小镇提醒 - { key: 'xmasTownNotify', val: true }, - // 起飞爆e - { key: 'energyAlert', val: true }, - // 飞行闹钟 - { key: 'trvAlarm', val: true }, - // 啤酒提醒 - { key: '_15Alarm', val: true }, - // 捡垃圾助手 - { key: 'cityFinder', val: false }, - // 叠E保护 - { key: 'SEProtect', val: false }, - // PT一键购买 - { key: 'ptQuickBuy', val: false }, - // 光速拔刀 6-关闭 - { key: 'quickAttIndex', val: 2 }, - // 光速跑路 0-leave 1-mug 2-hos 3-关闭 - { key: 'quickFinishAtt', val: 3 }, - // 自动开打和结束 - { key: 'autoStartFinish', val: false }, - // 废弃 - { key: 'attRelocate', val: true }, - // 攻击自刷新 0-无间隔 1-5s 6-关闭 - { key: 'attReload', val: 6 }, - // 价格监视 - { key: 'priceWatcher', val: { xan: -1, pt: -1 } }, - // 开发者模式 - { key: 'isDev', val: false }, - // 啤酒提醒时间 - { key: '_15AlarmTime', val: 50 }, - // 4条转跳 - { key: 'barsRedirect', val: true }, - // 浮动存钱框 - { key: 'floatDepo', val: true }, - // 公司转跳存钱 - { key: 'companyRedirect', val: true }, - // 收起公司冰蛙效率表 - { key: 'companyBWCollapse', val: true }, - // 清除多余的脚本 - { key: 'removeScripts', val: true }, - // 海外警告 - { key: 'abroadWarning', val: true }, - // 落地转跳 - { key: 'landedRedirect', val: '' }, - // 任何位置一键存钱 - { key: 'companyDepositAnywhere', val: false }, - - // 危险行为⚠️ - { key: 'dangerZone', val: false }, - ].forEach(df => { - if (typeof getWhSettingObj()[df.key] !== typeof df.val) setWhSetting(df.key, df.val); - }); -} \ No newline at end of file +// import getWhSettingObj from "./func/utils/getWhSettingObj"; +// import setWhSetting from "./func/utils/setWhSetting"; +// import addStyle from "./func/utils/addStyle"; +// import WHNotify from "./func/utils/WHNotify"; +// import getScriptEngine from "./func/utils/getScriptEngine"; +// import COFetch from "./func/utils/COFetch"; +// 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"; +// import Global from "./class/Global" +// +// export default function zhongIcon(glob: Global) { +// setDefaultSettings(); +// // 菜单node +// glob.$zhongNode = initIcon(getMenuItems(glob)); +// } +// +// interface MenuItemConfig { +// tagName?: string; +// domType: 'button' | 'plain' | 'checkbox' | 'select'; +// domId?: string; +// domText?: string; +// clickFunc?: (ev?) => void; +// domHTML?: string; +// tip?: string; +// dictName?: string; +// changeEv?: (ev) => void; +// domSelectOpt?: { domVal: string, domText: string }[]; +// } +// +// interface EventWrapper { +// onEv: boolean; +// daysLeft: number; +// events: Event[]; +// current?: Event; +// next?: Event; +// html?: string; +// } +// +// interface Event { +// start: number[]; +// end: number[]; +// name: string; +// eff: string; +// } +// +// // 元素生成器 +// 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 { version } = window.WHPARAMS; +// if ((self !== top) || !!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.info(e.target); +// if (e.target === zhong_node.querySelector('#wh-trans-icon-btn')) return; +// if (!zhong_node.contains(e.target)) { +// log.info('移除事件监听器'); +// document.body.removeEventListener('click', click_func); +// zhong_node.classList.remove('wh-icon-expanded'); +// } +// }; +// if (zhong_node.classList.contains('wh-icon-expanded')) { +// log.info('添加事件监听器'); +// document.body.addEventListener('click', click_func); +// } else { +// log.info('移除事件监听器'); +// 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; +// } +// +// // 菜单 +// function getMenuItems(glob): MenuItemConfig[] { +// const date = new Date(); +// +// const menu_list: MenuItemConfig[] = []; +// +// // 欢迎 显示玩家id +// if (glob.player_info.userID !== 0) { +// menu_list.push({ +// domType: 'plain', +// domId: 'wh-trans-welcome', +// domHTML: `欢迎 ${ glob.player_info.playername }[${ glob.player_info.userID }] 大佬`, +// }); +// } +// // 节日 +// let fest_date_html = ': '; +// { +// // 节日字典 +// const dict = { +// '0105': { name: '周末自驾游', eff: '获得双倍的赛车点数与赛车技能等级增益' }, +// '0114': { name: '情人节', eff: '使用爱情果汁(Love Juice)后获得降低攻击与复活的能量消耗的增益' }, +// '0204': { name: '员工激励日', eff: '获得三倍的工作点数与火车增益' }, +// '0217': { name: '圣帕特里克日', eff: '获得双倍的酒类效果增益,城市中可以捡到绿色世涛(Green Stout)' }, +// '0320': { name: '420日', eff: '获得三倍的大麻(Cannabis)效果增益' }, +// '0418': { name: '博物馆日', eff: '获得10%提高的博物馆PT兑换增益' }, +// '0514': { name: '世界献血日', eff: '获得减半的抽血CD和扣血增益' }, +// '0611': { name: '世界人口日', eff: '获得双倍的通过攻击获取的经验的增益' }, +// '0629': { name: '世界老虎日', eff: '获得5倍的狩猎技能增益' }, +// '0705': { name: '国际啤酒节', eff: '获得5倍的啤酒物品效果增益' }, +// '0827': { name: '旅游节', eff: '获得双倍的起飞后物品携带容量增益' }, +// '0915': { name: '饮料节', eff: '获得双倍的能量饮料效果增益' }, +// '1014': { name: '世界糖尿病日', eff: '获得三倍的糖类效果增益' }, +// '1015': { name: '周年庆', eff: '左上角的TORN图标可以食用' }, +// '1025': { name: '黑色星期五', eff: '某些商家将提供1元购活动' }, +// '1114': { name: '住院日', eff: '获得降低75%的住院时间增益' }, +// }; +// menu_list.fest_date_dict = dict; +// menu_list.fest_date_list = Object.keys(dict); +// const formatMMDD = (m, d) => { +// const MM = m < 10 ? `0${ m }` : m.toString(); +// const DD = d < 10 ? `0${ d }` : d.toString(); +// return MM + DD; +// } +// const fest_date_key = formatMMDD(date.getUTCMonth(), date.getUTCDate()); +// if (dict[fest_date_key]) fest_date_html += `今天 - ${ dict[fest_date_key]['name'] }()`; +// else { +// // 月日列表 +// let list = Object.keys(dict); +// list.push(fest_date_key); +// // 下个节日的位置 +// const index: number = list.sort().indexOf(fest_date_key) + 1; +// // 下个节日obj +// const next_fest_date = dict[list[index] || list[0]]; +// // 下个节日的时间 +// let next = new Date( +// index !== list.length ? date.getUTCFullYear() : date.getUTCFullYear() + 1, +// (list[index !== list.length ? index : 0] as any).slice(0, 2) | 0, +// (list[index !== list.length ? index : 0] as any).slice(2) | 0, +// 8 +// ).getTime(); +// // 剩余天数 +// const left = (next - date.getTime()) / 86400000 | 0; +// fest_date_html += `${ left }天后 - ${ next_fest_date.name }()`; +// } +// } +// menu_list.push({ +// domType: 'plain', +// domId: 'wh-trans-fest-date', +// domHTML: fest_date_html, +// }); +// // 活动 +// let eventObj: EventWrapper = { +// onEv: false, +// daysLeft: Infinity, +// events: [ +// { +// start: [0, 17, 8], end: [0, 24, 8], +// name: '捡垃圾周', +// eff: '获得捡垃圾概率提升的增益', +// }, +// { +// start: [3, 5, 20], end: [3, 25, 20], +// name: '复活节狩猎', +// eff: '复活节彩蛋会随机出现,集齐10个可兑换金蛋和一个独特的头像框(章)。', +// }, +// { +// start: [5, 20, 20], end: [5, 29, 20], +// name: '狗牌', +// eff: '击败其他玩家以获得狗牌,小心保护你的狗牌。', +// }, +// { +// start: [6, 5, 20], end: [6, 25, 20], +// name: '托恩先生和托恩女士', +// eff: '上传你的真实图片,然后拿章', +// }, +// { +// start: [8, 5, 20], end: [8, 23, 20], +// name: '大逃杀', +// eff: '加入特定队伍后,攻击其他队伍玩家,存活下来的3个队伍可以拿章', +// }, +// { +// start: [9, 25, 20], end: [10, 1, 20], +// name: '不给糖就捣蛋', +// eff: '买篮子之后攻击其他玩家后会随机掉落糖果,可用于兑换许多高价值物品', +// }, +// { +// start: [11, 14, 20], end: [11, 31, 20], +// name: '圣诞小镇', +// eff: '在小镇中闲逛来获取随机掉落的物品', +// }, +// ], +// }; +// menu_list.events = eventObj.events; +// eventObj.events.forEach((obj, index) => { +// if (eventObj.onEv) return; +// // 当前年份 +// const nowYear = date.getFullYear(); +// // 当前遍历的活动开始时间 +// const start = new Date(nowYear, obj.start[0], obj.start[1], obj.start[2]); +// // 当前遍历的活动结束时间 +// const end = new Date(nowYear, obj.end[0], obj.end[1], obj.end[2]); +// // 当前处于活动中 +// if (start < date && date < end) { +// eventObj.onEv = true; +// eventObj.daysLeft = (end.getTime() - date.getTime()) / 86400000 | 0; +// eventObj.current = obj; +// } +// // 当前没有活动 +// else { +// // 当前遍历的活动如果已经经过了,那么下次活动就是遍历的下一个活动对象,否则为当前活动。 +// // 如果本年度活动都经过了,那么下次活动是列表的第一个活动对象 +// const next = end < date ? eventObj.events[index + 1] || eventObj.events[0] : obj; +// // 经过了最后一个活动所以下次活动开始时间是第二年 +// const start = new Date(next !== obj && index === eventObj.events.length - 1 ? nowYear + 1 : nowYear, next.start[0], next.start[1], next.start[2]); +// const daysLeft = (start.getTime() - date.getTime()) / 86400000 | 0; +// if (0 <= daysLeft && daysLeft < eventObj.daysLeft) { +// eventObj.daysLeft = daysLeft; +// eventObj.next = next; +// } +// } +// }); +// eventObj.html = ': '; +// eventObj.onEv +// ? eventObj.html += `${ eventObj.current.name }() - 剩余${ eventObj.daysLeft }天` +// : eventObj.html += `${ eventObj.daysLeft }天后 - ${ eventObj.next.name }()`; +// menu_list.push({ +// domType: 'plain', +// domId: 'wh-trans-event-cont', +// domHTML: eventObj.html, +// }); +// // 飞花库存 +// menu_list.push({ +// domType: 'button', +// domId: 'wh-foreign-stock-btn', +// domText: '🌸 飞花库存', +// clickFunc: async function (e) { +// e.target.blur(); +// forStock(glob).then(); +// }, +// }); +// // 一键起飞 +// menu_list.push({ +// domType: 'button', +// domId: 'wh-quick-fly-btn', +// domText: '✈️ 一键起飞', +// clickFunc: async function () { +// if (window.hasWHQuickFlyOpt) return; +// window.hasWHQuickFlyOpt = true; +// addStyle(`#wh-quick-fly-opt{ +// position:fixed; +// left:64px; +// top:64px; +// background: #008000db; +// padding: 8px; +// border-radius: 4px; +// box-shadow: 0 0 5px 1px #ffffff29; +// color: white; +// font-size: 15px; +// width: 220px; +// z-index: 199999; +// } +// #wh-quick-fly-opt p{margin:4px 0;} +// #wh-quick-fly-opt a{ +// cursor: pointer; +// border: 1px solid; +// padding: 4px; +// display: inline-block; +// border-radius: 2px; +// } +// #wh-quick-fly-opt label{ +// display:block; +// } +// #wh-quick-fly-opt select{ +// width: 100%; +// padding: 6px; +// margin: 4px 0; +// } +// #wh-quick-fly-opt button{ +// font-size: 16px; +// color: white; +// cursor: pointer; +// float: right; +// background: #00BCD4; +// padding: 8px; +// border-radius: 4px; +// } +// #wh-quick-fly-opt.wh-quick-fly-opt-hide *{ +// display: none; +// } +// #wh-quick-fly-opt.wh-quick-fly-opt-hide input{ +// display: inline-block; +// } +// info{display:block;} +// `); +// const node = document.createElement('div'); +// node.id = 'wh-quick-fly-opt'; +// node.innerHTML = ` +// +//

主要用途:出院秒飞

+//

点起飞,页面加载完成后会马上飞走

+//
+//
+// +// +//

查看花偶库存

+//

注:需要验证时无法起飞

+// +//
+// `; +// const [dest_node, type_node] = node.querySelectorAll('select') as any as HTMLSelectElement[]; +// node.querySelector('button').addEventListener('click', () => { +// sessionStorage['wh-quick-fly'] = `${ dest_node.selectedIndex } ${ type_node.selectedIndex } ${ new Date().getTime() }`; +// if (!glob.href.contains('travelagency.php')) { +// WHNotify('正在转跳'); +// location.href = 'https://www.torn.com/travelagency.php'; +// } else { +// doQuickFly(); +// } +// }); +// node.querySelector('a').addEventListener('click', (e) => { +// e.preventDefault(); +// forStock(glob); +// }); +// node.querySelector('input').addEventListener('click', (e) => { +// node.classList.toggle('wh-quick-fly-opt-hide'); +// const el = e.target as HTMLInputElement; +// el.value = el.value === ' - ' ? ' + ' : ' - '; +// }); +// const info_node = node.querySelector('info'); +// const time_predict = document.createElement('p'); +// const yaoCD = document.createElement('p'); +// info_node.append(time_predict); +// info_node.append(yaoCD); +// const predict = [ +// ['~54分', '~36分', '~26分', '~16分',], +// ['~1时10分', '~50分', '~36分', '~22分',], +// ['~1时22分', '~58分', '~40分', '~24分',], +// ['~4时28分', '~3时8分', '~2时14分', '~1时20分',], +// ['~5时18分', '~3时42分', '~2时40分', '~1时36分',], +// ['~5时34分', '~3时54分', '~2时46分', '~1时40分',], +// ['~5时50分', '~4时6分', '~2时56分', '~1时46分',], +// ['~7时30分', '~5时16分', '~3时46分', '~2时16分',], +// ['~8时4分', '~5时38分', '~4时2分', '~2时24分',], +// ['~9时2分', '~6时20分', '~4时30分', '~2时42分',], +// ['~9时54分', '~6时56分', '~4时58分', '~2时58分',], +// ]; +// const showTime = function () { +// time_predict.innerHTML = `往返时间:${ predict[dest_node.selectedIndex][type_node.selectedIndex] }`; +// } +// dest_node.addEventListener('change', showTime); +// type_node.addEventListener('change', showTime); +// document.body.append(node); +// showTime(); +// yaoCD.innerHTML = `药CD剩余:${ getYaoCD() }`; +// }, +// }); +// // NPC LOOT +// menu_list.push({ +// domType: 'button', +// domId: 'wh-npc-loot-btn', +// domText: '🔫 LOOT', +// clickFunc: function (e) { +// e.target.blur(); +// const insert = `

点击开打:

+// +//
stock.png
`; +// popupMsg(insert, 'NPC LOOT'); +// }, +// tip: '显示5个可击杀NPC的开打时间', +// }); +// // 查看NNB +// menu_list.push({ +// domType: 'button', +// domId: 'wh-nnb-info', +// domText: '👮‍ 查看NNB', +// clickFunc: function (e) { +// e.target.blur(); +// const insert = ` +//

+//

NNBNatural Nerve Bar)意思是:扣除所有加成后,玩家本身的犯罪条上限,可用于衡量大佬隐藏的犯罪技能等级

+//

一般来说,左侧红色的犯罪条(Nerve Bar/NB)的上限都是包含加成的,如来自帮派、天赋的加成等。额外的加成不会影响玩家的犯罪技能

+//

查看NNB的方法很简单,在Torn主页面的最下方有一栏Perks,NB-Perks=NNB

+//
+//

以下是两种计算NNB的方法:

+// +// +//
+// +// `; +// const popup = popupMsg(insert, '查看NNB'); +// const select = popup.querySelector('input'); +// const node = popup.querySelector('p'); +// popup.querySelector('button').addEventListener('click', ev => { +// let target = ev.target as HTMLInputElement; +// target.style.display = 'none'; +// node.innerHTML = '加载中'; +// // API 计算 +// if (select.checked) { +// const api_key = glob.isPDA ? glob.PDA_APIKey : window.localStorage.getItem('APIKey'); +// fetch(`https://api.torn.com/user/?selections=bars,perks&key=${ api_key }`) +// .then(res => res.json()) +// .then(data => { +// if (data['error']) { +// node.innerHTML = `出错了 ${ JSON.stringify(data['error']) }`; +// target.style.display = null; +// return; +// } +// let nb = data['nerve']['maximum']; +// let perks = 0; +// Object.values(data).forEach(val => { +// (val instanceof Array) && val.forEach(s => { +// s = s.toLowerCase(); +// s.includes('maximum nerve') && (perks += (new RegExp('[0-9].').exec(s))[0] | 0) +// }) +// }); +// node.innerHTML = `NNB: ${ nb - perks }`; +// target.style.display = null; +// }); +// } +// // 主页计算 +// else { +// if (window.location.href.includes('index.php') && document.title.includes('Home')) { +// let nb = (document.querySelector('#barNerve p[class^="bar-value___"]').innerText.split('/')[1]) | 0; +// let perks = 0; +// document.querySelectorAll('#personal-perks li').forEach(elem => { +// const str = elem.innerText.toLowerCase(); +// str.includes('maximum nerve') && (perks += (/[0-9]./.exec(str) as any)[0] | 0) +// }); +// node.innerHTML = `NNB: ${ nb - perks }`; +// target.style.display = null; +// return; +// } +// node.innerHTML = '不在主页面,点击前往'; +// target.style.display = null; +// } +// }); +// }, +// }); +// // 常用链接 +// menu_list.push({ +// domType: 'button', +// domId: 'wh-link-collection', +// domText: '🔗 常用链接', +// clickFunc: function (e) { +// if (!this.styleAdded) { +// addStyle(` +// .wh-link-collection-cont a{ +// display: inline-block; +// border: solid 1px #b3b3b3; +// border-radius: 4px; +// margin: 0 5px 2px 0; +// padding: 4px 8px; +// text-align:center; +// background: #efefef; +// background: linear-gradient(#f1f1f1,#e3e3e3); +// color:black !important; +// } +// .wh-link-collection-cont span{ +// display: block; +// /*padding: 0 4px 8px;*/ +// } +// .wh-link-collection-cont .wh-link-collection-img{ +// display: block; +// width:60px; +// height:30px; +// background-size: 100% auto !important; +// } +// `); +// this.styleAdded = true; +// } +// e.target.blur(); +// const quick_link_dict = []; +// // 生存手册 +// quick_link_dict.push({ +// name: '生存手册', +// url: 'https://docs.qq.com/doc/DTVpmV2ZaRnB0RG56', +// new_tab: true, +// img: 'https://www.torn.com/images/items/293/medium.png', +// }); +// // 买啤酒 +// quick_link_dict.push({ +// name: '抢啤酒', +// url: 'https://www.torn.com/shops.php?step=bitsnbobs', +// new_tab: true, +// img: 'https://www.torn.com/images/items/180/medium.png', +// }); +// // 买XAN +// quick_link_dict.push({ +// name: '买XAN', +// url: 'https://www.torn.com/imarket.php#/p=shop&step=shop&type=&searchname=Xanax', +// new_tab: true, +// img: 'https://www.torn.com/images/items/206/medium.png', +// }); +// // 起飞 +// quick_link_dict.push({ +// name: '起飞', +// url: 'https://www.torn.com/travelagency.php', +// new_tab: true, +// img: 'https://www.torn.com/images/items/396/medium.png', +// }); +// // 买PT +// quick_link_dict.push({ +// name: '买PT', +// url: 'https://www.torn.com/pmarket.php', +// new_tab: true, +// img: 'https://www.torn.com/images/items/722/medium.png', +// }); +// // 租PI +// quick_link_dict.push({ +// name: '租PI', +// url: 'https://www.torn.com/properties.php?step=rentalmarket#/property=13', +// new_tab: false, +// img: 'https://www.torn.com/images/v2/properties/350x230/350x230_default_private_island.png', +// }); +// // 找工作 +// quick_link_dict.push({ +// name: '找工作', +// url: 'https://www.torn.com/joblist.php#!p=main', +// new_tab: false, +// img: 'https://www.torn.com/images/items/421/medium.png', +// }); +// // 下悬赏 +// quick_link_dict.push({ +// name: '下悬赏', +// url: 'https://www.torn.com/bounties.php#/p=add', +// new_tab: false, +// img: 'https://www.torn.com/images/items/431/medium.png', +// }); +// let insert = '

'; +// quick_link_dict.forEach(el => { +// insert += `${ el.name }`; +// }); +// insert += '

' +// let popup = popupMsg(insert, '常用链接'); +// popup.classList.add('wh-link-collection-cont'); +// popup.addEventListener('click', ev => { +// let target = ev.target as HTMLElement; +// if (target.tagName.toLowerCase() === 'a' || target.tagName.toLowerCase() === 'span') { +// popup.close(); +// } +// }); +// }, +// }); +// // 飞贼 +// menu_list.push({ +// domType: 'button', +// domId: 'wh-gs-btn', +// domText: '🐏 飞贼小助手', +// clickFunc: function (e) { +// e.target.blur(); +// loadGS(getScriptEngine(glob)); +// }, +// tip: '加载从PC端移植的伞佬的油猴版飞贼小助手', +// }); +// // 物品价格监视 +// menu_list.push({ +// domType: 'button', +// domId: 'wh-price-watcher-btn', +// domText: '💊 价格监视', +// clickFunc: function () { +// const watcher_conf = getWhSettingObj()['priceWatcher']; +// const pre_str = JSON.stringify(watcher_conf); +// const html = ` +//

输入需要监视的价格,低于该价格发出通知,-1为关闭

+//

注:需要APIKey,当前可用APIKey为
+// (来自冰蛙)
+// (来自PDA) +//

+//

PT

+//

XAN

+//

+// `; +// const popup = popupMsg(html, '价格监视设置'); +// popup.querySelector('button').onclick = () => { +// 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(); +// }; +// } +// }); +// // 小窗犯罪 +// menu_list.push({ +// domType: 'button', +// domId: 'wh-crime-iframe-btn', +// domText: '🤑 小窗犯罪', +// clickFunc: function () { +// // 弹出小窗口 +// const ifHTML = ``; +// const popup_insert = `

加载中请稍后${ loading_gif_html() }

`; +// const $popup = popupMsg(popup_insert, '小窗快速犯罪'); +// // 运行状态node +// let loading_node = $popup.querySelector('p:first-of-type'); +// // if容器 +// const if_cont = $popup.querySelector('#wh-quick-crime-if-container'); +// if_cont.innerHTML = ifHTML; +// +// // if内未加载脚本时插入的快捷crime node +// const mobile_prepend_node = document.createElement('div'); +// mobile_prepend_node.classList.add('wh-translate'); +// mobile_prepend_node.innerHTML = `
快捷操作:
+//
+// +// +// +//
+//
+// +// +// +//
+//
+// +// +// +//

`; +// +// // if对象加载后运行 +// let cIframe = $popup.querySelector('iframe'); +// +// // 加载状态 +// const if_onload_func = () => { +// // if内部文档对象 +// const ifDocu = cIframe.contentWindow.document; +// // 内部插件运行flag +// const ifWH = cIframe.contentWindow.WHTRANS; +// // 文档加载完成后移除 +// if (!!loading_node) loading_node.remove(); +// // 文档加载完成后才显示if +// cIframe.style.display = 'block'; +// // 验证码flag +// const isValidate = ifDocu.querySelector('h4#skip-to-content').innerText.toLowerCase().includes('validate'); +// // 如果iframe内部未运行脚本 +// if (ifWH === undefined) { +// // 隐藏顶部 +// elementReady('#header-root', ifDocu).then(e => e.style.display = 'none'); +// // 隐藏4条 +// elementReady('#sidebarroot', ifDocu).then(e => e.style.display = 'none'); +// // 隐藏聊天 +// elementReady('#chatRoot', ifDocu).then(e => e.style.display = 'none'); +// // 非验证码页面隐藏滚动条 +// if (!isValidate) ifDocu.body.style.overflow = 'hidden'; +// // 调整容器位置 +// elementReady('.content-wrapper', ifDocu).then(elem => { +// // 加入 +// elem.prepend(mobile_prepend_node); +// elem.style.margin = '0px'; +// elem.style.position = 'absolute'; +// elem.style.top = '-35px'; +// new MutationObserver((m, o) => { +// o.disconnect(); +// if (!elem.querySelector('.wh-translate')) elem.prepend(mobile_prepend_node); +// o.observe(elem, { childList: true, subtree: true }); +// }) +// .observe(elem, { childList: true, subtree: true }); +// }); +// // 隐藏返回顶部按钮 +// elementReady('#go-to-top-btn button', ifDocu).then(e => e.style.display = 'none'); +// } +// }; +// cIframe.onload = if_onload_func; +// +// // 超时判断 +// let time_counter = 0; +// let time_out_id = window.setInterval(() => { +// loading_node = $popup.querySelector('p:first-of-type'); +// if (!loading_node) { +// clearInterval(time_out_id); +// time_out_id = undefined; +// return; +// } +// time_counter++; +// if (time_counter > 0 && !loading_node.querySelector('button')) { +// const reload_btn = document.createElement('button'); +// reload_btn.innerHTML = '重新加载'; +// reload_btn.onclick = () => { +// reload_btn.remove(); +// time_counter = 0; +// if_cont.innerHTML = null; +// if_cont.innerHTML = ifHTML; +// cIframe = $popup.querySelector('iframe'); +// cIframe.onload = if_onload_func; +// }; +// loading_node.append(reload_btn); +// } +// }, 1000); +// } +// }); +// // 危险行为开关⚠️ +// menu_list.push({ +// domType: 'button', +// domId: 'wh-danger-zone', +// domText: '⚠️ 危险功能', +// clickFunc: function (e) { +// e.target.blur(); +// const insert = `

即将打开危险功能,使用这些功能可能会造成账号封禁。请自行考虑是否使用。

+//

+//
`; +// const popup = popupMsg(insert, '⚠️警告'); +// const warning_check = popup.querySelector('input'); +// const ok_btn = popup.querySelector('button'); +// warning_check.onchange = () => ok_btn.disabled = false; +// ok_btn.onclick = () => { +// setWhSetting('dangerZone', warning_check.checked); +// popup['close'](); +// window.location.reload(); +// }; +// }, +// }); +// // 传单助手 +// menu_list.push({ +// domType: 'button', +// domId: '', +// domText: '📜️ 传单助手', +// clickFunc: adHelper +// }); +// // 守望者 +// menu_list.push({ +// domType: 'button', +// domId: '', +// domText: '🛡️ 守望者', +// clickFunc: function () { +// safeKeeper(); +// }, +// }); +// // 更新历史 +// menu_list.push({ +// domType: 'button', domId: '', domText: '🐞 更新历史', clickFunc: async () => { +// let popup = popupMsg( +// '更新历史:
https://gitlab.com/JJins/wuhu-torn-helper/-/blob/dev/CHANGELOG.md
', +// '更新历史' +// ); +// popup.classList.add('wh-changelog'); +// let progressBar = document.createElement('div'); +// progressBar.style.height = '2px'; +// progressBar.style.width = '1%'; +// progressBar.style.backgroundColor = 'red'; +// let progressText = document.createElement('p'); +// progressText.innerText = '加载更新文件……'; +// progressText.style.textAlign = 'center'; +// let style = document.createElement('style'); +// style.innerHTML = `.wh-changelog h2,.wh-changelog h3,.wh-changelog h4 {margin:8px 0;}.wh-changelog li{list-style: inside;}`; +// +// popup.append(progressBar, progressText, style); +// let update = await COFetch('https://gitlab.com/JJins/wuhu-torn-helper/-/raw/dev/CHANGELOG.md?' + Date.now()); +// progressBar.style.width = '60%'; +// progressText.innerText = '解析中……'; +// let md = mdParse(update); +// popup.append(md); +// progressBar.style.width = '100%'; +// progressText.innerText = '加载完成'; +// +// setTimeout(() => { +// progressBar.remove(); +// progressText.remove() +// }, 3000); +// }, +// }); +// // 助手设置 +// menu_list.push({ +// domType: 'button', domId: '', domText: '⚙️ 助手设置', clickFunc: () => { +// let { $zhongNode } = glob; +// $zhongNode.setting_root = document.createElement('div'); +// $zhongNode.setting_root.classList.add('gSetting'); +// getSettingItems(glob).forEach(set => elemGenerator(set, $zhongNode.setting_root)); +// let pop = popupMsg('', '芜湖助手设置'); +// pop.appendChild($zhongNode.setting_root); +// // 本日不提醒 +// $zhongNode.setting_root.querySelector('#wh-qua-alarm-check-btn').addEventListener('click', glob.beer.skip_today); +// // 开发详情按钮 +// if (log.debug()) $zhongNode.setting_root.querySelector('button#wh-devInfo').onclick = () => { +// const date = new Date(); +// let os = '未知'; +// try { +// os = window.navigator.userAgentData.platform || window.navigator.platform +// } catch { +// } +// +// const insert = ` +// +// +// +// +// +// +// +// +// +// +//
URL${ window.location.href }
页面尺寸${ window.innerWidth }x${ window.innerHeight }
设备类型${ getDeviceType().toUpperCase() }
脚本运行方式${ { 'gm': '油猴', 'raw': '直接运行', 'pda': 'TornPDA' }[getScriptEngine(glob)] }
时间${ date.getFullYear() }/${ date.getMonth() + 1 }/${ date.getDate() } ${ date.getHours() }:${ date.getMinutes() }:${ date.getSeconds() }
插件版本${ glob.version }
操作系统${ os }
UA${ window.navigator.userAgent }
用户ID${ glob.player_info.userID }
用户名${ glob.player_info.playername }
+// `; +// pop.close(); +// popupMsg(insert, '开发者详情'); +// }; +// (window['initializeTooltip']) && (window['initializeTooltip']('#wh-popup-cont', 'white-tooltip')); +// }, +// }); +// // 测试 +// if (log.debug()) menu_list.push({ +// domType: 'button', +// domId: '', +// domText: '📐️ 测试', +// clickFunc: async function () { +// let res = await COFetch('https://gitlab.com/JJins/wuhu-torn-helper/-/raw/dev/CHANGELOG.md') +// log.info(mdParse(res)) +// }, +// }); +// +// return menu_list; +// } +// +// // 设置 +// function getSettingItems(glob): MenuItemConfig[] { +// const date = new Date(); +// +// let setting_list = []; +// +// // 12月时加入圣诞小镇选项 +// if (date.getMonth() === 11) { +// setting_list.push({ +// domType: 'plain', +// domId: '', +// domHTML: '圣诞小镇', +// tagName: 'h4', +// }) +// setting_list.push({ +// domType: 'checkbox', +// domId: 'wh-xmastown-wt', +// domText: ' 圣诞小镇攻略', +// dictName: 'xmasTownWT', +// isHide: true, +// }); +// setting_list.push({ +// domType: 'checkbox', +// domId: 'wh-xmastown-notify', +// domText: ' 圣诞小镇物品提示', +// dictName: 'xmasTownNotify', +// isHide: true, +// }); +// } +// +// // 翻译 +// setting_list.push({ +// domType: 'plain', +// domId: '', +// domHTML: '翻译', +// tagName: 'h4', +// }); +// // 开启翻译 +// setting_list.push({ +// domType: 'checkbox', +// domId: 'wh-trans-enable', +// domText: ' 开启翻译', +// dictName: 'transEnable', +// isHide: true, +// }); +// // 更新翻译词库 +// setting_list.push({ +// domType: 'button', +// domId: '', +// domText: '更新翻译词库', +// clickFunc: updateTransDict +// }); +// +// // 战斗优化 +// setting_list.push({ +// domType: 'plain', +// domId: '', +// domHTML: '战斗优化', +// tagName: 'h4', +// }); +// // 光速拔刀 +// setting_list.push({ +// domType: 'select', +// domId: 'wh-quick-attack-index', +// domText: '光速拔刀 ', +// domSelectOpt: [ +// { +// domVal: 'pri', +// domText: '主手', +// }, +// { +// domVal: 'sec', +// domText: '副手', +// }, +// { +// domVal: 'wea', +// domText: '近战', +// }, +// { +// domVal: 'gre', +// domText: '手雷', +// }, +// { +// domVal: 'fis', +// domText: '拳头', +// }, +// { +// domVal: 'kic', +// domText: '脚踢', +// }, +// { +// domVal: 'none', +// domText: '关闭', +// }, +// ], +// dictName: 'quickAttIndex', +// isHide: true, +// tip: '将Start Fight按钮移动到指定格子上', +// }); +// // 光速跑路 +// setting_list.push({ +// domType: 'select', +// domId: 'wh-quick-mug', +// domText: '光速跑路 ', +// domSelectOpt: [ +// { +// domVal: 'leave', +// domText: '跑路(LEAVE)', +// }, +// { +// domVal: 'mug', +// domText: '打劫(MUG)', +// }, +// { +// domVal: 'hosp', +// domText: '住院(HOSP)', +// }, +// { +// domVal: 'none', +// domText: '关闭', +// }, +// ], +// dictName: 'quickFinishAtt', +// isHide: true, +// tip: '将结束后指定按钮移动到上面指定的格子上', +// }); +// // 攻击链接转跳 +// setting_list.push({ +// domType: 'checkbox', +// domId: 'wh-attack-relocate', +// domText: ' 真·攻击界面转跳', +// dictName: 'attRelocate', +// tip: '在无法打开攻击界面的情况下依然可以转跳到正确的攻击页面', +// isHide: true, +// }); +// +// // 飞行 +// setting_list.push({ +// domType: 'plain', +// domId: '', +// domHTML: '飞行', +// tagName: 'h4', +// }); +// // 起飞警告 +// setting_list.push({ +// domType: 'checkbox', +// domId: 'wh-energy-alert', +// domText: ' 起飞爆E警告', +// dictName: 'energyAlert', +// tip: '起飞前计算来回是否会爆体,红字警告', +// isHide: true, +// }); +// // 飞行闹钟 +// setting_list.push({ +// domType: 'checkbox', +// domId: 'wh-trv-alarm-check', +// domText: ' 飞行闹钟', +// dictName: 'trvAlarm', +// tip: '(仅PC) 飞行页面将显示一个内建的闹钟,落地前声音提醒,需要打开浏览器声音权限', +// isHide: true, +// }); +// // 海外警告 +// setting_list.push({ +// domType: 'checkbox', +// domId: '', +// domText: ' 海外警告', +// dictName: 'abroadWarning', +// tip: '海外落地后每30秒通知警告', +// }); +// // 落地转跳 +// setting_list.push({ domType: 'button', domId: '', domText: '落地转跳', clickFunc: landedRedirect }); +// +// // 公司 +// setting_list.push({ +// domType: 'plain', +// domId: '', +// domHTML: '公司', +// tagName: 'h4', +// }); +// // 浮动存钱框 +// setting_list.push({ +// domType: 'checkbox', +// domId: '', +// domText: ' 浮动存钱框', +// dictName: 'floatDepo', +// tip: '打开公司或帮派的存钱页面后存钱框将浮动显示', +// }); +// // 公司转跳存钱 +// setting_list.push({ +// domType: 'checkbox', +// domId: '', +// domText: ' 公司转跳存钱', +// dictName: 'companyRedirect', +// tip: '打开公司页面时自动打开存钱选项卡', +// }); +// // 收起公司冰蛙效率表 +// setting_list.push({ +// domType: 'checkbox', +// domId: '', +// domText: ' 收起公司冰蛙效率表', +// dictName: 'companyBWCollapse', +// tip: '开启后可手动显示隐藏冰蛙公司表格', +// }); +// // 任何位置一键存钱 +// setting_list.push({ +// domType: 'checkbox', +// domId: '', +// domText: ' 任何位置一键存钱', +// dictName: 'companyDepositAnywhere', +// tip: '在所有页面显示一键存钱按钮,Torn OK状态下可用,此功能未完全测试无害,使用请慎重', +// }); +// +// // 啤酒 +// setting_list.push({ +// domType: 'plain', +// domId: '', +// domHTML: '啤酒', +// tagName: 'h4', +// }); +// // 啤酒提醒 +// setting_list.push({ +// domType: 'checkbox', +// domId: 'wh-qua-alarm-check', +// domText: ' 啤酒提醒 ', +// dictName: '_15Alarm', +// tip: '每小时的整15分钟的倍数时通知提醒抢啤酒或者血包', +// isHide: true, +// changeEv: function (ev) { +// ev.target.checked ? glob.beer.start() : glob.beer.stop(); +// }, +// }); +// // 啤酒提醒状态 +// setting_list.push({ +// domType: 'button', +// domId: '', +// domText: '啤酒提醒状态', +// clickFunc: function () { +// WHNotify(`啤酒提醒${ glob.beer.status() }`); +// } +// }); +// // 啤酒提醒时间 +// setting_list.push({ +// domType: 'button', +// domId: '', +// domText: '啤酒提醒时间设定', +// // tip: '通知提前时间', +// clickFunc: function () { +// 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: 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 = glob.beer.is_running(); +// glob.beer.set_time(num); +// if (before_state) glob.beer.start(); +// popup.close(); +// }); +// popup.appendChild(confirm); +// }, +// }); +// +// // 其他 +// setting_list.push({ +// domType: 'plain', +// domId: '', +// domHTML: '其他', +// tagName: 'h4', +// }); +// // 任务助手 +// setting_list.push({ +// domType: 'checkbox', +// domId: 'wh-mission-lint', +// domText: ' 任务助手', +// dictName: 'missionHint', +// tip: 'Duke任务的一些中文小提示', +// isHide: true, +// }); +// // 捡垃圾助手 +// setting_list.push({ +// domType: 'checkbox', +// domId: 'wh-city-finder', +// domText: ' 捡垃圾助手', +// dictName: 'cityFinder', +// tip: '城市地图中放大显示物品并且估计价值', +// isHide: true, +// }); +// // 快速crime +// setting_list.push({ +// domType: 'checkbox', +// domId: 'wh-quick-crime', +// domText: ' 快速犯罪', +// dictName: 'quickCrime', +// tip: '显示快捷操作按钮,目前不支持自定义', +// isHide: true, +// }); +// // 叠E保护 +// setting_list.push({ +// domType: 'checkbox', +// domId: 'wh-SEProtect-check', +// domText: ' 叠E保护', +// dictName: 'SEProtect', +// tip: '隐藏健身房的锻炼按钮,防止误操作', +// isHide: true, +// }); +// // PT一键购买 +// setting_list.push({ +// domType: 'checkbox', +// domId: 'wh-ptQuickBuy-check', +// domText: ' PT一键购买', +// dictName: 'ptQuickBuy', +// tip: 'PT市场页面购买时跳过确认', +// isHide: true, +// }); +// // 4条转跳 +// setting_list.push({ +// domType: 'checkbox', +// domId: '', +// domText: ' 4条转跳', +// dictName: 'barsRedirect', +// tip: '点击4条时转跳对应页面', +// }); +// // 清除多余的脚本 +// setting_list.push({ +// domType: 'checkbox', +// domId: '', +// domText: ' 清除多余的脚本', +// dictName: 'removeScripts', +// tip: '清除Google相关脚本、顶部横幅等', +// }); +// // 危险行为⚠️ +// if (getWhSettingObj()['dangerZone'] === true) { +// // 攻击界面自刷新 +// setting_list.push({ +// domType: 'select', +// domId: 'wh-attack-reload', +// domText: '⚠️攻击界面自动刷新 ', +// dictName: 'attReload', +// domSelectOpt: [ +// { +// domVal: 'none', +// domText: '无间隔', +// }, +// { +// domVal: '1', +// domText: '约1s', +// }, +// { +// domVal: '2', +// domText: '约2s', +// }, +// { +// domVal: '3', +// domText: '约3s', +// }, +// { +// domVal: '4', +// domText: '约4s', +// }, +// { +// domVal: '5', +// domText: '约5s', +// }, +// { +// domVal: 'disabled', +// domText: '关闭', +// }, +// ], +// isHide: true, +// tip: '危险功能:接机时常用,将自动刷新页面直到目标落地', +// }); +// // 自动开打和结束 +// setting_list.push({ +// domType: 'checkbox', +// domId: 'wh-auto-start-finish', +// domText: ' ⚠️自动开打和结束', +// dictName: 'autoStartFinish', +// tip: '脚本将会自动按下战斗和结束按钮', +// isHide: true, +// }); +// } else { +// setWhSetting('autoStartFinish', false, false) +// setWhSetting('attReload', 6, false) +// } +// // dev +// setting_list.push({ +// domType: 'checkbox', +// domId: 'wh-dev-mode', +// domText: ` 开发者模式${ log.debug() ? ' ' : '' }`, +// dictName: 'isDev', +// isHide: true, +// }); +// // 更多设定 +// if (log.debug()) setting_list.push({ +// domType: 'button', domId: 'wh-otherBtn', domText: '更多设定', clickFunc: () => { +// const html = `清空设置数据、请求通知权限、测试跨域请求`; +// const popup = popupMsg(html, '更多设定'); +// }, +// isHide: true, +// }); +// +// return setting_list; +// } +// +// // 默认设置 +// function setDefaultSettings(): void { +// [ +// // 开启翻译 +// { key: 'transEnable', val: false }, +// // 快速犯罪 +// { key: 'quickCrime', val: true }, +// // 任务助手 +// { key: 'missionHint', val: true }, +// // 小镇攻略 +// { key: 'xmasTownWT', val: true }, +// // 小镇提醒 +// { key: 'xmasTownNotify', val: true }, +// // 起飞爆e +// { key: 'energyAlert', val: true }, +// // 飞行闹钟 +// { key: 'trvAlarm', val: true }, +// // 啤酒提醒 +// { key: '_15Alarm', val: true }, +// // 捡垃圾助手 +// { key: 'cityFinder', val: false }, +// // 叠E保护 +// { key: 'SEProtect', val: false }, +// // PT一键购买 +// { key: 'ptQuickBuy', val: false }, +// // 光速拔刀 6-关闭 +// { key: 'quickAttIndex', val: 2 }, +// // 光速跑路 0-leave 1-mug 2-hos 3-关闭 +// { key: 'quickFinishAtt', val: 3 }, +// // 自动开打和结束 +// { key: 'autoStartFinish', val: false }, +// // 废弃 +// { key: 'attRelocate', val: true }, +// // 攻击自刷新 0-无间隔 1-5s 6-关闭 +// { key: 'attReload', val: 6 }, +// // 价格监视 +// { key: 'priceWatcher', val: { xan: -1, pt: -1 } }, +// // 开发者模式 +// { key: 'isDev', val: false }, +// // 啤酒提醒时间 +// { key: '_15AlarmTime', val: 50 }, +// // 4条转跳 +// { key: 'barsRedirect', val: true }, +// // 浮动存钱框 +// { key: 'floatDepo', val: true }, +// // 公司转跳存钱 +// { key: 'companyRedirect', val: true }, +// // 收起公司冰蛙效率表 +// { key: 'companyBWCollapse', val: true }, +// // 清除多余的脚本 +// { key: 'removeScripts', val: true }, +// // 海外警告 +// { key: 'abroadWarning', val: true }, +// // 落地转跳 +// { key: 'landedRedirect', val: '' }, +// // 任何位置一键存钱 +// { key: 'companyDepositAnywhere', val: false }, +// +// // 危险行为⚠️ +// { key: 'dangerZone', val: false }, +// ].forEach(df => { +// if (typeof getWhSettingObj()[df.key] !== typeof df.val) setWhSetting(df.key, df.val); +// }); +// } \ No newline at end of file