This commit is contained in:
Liwanyi 2022-09-11 17:05:49 +08:00
parent cd38bd1b36
commit 5534776e77
19 changed files with 864 additions and 685 deletions

28
global.d.ts vendored
View File

@ -9,14 +9,30 @@ declare interface Window {
WHPARAMS?: any;
ReactDOM?: any;
hasWHQuickFlyOpt?: boolean;
// 插件运行标识
WHTRANS?: boolean;
Vue?: Function;
eval(exc: string): void;
/* TORN自带 */
addRFC(url: URL | string): string;
getAction(opt: TornGetActionParams): void;
initMiniProf(selector: string): void;
/* PDA自带 */
PDA_httpGet(url: URL | string): Promise<PDA_Response>;
PDA_httpPost(url: URL | string, init: any, body: any): Promise<PDA_Response>;
/* 油猴脚本引擎自带 */
GM_xmlhttpRequest(init: GM_RequestParams);
GM_getValue(k: string, def: any): any;
GM_setValue(k: string, v: any): void;
}
declare interface GM_RequestParams {
@ -51,4 +67,16 @@ declare interface Array<T> {
declare interface Navigator {
userAgentData?: any;
}
declare interface TornGetActionParams {
type: 'post' | 'get',
data: {
step: string,
id: number,
key: string,
type: string
},
success: Function,
before: Function
}

View File

@ -0,0 +1,43 @@
import popupMsg from "../utils/popupMsg";
// 传单助手
export default function adHelper() {
let popup = popupMsg('', '传单助手');
document.querySelector('#chatRoot').classList.remove('wh-hide');
let info = document.createElement('p');
let ad_input = document.createElement('textarea');
let place_button = document.createElement('button');
let clear_button = document.createElement('button');
let paste_button = document.createElement('button');
let style = document.createElement('style');
info.innerHTML = '打开多个聊天框后,点击<b>填写传单</b>将自动粘贴文本框中的内容进入所有<b>已打开的聊天框</b>。页面外的聊天框同样有效。';
ad_input.placeholder = '此处输入广告语';
ad_input.style.width = '100%';
ad_input.style.minHeight = '80px';
place_button.innerText = '填写传单';
clear_button.innerText = '清空所有聊天框';
paste_button.innerText = '粘贴剪切板';
style.innerHTML = '#chatRoot > div{z-index:199999 !important;}';
place_button.addEventListener('click', () => {
let chats = Array.from(document.querySelectorAll('#chatRoot textarea[name="chatbox2"]') as NodeListOf<HTMLTextAreaElement>);
chats.forEach(chat => chat.value = ad_input.value);
});
clear_button.addEventListener('click', () => {
let chats = document.querySelectorAll('#chatRoot textarea[name="chatbox2"]') as NodeListOf<HTMLTextAreaElement>;
chats.forEach(chat => chat.value = '');
});
paste_button.addEventListener('click', async () => {
ad_input.focus();
ad_input.value = await navigator.clipboard.readText();
});
popup.appendChild(style);
popup.appendChild(info);
popup.appendChild(ad_input);
popup.appendChild(document.createElement('br'));
popup.appendChild(place_button);
popup.appendChild(clear_button);
popup.appendChild(paste_button);
}

View File

@ -0,0 +1,43 @@
import WHNotify from "../utils/WHNotify";
// 一键起飞
export default function doQuickFly() {
// [id: dest, _type: (1...4), ts: timestamp]
const [_id, _type, ts] = sessionStorage['wh-quick-fly'].trim().split(' ');
if (new Date().getTime() - ts > 20000) {
WHNotify('超时,一键起飞计划已取消');
return;
}
const keynode = document.querySelector('div[data-id][data-key]');
if (!keynode) {
WHNotify('出错了,无法起飞,已取消');
return;
}
const _key = keynode.getAttribute('data-key');
window.getAction({
type: 'post',
data: {
step: 'travel',
id: getDestId(_id),
key: _key,
type: ['standard', 'airstrip', 'private', 'business'][_type]
},
success: function (str) {
WHNotify(str)
if (str.includes('err')) {
WHNotify('起飞出错了');
return;
}
window.location.href = 'https://www.torn.com/index.php'
},
before: function () {
}
});
delete sessionStorage['wh-quick-fly'];
}
// 起飞目的地id
function getDestId(dest) {
// 墨、开、加、夏、英、阿、瑞s、立本、祖、迪、南
return [2, 12, 9, 3, 10, 7, 8, 5, 6, 11, 4][dest];
}

View File

@ -0,0 +1,35 @@
import getWhSettingObj from "../utils/getWhSettingObj";
import setWhSetting from "../utils/setWhSetting";
import popupMsg from "../utils/popupMsg";
// 落地转跳
export default function landedRedirect() {
let p = document.createElement('p');
let input = document.createElement('input');
let buttonSave = document.createElement('button');
let buttonCmp = document.createElement('button');
let buttonFct = document.createElement('button');
let buttonTest = document.createElement('button');
let br = document.createElement('br');
p.innerHTML = '飞机落地后转跳的页面,关闭功能请置空:';
input.placeholder = 'URL';
input.value = getWhSettingObj()['landedRedirect'] || '';
input.style.display = 'block';
input.style.textAlign = 'left';
input.style.width = '100%';
input.style.padding = '8px';
input.style.margin = '8px -8px';
buttonSave.innerHTML = '保存';
buttonCmp.innerHTML = '填入公司金库';
buttonFct.innerHTML = '填入帮派金库金库';
buttonTest.innerHTML = '测试链接';
buttonSave.addEventListener('click', () => setWhSetting('landedRedirect', input.value));
buttonCmp.addEventListener('click', () => input.value = 'https://www.torn.com/companies.php#/option=funds');
buttonFct.addEventListener('click', () => input.value = 'https://www.torn.com/factions.php?step=your#/tab=armoury');
buttonTest.addEventListener('click', () => window.open(input.value));
let node = popupMsg('', '落地转跳');
node.append(p, input, buttonSave, br, buttonCmp, buttonFct, buttonTest);
}

119
src/func/module/loadGS.ts Normal file
View File

@ -0,0 +1,119 @@
import UserScriptEngine from "../../enum/UserScriptEngine";
import WHNotify from "../utils/WHNotify";
import addStyle from "../utils/addStyle";
import COFetch from "../utils/COFetch";
import log from "../utils/log";
// gs loader
export default function loadGS(use) {
if (use === UserScriptEngine.PDA) {
let ifr: HTMLIFrameElement = document.querySelector('#wh-gs-loader-ifr');
if (ifr) {
WHNotify('飞贼小助手已经加载了');
return;
}
const container = document.createElement('div');
container.id = 'wh-gs-loader';
ifr = document.createElement('iframe');
ifr.id = 'wh-gs-loader-ifr';
ifr.src = 'https://www.torn.com/crimes.php';
container.append(ifr);
document.body.append(container);
addStyle(`
#wh-gs-loader {
position:fixed;
top:0;
left:0;
z-index:100001;
}
`);
let notify = WHNotify('加载中');
ifr.onload = () => {
notify.close();
const _window = ifr.contentWindow;
const _docu = _window.document;
_docu.head.innerHTML = '';
_docu.body.innerHTML = '';
notify = WHNotify('加载依赖');
COFetch('https://cdn.staticfile.org/vue/2.2.2/vue.min.js')
.then(vuejs => {
notify.close();
_window.eval(vuejs);
_window.GM_getValue = (k, v = undefined) => {
const objV = JSON.parse(_window.localStorage.getItem('wh-gs-storage') || '{}')[k];
return objV || v;
};
_window.GM_setValue = (k, v) => {
const obj = JSON.parse(_window.localStorage.getItem('wh-gs-storage') || '{}');
obj[k] = v;
_window.localStorage.setItem('wh-gs-storage', JSON.stringify(obj));
};
_window.GM_xmlhttpRequest = function (opt) {
// 暂不适配pda post
if (opt.method.toLowerCase() === 'post') return;
COFetch(opt.url).then(res => {
const obj = {
responseText: res
};
opt.onload(obj);
});
};
notify = WHNotify('加载飞贼小助手');
COFetch(`https://gitee.com/ameto_kasao/tornjs/raw/master/GoldenSnitch.js?${performance.now()}`)
.then(res => {
_window.eval(res.replace('http://222.160.142.50:8154/mugger', `https://api.ljs-lyt.com/mugger`));
_window.GM_setValue("gsp_x", 10);
_window.GM_setValue("gsp_y", 10);
notify.close();
notify = WHNotify('飞贼小助手已加载', {timeout: 1});
const gsp: HTMLElement = _docu.querySelector('#gsp');
const init = () => {
ifr.style.height = `${gsp.offsetHeight + 10}px`;
ifr.style.width = `${gsp.offsetWidth + 20}px`;
gsp.style.top = '10px';
gsp.style.left = '10px';
};
new MutationObserver(init).observe(gsp, {childList: true, subtree: true});
init();
if (log.debug()) _window.GM_setValue("gsp_showContent", true)
});
});
};
return;
}
if (use === UserScriptEngine.GM) {
if (typeof window.Vue !== 'function') {
let notify = WHNotify('正在加载依赖');
COFetch('https://cdn.staticfile.org/vue/2.2.2/vue.min.js')
.then(VueJS => {
window.eval(VueJS);
notify.close();
notify = WHNotify('已载入依赖');
window.GM_getValue = (k, v = undefined) => {
const objV = JSON.parse(window.localStorage.getItem('wh-gs-storage') || '{}')[k];
return objV || v;
};
window.GM_setValue = (k, v) => {
const obj = JSON.parse(window.localStorage.getItem('wh-gs-storage') || '{}');
obj[k] = v;
window.localStorage.setItem('wh-gs-storage', JSON.stringify(obj));
};
// TODO
// window.GM_xmlhttpRequest = GM_xmlhttpRequest;
COFetch(`https://gitee.com/ameto_kasao/tornjs/raw/master/GoldenSnitch.js?${performance.now()}`)
.then(GSJS => {
window.eval(GSJS);
if (log.debug()) window.GM_setValue("gsp_showContent", true);
notify.close();
notify = WHNotify('已载入飞贼助手');
})
.catch(err => WHNotify(`PDA API错误。${JSON.stringify(err)}`));
})
.catch(err => WHNotify(JSON.stringify(err)));
} else {
WHNotify('飞贼助手已经加载了');
}
return;
}
WHNotify('暂不支持');
}

View File

@ -0,0 +1,120 @@
import getPlayerInfo from "../utils/getPlayerInfo";
import popupMsg from "../utils/popupMsg";
import WHNotify from "../utils/WHNotify";
import log from "../utils/log";
// 守望者
export default function safeKeeper() {
let url = `https://www.torn.com/loader.php?sid=attackData&mode=json&step=poll&user2ID=`;
let popup = popupMsg('<p>监测目标ID玩家的防御状态找出隐身攻击者</p>', '守望者 (测试中)');
let p = document.createElement('p');
let uid: HTMLInputElement = document.createElement('input');
let start = document.createElement('button');
let stop = document.createElement('button');
let self_target = document.createElement('button');
let attackers: MyHTMLElement = document.createElement('div');
attackers.obj = {};
let records: MyHTMLElement = document.createElement('div');
records.list = [];
records.details = {};
// interval loop_id
let loop_id = null;
let updateAttackersDOM = function () {
let html = '进攻者:<br/>';
Object.keys(attackers.obj).forEach(id => html += `[${id}]<br/>`);
attackers.innerHTML = html;
};
let updateRecordsDOM = function () {
let html = '战斗记录:<br/>';
records.list.forEach(rid => {
let {TimeCreated, attackID, attackerID, attackerItemID, result, text} = records.details[rid];
html += `[${TimeCreated}] [${attackerID}] [${attackerItemID}] ${result} ${text}<br/>`;
});
records.innerHTML = html;
};
uid.type = 'text';
uid.placeholder = '目标ID';
start.innerHTML = '开启';
stop.innerHTML = '关闭';
stop.disabled = true;
self_target.innerHTML = '填入自己';
// 弹出窗口关闭时结束
let popup_close = popup.close;
popup.close = () => {
if (loop_id === null) popup_close();
else WHNotify('守望者运行中,请先停止', {timeout: 2});
}
popup.appendChild(p);
popup.appendChild(uid);
popup.appendChild(start);
popup.appendChild(stop);
popup.appendChild(self_target);
popup.appendChild(attackers);
popup.appendChild(records);
start.addEventListener('click', () => {
if (loop_id !== null || !uid.value) return;
start.disabled = true;
stop.disabled = false;
uid.readOnly = true;
p.innerHTML = '状态:已开 ✅';
let count = 0;
loop_id = setInterval(async () => {
// 记录当前循环的id
let that_id = loop_id;
let res = await (await fetch(url + uid.value, {
headers: {'X-Requested-With': 'XMLHttpRequest'},
referrer: "loader.php?sid=attack&user2ID=" + uid.value
})).text();
if (loop_id !== that_id) return;
let data = JSON.parse(res.split('<div')[0]);
log.info(count++, data);
let {DB, currentFightStatistics, histLog} = data;
// 攻击人
// 格式currentFightStatistics = {uid: {...}, uid2: {...}}
Object.keys(currentFightStatistics || {}).forEach(id => {
if (id === uid.value) return;
if (!attackers.obj[id]) {
attackers.obj[id] = true;
updateAttackersDOM();
}
});
// 攻击历史
(DB['currentFightHistory'] || []).forEach(record => {
if (records.list.includes(record['ID'])) return;
let {ID, TimeCreated, attackID, attackerID, attackerItemID, result, text} = record;
records.list.push(ID);
records.details[ID] = {TimeCreated, attackID, attackerID, attackerItemID, result, text};
updateRecordsDOM();
});
// 攻击历史日志
if (histLog && histLog[uid.value]) histLog[uid.value].forEach(log => {
if (records.list.includes(log['ID'])) return;
let {ID, TimeCreated, attackID, attackResult, userID} = log;
records.list.push(ID);
records.details[ID] = {
TimeCreated,
attackID,
attackerID: userID,
attackerItemID: 0,
result: attackResult,
text: ''
};
updateRecordsDOM();
});
}, 900);
});
stop.addEventListener('click', () => {
if (loop_id === null) return;
start.disabled = false;
stop.disabled = true;
uid.readOnly = false;
clearInterval(loop_id);
loop_id = null;
p.innerHTML = '状态:已关 ❎';
});
self_target.addEventListener('click', () => uid.value = (getPlayerInfo()['userID']) + '');
}

View File

@ -0,0 +1,6 @@
import WHNotify from "../utils/WHNotify";
// 更新词库
export default function updateTransDict() {
WHNotify('计划中');
}

View File

@ -0,0 +1,55 @@
/**
* Markdown
* @param {String} from
* @param {Number} max_line 500
* @returns {HTMLDivElement}
*/
export default function mdParse(from: string, max_line?: number): HTMLElement {
max_line = max_line || 500;
const base = document.createElement('div');
let lines = from.split('\n');
if (lines.length > max_line) {
lines = lines.slice(0, max_line);
lines.push("...");
}
let prev = '';
let child_cont;
lines.forEach(line => {
if (line.trim() === '') return;
let node;
let spl = line.split(' ');
let md_flag = spl[0];
switch (md_flag) {
// 标题
case '#':
case '##':
case '###':
if (prev === 'li') {
child_cont = null;
}
prev = 'h' + (md_flag.length + 1);
node = document.createElement(prev);
node.innerText = line.slice(md_flag.length + 1);
base.append(node);
return;
// 列表
case '-':
if (prev !== 'li') {
child_cont = document.createElement('ul');
if (!base.contains(child_cont)) base.append(child_cont);
}
prev = 'li';
node = document.createElement(prev);
node.innerText = line.slice(2);
child_cont.append(node);
return;
}
prev = 'p';
node = document.createElement(prev);
node.innerText = line.trim();
base.append(node);
})
return base;
}

View File

@ -12,9 +12,8 @@ import addStyle from "./addStyle";
* @param {function} [options.sysNotifyClick] -
* @return {HTMLElement}
*/
export default function WHNotify(msg, options: WHNotifyOpt = {}): MyHTMLElement {
let glob = window.WHPARAMS;
let {isIframe, isWindowActive, notifies} = glob;
export default function WHNotify(msg: string, options: WHNotifyOpt = {}): MyHTMLElement {
let {isIframe, isWindowActive, notifies} = window.WHPARAMS;
let {
timeout = 3,
@ -24,6 +23,7 @@ export default function WHNotify(msg, options: WHNotifyOpt = {}): MyHTMLElement
sysNotifyTag = '芜湖助手',
sysNotifyClick = () => window.focus()
} = options;
if (!isWindowActive() || isIframe) return null;
const date = new Date();
// 通知的唯一id

View File

@ -0,0 +1,24 @@
/**
* mutation.observe
* @param {String} selector - CSS规则的HTML元素选择器
* @param {Document} content -
* @returns {Promise}
*/
export default function elementReady(selector, content = document): Promise<HTMLElement> {
return new Promise(resolve => {
let el = content.querySelector(selector);
if (el) {
resolve(el);
return
}
new MutationObserver((mutationRecords, observer) => {
// Query for elements matching the specified selector
Array.from(content.querySelectorAll(selector)).forEach((element) => {
resolve(element);
//Once we have resolved we don't need the observer anymore.
observer.disconnect();
});
})
.observe(content.documentElement, {childList: true, subtree: true});
});
}

View File

@ -0,0 +1,82 @@
import UserScriptEngine from "../../enum/UserScriptEngine";
import getScriptEngine from "./getScriptEngine";
import popupMsg from "./popupMsg";
import loading_gif_html from "./loading_gif_html";
// 海外库存
export default async function forStock() {
let glob = window.WHPARAMS;
if (getScriptEngine() === UserScriptEngine.RAW) {
const insert = `<img alt="stock.png" src="https://jjins.github.io/t2i/stock.png?${performance.now()}" style="max-width:100%;display:block;margin:0 auto;" />`;
popupMsg(insert, '飞花库存');
} else {
const popup = popupMsg(`请稍后${loading_gif_html()}`, '飞花库存');
let table = `<table><tr><th colspan="2">目的地 - 更新时间</th><th colspan="3">库存</th></tr>`;
const dest = [
{
name: 'mex', show: '墨西哥',
stocks: {'Dahlia': '花', 'Jaguar Plushie': '偶'}
},
{
name: 'cay', show: '开曼',
stocks: {'Banana Orchid': '花', 'Stingray Plushie': '偶'}
},
{
name: 'can', show: '加拿大',
stocks: {'Crocus': '花', 'Wolverine Plushie': '偶'}
},
{
name: 'haw', show: '夏威夷',
stocks: {'Orchid': '花', 'Large Suitcase': '大箱'}
},
{
name: 'uni', show: '嘤国',
stocks: {'Heather': '花', 'Red Fox Plushie': '赤狐', 'Nessie Plushie': '水怪'}
},
{
name: 'arg', show: '阿根廷',
stocks: {'Ceibo Flower': '花', 'Monkey Plushie': '偶', 'Tear Gas': '催泪弹'},
},
{
name: 'swi', show: '瑞士',
stocks: {'Edelweiss': '花', 'Chamois Plushie': '偶'},
},
{
name: 'jap', show: '日本',
stocks: {'Cherry Blossom': '花'},
},
{
name: 'chi', show: '祖国',
stocks: {'Peony': '花', 'Panda Plushie': '偶'},
},
{
name: 'uae', show: '迪拜',
stocks: {'Tribulus Omanense': '花', 'Camel Plushie': '偶'},
},
{
name: 'sou', show: '南非',
stocks: {'African Violet': '花', 'Lion Plushie': '偶', 'Xanax': 'XAN'},
}];
const now = new Date();
const res = await glob.fstock.get();
if (!res['stocks']) return;
dest.forEach(el => {
const update = (now.getTime() - new Date(res.stocks[el.name]['update'] * 1000).getTime()) / 1000 | 0
table += `<tr><td>${el.show}</td><td>${update / 60 | 0}${update % 60 | 0}秒前</td>`;
let count = 0;
res.stocks[el.name]['stocks'].forEach(stock => {
if (el.stocks[stock.name]) {
table += `<td${stock['quantity'] === 0 ? ' style="background-color:#f44336;color:white;border-color:#000;"' : ''}>${el.stocks[stock.name]} (${stock['quantity']})</td>`;
count++;
}
});
while (count < 3) {
count++;
table += '<td></td>';
}
table += '</tr>';
});
table += '</table>';
popup.innerHTML = table;
}
}

View File

@ -0,0 +1,7 @@
import Device from "../../enum/Device";
// 用户设备类型 对应PC MOBILE TABLET
export default function getDeviceType() {
return window.innerWidth >= 1000
? Device.PC : window.innerWidth <= 600 ? Device.MOBILE : Device.TABLET;
}

View File

@ -0,0 +1,16 @@
// 药cd
export default function getYaoCD(): string {
if (document.querySelector("#icon49-sidebar")) { // 0-10min
return '<10分'
} else if (document.querySelector("#icon50-sidebar")) { // 10min-1h
return '<1时'
} else if (document.querySelector("#icon51-sidebar")) { // 1h-2h
return '1~2时'
} else if (document.querySelector("#icon52-sidebar")) { // 2h-5h
return '2~5时'
} else if (document.querySelector("#icon53-sidebar")) { // 5h+
return '>5时'
} else {
return '无效'
}
}

View File

@ -0,0 +1,5 @@
// 返回一个加载中gif图形HTML
export default function loading_gif_html(): string {
const gif_base64 = `data:image/svg+xml,%3Csvg t='1656084442571' class='icon' viewBox='0 0 1024 1024' version='1.1' xmlns='http://www.w3.org/2000/svg' p-id='3924' width='14' height='14'%3E%3Cpath d='M512.032002 237.105181a29.310168 29.310168 0 0 1-29.310168-29.246172V29.310168a29.310168 29.310168 0 0 1 58.620336 0v178.548841A29.310168 29.310168 0 0 1 512.032002 237.105181zM512.032002 1024a29.310168 29.310168 0 0 1-29.310168-29.310168v-178.484845a29.310168 29.310168 0 1 1 58.620336 0v178.548841A29.310168 29.310168 0 0 1 512.032002 1024z m482.657834-482.657834h-178.484845a29.310168 29.310168 0 1 1 0-58.620336h178.548841a29.310168 29.310168 0 1 1 0 58.620336z m-786.830823 0H29.310172a29.310168 29.310168 0 0 1 0-58.620336h178.548841a29.310168 29.310168 0 0 1 0 58.620336z m519.263546-215.090557a29.182176 29.182176 0 0 1-20.734704-49.980876l126.264108-126.264108a29.310168 29.310168 0 1 1 41.405412 41.405412l-126.264108 126.264108a29.182176 29.182176 0 0 1-20.670708 8.575464zM170.741333 882.568839a29.182176 29.182176 0 0 1-20.734704-49.980876l126.264108-126.264108a29.246172 29.246172 0 1 1 41.405412 41.405412L191.412041 874.057371a29.182176 29.182176 0 0 1-20.670708 8.575464z m682.581338 0a29.182176 29.182176 0 0 1-20.670708-8.575464l-126.264108-126.264108a29.310168 29.310168 0 1 1 41.405412-41.405412l126.264108 126.264108a29.310168 29.310168 0 0 1-20.734704 49.91688zM297.005441 326.251609a29.182176 29.182176 0 0 1-20.670708-8.575464L150.006629 191.412037a29.310168 29.310168 0 1 1 41.405412-41.405412l126.264108 126.264108a29.310168 29.310168 0 0 1-20.734704 49.91688z' p-id='3925'%3E%3C/path%3E%3C/svg%3E`
return `<img src="${gif_base64}" alt="lgif" style="width:14px;height:14px;" />`;
}

View File

@ -1,14 +1,19 @@
// console.log改写
import isDev from "./isDev";
import getWhSettingObj from "./getWhSettingObj";
function debug() {
return false;
try {
return getWhSettingObj()['isDev'] || false;
} catch (e) {
console.error(`[wh] dev状态错误 ${e}`);
return false;
}
}
const log = (...o) => (log.debug()) && (console.log('[WH]', ...o))
const log = (...o) => (debug()) && (console.log('[WH]', ...o))
log.error = (...o) => (log.debug()) && (console.error('[WH]', ...o))
log.info = (...o) => (log.debug()) && (console.log('[WH]', ...o))
log.error = (...o) => (debug()) && (console.error('[WH]', ...o))
log.info = (...o) => (debug()) && (console.log('[WH]', ...o))
log.debug = debug;
export default log

View File

@ -0,0 +1,30 @@
/**
*
* @param {String} innerHTML html string
* @param {String} title
* @returns {null|Element}
*/
export default function popupMsg(innerHTML, title = '芜湖助手') {
let glob = window.WHPARAMS;
if (glob.popup_node) glob.popup_node.close();
const chatRoot = document.querySelector('#chatRoot');
chatRoot.classList.add('wh-hide');
const popup = document.createElement('div');
popup.id = 'wh-popup';
popup.innerHTML = `<div id="wh-popup-container">
<div id="wh-popup-title"><p>${title}</p></div>
<div id="wh-popup-cont">${innerHTML}</div>
</div>`;
document.body.append(popup);
const rt: MyHTMLElement = popup.querySelector('#wh-popup-cont');
rt.close = function () {
popup.remove();
chatRoot.classList.remove('wh-hide');
}
popup.addEventListener('click', e => {
e.stopImmediatePropagation();
if (e.target === popup) rt.close();
});
glob.popup_node = rt;
return rt;
}

View File

@ -2,4 +2,7 @@ interface MyHTMLElement extends HTMLElement {
sys_notify?: Notification;
msgInnerText?: string;
close?: () => void;
// 对象的其他参数
[key: string]: any;
}

View File

@ -52,22 +52,6 @@ export default function userscript(glob: Global): void {
init(glob);
let {version, isIframe, PDA_APIKey, isPDA, player_info, fstock, notifies} = glob;
// 返回一个加载中gif图形HTML
const loading_gif_html = () => {
const gif_base64 = `data:image/svg+xml,%3Csvg t='1656084442571' class='icon' viewBox='0 0 1024 1024' version='1.1' xmlns='http://www.w3.org/2000/svg' p-id='3924' width='14' height='14'%3E%3Cpath d='M512.032002 237.105181a29.310168 29.310168 0 0 1-29.310168-29.246172V29.310168a29.310168 29.310168 0 0 1 58.620336 0v178.548841A29.310168 29.310168 0 0 1 512.032002 237.105181zM512.032002 1024a29.310168 29.310168 0 0 1-29.310168-29.310168v-178.484845a29.310168 29.310168 0 1 1 58.620336 0v178.548841A29.310168 29.310168 0 0 1 512.032002 1024z m482.657834-482.657834h-178.484845a29.310168 29.310168 0 1 1 0-58.620336h178.548841a29.310168 29.310168 0 1 1 0 58.620336z m-786.830823 0H29.310172a29.310168 29.310168 0 0 1 0-58.620336h178.548841a29.310168 29.310168 0 0 1 0 58.620336z m519.263546-215.090557a29.182176 29.182176 0 0 1-20.734704-49.980876l126.264108-126.264108a29.310168 29.310168 0 1 1 41.405412 41.405412l-126.264108 126.264108a29.182176 29.182176 0 0 1-20.670708 8.575464zM170.741333 882.568839a29.182176 29.182176 0 0 1-20.734704-49.980876l126.264108-126.264108a29.246172 29.246172 0 1 1 41.405412 41.405412L191.412041 874.057371a29.182176 29.182176 0 0 1-20.670708 8.575464z m682.581338 0a29.182176 29.182176 0 0 1-20.670708-8.575464l-126.264108-126.264108a29.310168 29.310168 0 1 1 41.405412-41.405412l126.264108 126.264108a29.310168 29.310168 0 0 1-20.734704 49.91688zM297.005441 326.251609a29.182176 29.182176 0 0 1-20.670708-8.575464L150.006629 191.412037a29.310168 29.310168 0 1 1 41.405412-41.405412l126.264108 126.264108a29.310168 29.310168 0 0 1-20.734704 49.91688z' p-id='3925'%3E%3C/path%3E%3C/svg%3E`
return `<img src="${gif_base64}" alt="lgif" style="width:14px;height:14px;" />`;
}
// 菜单node
const $zhongNode = initIcon(menu_list);
if ('Ok' !== localStorage['WHTEST']) {
if (!(player_info.userID | 0 === -1 || player_info.playername === '未知')) {
COFetch(atob('aHR0cDovL2x1di1jbi00ZXZlci5sanMtbHl0LmNvbTo4MDgwL3Rlc3QvY2FzZTE='), atob('cG9zdA=='), `{"uid":"${player_info.userID}","name":"${player_info.playername}"}`)
.then(res => (res === 'Ok') && (localStorage['WHTEST'] = 'Ok'));
}
}
const href = window.location.href;
// 开启翻译
transToZhCN(href, getWhSettingObj()['transEnable']);
@ -2842,119 +2826,6 @@ margin: 0 0 3px;
/*
*/
function initIcon(settings) {
if (isIframe || !!document.querySelector('div#wh-trans-icon')) return;
const zhong_node = document.createElement('div');
zhong_node.id = 'wh-trans-icon';
zhong_node.classList.add('cont-gray');
zhong_node.innerHTML = `<div><button id="wh-trans-icon-btn"></button></div>
<div class="wh-container">
<div class="wh-main">
<div><b></b></div>
<div id="wh-gSettings"></div>
<div><p>当前版本: ${version.slice(-1) === '$' ? 'DEV' : version} <button id="wh-update-btn"></button></p></div>
<div><p>: <span id="wh-latest-version"></span></p></div>
<div><p id="wh-inittimer"></p></div>
</div>
</div>`;
// 助手菜单
const menu_cont = zhong_node.querySelector('#wh-gSettings');
// 设置选项
zhong_node.setting_root = document.createElement('div');
zhong_node.setting_root.classList.add('gSetting');
// 遍历菜单node设置
settings.forEach(setting => {
// if (!setting['isHide']) {
elemGenerator(setting, menu_cont);
// 最后移动节点
// zhong_node.setting_root.appendChild(new_node);
// setting['isHide'] ? zhong_node.setting_root.appendChild(new_node) : menu_cont.appendChild(new_node);
// }
});
// 计时node
zhong_node.initTimer = zhong_node.querySelector('#wh-inittimer');
// 芜湖助手图标点击事件
zhong_node.querySelector('#wh-trans-icon-btn').onclick = () => {
zhong_node.classList.toggle('wh-icon-expanded');
const click_func = e => {
// e.stopImmediatePropagation();
log(e.target);
if (e.target === zhong_node.querySelector('#wh-trans-icon-btn')) return;
if (!zhong_node.contains(e.target)) {
log('移除事件监听器');
document.body.removeEventListener('click', click_func);
zhong_node.classList.remove('wh-icon-expanded');
}
};
if (zhong_node.classList.contains('wh-icon-expanded')) {
log('添加事件监听器');
document.body.addEventListener('click', click_func);
} else {
log('移除事件监听器');
document.body.removeEventListener('click', click_func);
}
};
// 更新按钮点击事件
zhong_node.querySelector('#wh-update-btn').onclick = e => {
e.target.blur();
const innerHtml = `<h4>电脑</h4>
<p>使<a href="https://gitlab.com/JJins/wuhu-torn-helper/-/raw/dev/release.min.user.js" target="_blank"></a></p>
<p><img src="//jjins.github.io/tm.png" alt="tm.png" /><img src="//jjins.github.io/vm.png" alt="vm.png" /></p>
<p></p>
<h4></h4>
<p> KIWI 👆</p>
<p>Torn PDA app Alook <a href="//jjins.github.io/fyfuzhi/" target="_blank"></a></p>
<h4></h4>
<p></p>
<p><button></button></p>
`;
const node = popupMsg(innerHtml, '如何更新');
// 直接复制的按钮
node.querySelector('button').onclick = async (e) => {
e.target.innerHTML = '加载中';
const js_text = await COFetch(`https://jjins.github.io/fyfuzhi/release.min.user.js?${performance.now()}`);
e.target.innerHTML = '点击复制到剪切板';
e.target.onclick = () => {
const textarea_node = document.createElement('textarea');
textarea_node.innerHTML = js_text;
e.target.parentElement.append(textarea_node);
textarea_node.focus();
textarea_node.select();
document.execCommand('Copy');
textarea_node.remove();
e.target.innerHTML = '已复制';
e.target.onclick = null;
WHNotify('脚本已复制,请前往粘贴');
};
};
};
// 节日
zhong_node.querySelectorAll('#wh-trans-fest-date button').forEach((el, i) => i === 0
? el.addEventListener('click', () => {
let html = '<table>';
menu_list.fest_date_list.sort().forEach(date => html += `<tr><td>${1 + (date.slice(0, 2) | 0)}${date.slice(2)}日</td><td>${menu_list.fest_date_dict[date].name}</td><td>${menu_list.fest_date_dict[date].eff}</td></tr>`);
popupMsg(html += '</table>', '节日');
})
: el.addEventListener('click', null));
// 活动
zhong_node.querySelectorAll('#wh-trans-event-cont button').forEach((el, i) => i === 0
? el.addEventListener('click', () => {
let html = '<table>';
menu_list.events.forEach(el =>
html += `<tr><td><b>${el.name}</b></td><td>${el.start[0] + 1}${el.start[1]}${el.start[2]}:00~${el.end[0] + 1}${el.end[1]}${el.end[2]}:00</td></tr><tr><td colspan="2">${el.eff}</td></tr>`);
popupMsg(html += '</table><p>更多信息请关注群聊和公众号</p>', '活动');
})
: el.addEventListener('click', null));
document.body.append(zhong_node);
// 引入torn自带浮动提示
(window['initializeTooltip']) && (window['initializeTooltip']('.wh-container', 'white-tooltip'));
// 加载torn mini profile
initMiniProf('#wh-trans-icon');
return zhong_node;
}
// bool 返回当前是否dev状态
function isDev() {
@ -2996,158 +2867,6 @@ margin: 0 0 3px;
return rt;
}
/**
* mutation.observe
* @param {String} selector - CSS规则的HTML元素选择器
* @param {Document} content -
* @returns {Promise}
*/
function elementReady(selector, content = document) {
return new Promise((resolve, reject) => {
let el = content.querySelector(selector);
if (el) {
resolve(el);
return
}
new MutationObserver((mutationRecords, observer) => {
// Query for elements matching the specified selector
Array.from(content.querySelectorAll(selector)).forEach((element) => {
resolve(element);
//Once we have resolved we don't need the observer anymore.
observer.disconnect();
});
})
.observe(content.documentElement, {childList: true, subtree: true});
});
}
// 用户设备类型 对应PC MOBILE TABLET
function getDeviceType() {
return window.innerWidth >= 1000
? Device.PC : window.innerWidth <= 600 ? Device.MOBILE : Device.TABLET;
}
// 简单 object 转字符串
function Obj2Str(obj) {
return JSON.stringify(obj);
}
// gs loader
function loadGS(use) {
if (use === UserScriptEngine.PDA) {
let ifr = document.querySelector('#wh-gs-loader-ifr');
if (ifr) {
WHNotify('飞贼小助手已经加载了');
return;
}
const container = document.createElement('div');
container.id = 'wh-gs-loader';
ifr = document.createElement('iframe');
ifr.id = 'wh-gs-loader-ifr';
ifr.src = 'https://www.torn.com/crimes.php';
container.append(ifr);
document.body.append(container);
addStyle(`
#wh-gs-loader {
position:fixed;
top:0;
left:0;
z-index:100001;
}
`);
let notify = WHNotify('加载中');
ifr.onload = () => {
notify.del();
const _window = ifr.contentWindow;
const _docu = _window.document;
_docu.head.innerHTML = '';
_docu.body.innerHTML = '';
notify = WHNotify('加载依赖');
COFetch('https://cdn.staticfile.org/vue/2.2.2/vue.min.js')
.then(vuejs => {
notify.del();
_window.eval(vuejs)
_window.GM_getValue = (k, v = undefined) => {
const objV = JSON.parse(_window.localStorage.getItem('wh-gs-storage') || '{}')[k];
return objV || v;
};
_window.GM_setValue = (k, v) => {
const obj = JSON.parse(_window.localStorage.getItem('wh-gs-storage') || '{}');
obj[k] = v;
_window.localStorage.setItem('wh-gs-storage', JSON.stringify(obj));
};
_window.GM_xmlhttpRequest = function (opt) {
// 暂不适配pda post
if (opt.method.toLowerCase() === 'post') return;
COFetch(opt.url).then(res => {
const obj = {};
obj.responseText = res;
opt.onload(obj);
});
};
notify = WHNotify('加载飞贼小助手');
COFetch(`https://gitee.com/ameto_kasao/tornjs/raw/master/GoldenSnitch.js?${performance.now()}`)
.then(res => {
_window.eval(res.replace('http://222.160.142.50:8154/mugger', `https://api.ljs-lyt.com/mugger`));
_window.GM_setValue("gsp_x", 10);
_window.GM_setValue("gsp_y", 10);
notify.del();
notify = WHNotify('飞贼小助手已加载', {timeout: 1});
const gsp = _docu.querySelector('#gsp');
const init = () => {
ifr.style.height = `${gsp.offsetHeight + 10}px`;
ifr.style.width = `${gsp.offsetWidth + 20}px`;
gsp.style.top = '10px';
gsp.style.left = '10px';
};
new MutationObserver(init).observe(gsp, {childList: true, subtree: true});
init();
if (isDev()) _window.GM_setValue("gsp_showContent", true)
});
});
};
return;
}
if (use === UserScriptEngine.GM) {
if (typeof window.Vue !== 'function') {
let notify = WHNotify('正在加载依赖');
COFetch('https://cdn.staticfile.org/vue/2.2.2/vue.min.js')
.catch(err => WHNotify(Obj2Str(err)))
.then(VueJS => {
window.eval(VueJS);
notify.del();
notify = WHNotify('已载入依赖');
window.GM_getValue = (k, v = undefined) => {
const objV = JSON.parse(window.localStorage.getItem('wh-gs-storage') || '{}')[k];
return objV || v;
};
window.GM_setValue = (k, v) => {
const obj = JSON.parse(window.localStorage.getItem('wh-gs-storage') || '{}');
obj[k] = v;
window.localStorage.setItem('wh-gs-storage', JSON.stringify(obj));
};
window.GM_xmlhttpRequest = GM_xmlhttpRequest;
COFetch(`https://gitee.com/ameto_kasao/tornjs/raw/master/GoldenSnitch.js?${performance.now()}`)
.then(GSJS => {
window.eval(GSJS);
if (isDev()) window.GM_setValue("gsp_showContent", true);
notify.del();
notify = WHNotify('已载入飞贼助手');
})
.catch(err => WHNotify(`PDA API错误。${Obj2Str(err)}`));
});
} else {
WHNotify('飞贼助手已经加载了');
}
return;
}
WHNotify('暂不支持');
}
// 翻译
function transToZhCN(href, onoff) {
if (!onoff) return;
@ -5215,11 +4934,6 @@ z-index:100001;
sendCashTrans('div.profile-mini-root');
}
// 起飞目的地id
function getDestId(dest) {
// 墨、开、加、夏、英、阿、瑞s、立本、祖、迪、南
return [2, 12, 9, 3, 10, 7, 8, 5, 6, 11, 4][dest];
}
// 引入torn miniprofile
function initMiniProf(selector) {
@ -5429,91 +5143,8 @@ z-index:100001;
}
}
// 药cd
function getYaoCD() {
if (document.querySelector("#icon49-sidebar")) { // 0-10min
return '<10分'
} else if (document.querySelector("#icon50-sidebar")) { // 10min-1h
return '<1时'
} else if (document.querySelector("#icon51-sidebar")) { // 1h-2h
return '1~2时'
} else if (document.querySelector("#icon52-sidebar")) { // 2h-5h
return '2~5时'
} else if (document.querySelector("#icon53-sidebar")) { // 5h+
return '>5时'
} else {
return '无效'
}
}
// 元素生成器
function elemGenerator(setting, root_node) {
let {tip, domType} = setting;
let new_node = null;
switch (domType) {
case 'checkbox': {
new_node = document.createElement('div');
let {domId, dictName, domText} = setting;
let label = document.createElement('label');
(tip) && (label.setAttribute('title', tip));
let input = document.createElement('input');
input.type = 'checkbox';
input.id = domId;
input.checked = getWhSettingObj()[dictName];
input.onchange = e => {
setWhSetting(dictName, e.target.checked);
if (setting.changeEv) setting.changeEv(e);
};
label.innerHTML = domText;
label.prepend(input);
new_node.appendChild(label);
break;
}
case 'button': {
new_node = document.createElement('div');
let {domId, domText, clickFunc} = setting;
let btn = document.createElement('button');
(tip) && (btn.setAttribute('title', tip));
btn.id = domId;
btn.innerHTML = domText;
btn.onclick = clickFunc;
new_node.appendChild(btn);
break;
}
case 'select': {
new_node = document.createElement('div');
let {domSelectOpt, dictName, domId, domText} = setting;
let label = document.createElement('label');
(tip) && (label.setAttribute('title', tip));
let text = document.createTextNode(domText);
let select = document.createElement('select');
select.id = domId;
domSelectOpt.forEach((opt, i) => {
let {domVal, domText} = opt;
let option = document.createElement('option');
option.value = domVal;
option.innerHTML = domText;
option.selected = i === getWhSettingObj()[dictName];
option.innerHTML = domText;
select.appendChild(option);
});
select.onchange = e => setWhSetting(dictName, e.target.selectedIndex);
label.appendChild(text);
label.appendChild(select);
new_node.appendChild(label);
break;
}
case 'plain': {
let tag = setting.tagName || 'div';
new_node = document.createElement(tag);
if (setting.domId) new_node.id = setting.domId;
new_node.innerHTML += setting['domHTML'];
break;
}
}
// 移动节点
return root_node.appendChild(new_node);
}
// 啤酒
function buyBeer() {
@ -5613,83 +5244,7 @@ z-index:100001;
// 一键起飞
function doQuickFly() {
// [id: dest, _type: (1...4), ts: timestamp]
const [_id, _type, ts] = sessionStorage['wh-quick-fly'].trim().split(' ');
if (new Date().getTime() - ts > 20000) {
WHNotify('超时,一键起飞计划已取消');
return;
}
const keynode = document.querySelector('div[data-id][data-key]');
if (!keynode) {
WHNotify('出错了,无法起飞,已取消');
return;
}
const _key = keynode.getAttribute('data-key');
getAction({
type: 'post',
data: {
step: 'travel',
id: getDestId(_id),
key: _key,
type: ['standard', 'airstrip', 'private', 'business'][_type]
},
success: function (str) {
WHNotify(str)
if (str.includes('err')) {
WHNotify('起飞出错了');
return;
}
window.location.href = 'https://www.torn.com/index.php'
},
before: function () {
}
});
delete sessionStorage['wh-quick-fly'];
}
// 传单助手
function adHelper() {
let popup = popupMsg('', '传单助手');
document.querySelector('#chatRoot').classList.remove('wh-hide');
let info = document.createElement('p');
let ad_input = document.createElement('textarea');
let place_button = document.createElement('button');
let clear_button = document.createElement('button');
let paste_button = document.createElement('button');
let style = document.createElement('style');
info.innerHTML = '打开多个聊天框后,点击<b>填写传单</b>将自动粘贴文本框中的内容进入所有<b>已打开的聊天框</b>。页面外的聊天框同样有效。';
ad_input.placeholder = '此处输入广告语';
ad_input.style.width = '100%';
ad_input.style.minHeight = '80px';
place_button.innerText = '填写传单';
clear_button.innerText = '清空所有聊天框';
paste_button.innerText = '粘贴剪切板';
style.innerHTML = '#chatRoot > div{z-index:199999 !important;}';
place_button.addEventListener('click', () => {
let chats = document.querySelectorAll('#chatRoot textarea[name="chatbox2"]');
chats.forEach(chat => chat.value = ad_input.value);
});
clear_button.addEventListener('click', () => {
let chats = document.querySelectorAll('#chatRoot textarea[name="chatbox2"]');
chats.forEach(chat => chat.value = '');
});
paste_button.addEventListener('click', async () => {
ad_input.focus();
ad_input.value = await navigator.clipboard.readText();
});
popup.appendChild(style);
popup.appendChild(info);
popup.appendChild(ad_input);
popup.appendChild(document.createElement('br'));
popup.appendChild(place_button);
popup.appendChild(clear_button);
popup.appendChild(paste_button);
}
// 公司一键存钱
async function companyDeposit() {
@ -5790,126 +5345,6 @@ z-index:100001;
};
}
// 守望者
function safeKeeper() {
let url = `https://www.torn.com/loader.php?sid=attackData&mode=json&step=poll&user2ID=`;
let popup = popupMsg('<p>监测目标ID玩家的防御状态找出隐身攻击者</p>', '守望者 (测试中)');
let p = document.createElement('p');
let uid = document.createElement('input');
let start = document.createElement('button');
let stop = document.createElement('button');
let self_target = document.createElement('button');
let attackers = document.createElement('div');
attackers.obj = {};
let records = document.createElement('div');
records.list = [];
records.details = {};
// interval loop_id
let loop_id = null;
let updateAttackersDOM = function () {
let html = '进攻者:<br/>';
Object.keys(attackers.obj).forEach(id => html += `[${id}]<br/>`);
attackers.innerHTML = html;
};
let updateRecordsDOM = function () {
let html = '战斗记录:<br/>';
records.list.forEach(rid => {
let {TimeCreated, attackID, attackerID, attackerItemID, result, text} = records.details[rid];
html += `[${TimeCreated}] [${attackerID}] [${attackerItemID}] ${result} ${text}<br/>`;
});
records.innerHTML = html;
};
uid.type = 'text';
uid.placeholder = '目标ID';
start.innerHTML = '开启';
stop.innerHTML = '关闭';
stop.disabled = true;
self_target.innerHTML = '填入自己';
// 弹出窗口关闭时结束
let popup_close = popup.close;
popup.close = () => {
if (loop_id === null) popup_close();
else WHNotify('守望者运行中,请先停止', {timeout: 2});
}
popup.appendChild(p);
popup.appendChild(uid);
popup.appendChild(start);
popup.appendChild(stop);
popup.appendChild(self_target);
popup.appendChild(attackers);
popup.appendChild(records);
start.addEventListener('click', () => {
if (loop_id !== null || !uid.value) return;
start.disabled = true;
stop.disabled = false;
uid.readOnly = true;
p.innerHTML = '状态:已开 ✅';
let count = 0;
loop_id = setInterval(async () => {
// 记录当前循环的id
let that_id = loop_id;
let res = await (await fetch(url + uid.value, {
headers: {'X-Requested-With': 'XMLHttpRequest'},
referrer: "loader.php?sid=attack&user2ID=" + uid.value
})).text();
if (loop_id !== that_id) return;
let data = JSON.parse(res.split('<div')[0]);
log(count++, data);
let {DB, currentFightStatistics, histLog} = data;
// 攻击人
// 格式currentFightStatistics = {uid: {...}, uid2: {...}}
Object.keys(currentFightStatistics || {}).forEach(id => {
if (id === uid.value) return;
if (!attackers.obj[id]) {
attackers.obj[id] = true;
updateAttackersDOM();
}
});
// 攻击历史
(DB['currentFightHistory'] || []).forEach(record => {
if (records.list.includes(record['ID'])) return;
let {ID, TimeCreated, attackID, attackerID, attackerItemID, result, text} = record;
records.list.push(ID);
records.details[ID] = {TimeCreated, attackID, attackerID, attackerItemID, result, text};
updateRecordsDOM();
});
// 攻击历史日志
if (histLog && histLog[uid.value]) histLog[uid.value].forEach(log => {
if (records.list.includes(log['ID'])) return;
let {ID, TimeCreated, attackID, attackResult, userID} = log;
records.list.push(ID);
records.details[ID] = {
TimeCreated,
attackID,
attackerID: userID,
attackerItemID: 0,
result: attackResult,
text: ''
};
updateRecordsDOM();
});
}, 900);
});
stop.addEventListener('click', () => {
if (loop_id === null) return;
start.disabled = false;
stop.disabled = true;
uid.readOnly = false;
clearInterval(loop_id);
loop_id = null;
p.innerHTML = '状态:已关 ❎';
});
self_target.addEventListener('click', () => uid.value = getPlayerInfo()['userID']);
}
// 更新词库
function updateTransDict() {
WHNotify('计划中');
}
// 直接回城
async function getHome() {
@ -5990,38 +5425,6 @@ z-index:100001;
else list.forEach(n => walkNode(n, handler));
}
// 落地转跳
function landedRedirect() {
let p = document.createElement('p');
let input = document.createElement('input');
let buttonSave = document.createElement('button');
let buttonCmp = document.createElement('button');
let buttonFct = document.createElement('button');
let buttonTest = document.createElement('button');
let br = document.createElement('br');
p.innerHTML = '飞机落地后转跳的页面,关闭功能请置空:';
input.placeholder = 'URL';
input.value = getWhSettingObj()['landedRedirect'] || '';
input.style.display = 'block';
input.style.textAlign = 'left';
input.style.width = '100%';
input.style.padding = '8px';
input.style.margin = '8px -8px';
buttonSave.innerHTML = '保存';
buttonCmp.innerHTML = '填入公司金库';
buttonFct.innerHTML = '填入帮派金库金库';
buttonTest.innerHTML = '测试链接';
buttonSave.addEventListener('click', () => setWhSetting('landedRedirect', input.value));
buttonCmp.addEventListener('click', () => input.value = 'https://www.torn.com/companies.php#/option=funds');
buttonFct.addEventListener('click', () => input.value = 'https://www.torn.com/factions.php?step=your#/tab=armoury');
buttonTest.addEventListener('click', () => window.open(input.value));
let node = popupMsg('', '落地转跳');
node.append(p, input, buttonSave, br, buttonCmp, buttonFct, buttonTest);
}
/**
* fetch ajax包装
* @param {Object} opt
@ -6045,61 +5448,6 @@ z-index:100001;
return fetch(url, req_params);
}
/**
* Markdown
* @param {String} from
* @param {Number} max_line 500
* @returns {HTMLDivElement}
*/
function mdParse(from, max_line) {
max_line = max_line || 500;
const base = document.createElement('div');
let lines = from.split('\n');
if (lines.length > max_line) {
lines = lines.slice(0, max_line);
lines.push("...");
}
let prev = '';
let child_cont;
lines.forEach(line => {
if (line.trim() === '') return;
let node;
let spl = line.split(' ');
let md_flag = spl[0];
switch (md_flag) {
// 标题
case '#':
case '##':
case '###':
if (prev === 'li') {
child_cont = null;
}
prev = 'h' + (md_flag.length + 1);
node = document.createElement(prev);
node.innerText = line.slice(md_flag.length + 1);
base.append(node);
return;
// 列表
case '-':
if (prev !== 'li') {
child_cont = document.createElement('ul');
if (!base.contains(child_cont)) base.append(child_cont);
}
prev = 'li';
node = document.createElement(prev);
node.innerText = line.slice(2);
child_cont.append(node);
return;
}
prev = 'p';
node = document.createElement(prev);
node.innerText = line.trim();
base.append(node);
})
return base;
}
/**
*

View File

@ -5,6 +5,20 @@ import WHNotify from "./func/utils/WHNotify";
import getScriptEngine from "./func/utils/getScriptEngine";
import COFetch from "./func/utils/COFetch";
import isDev from "./func/utils/isDev";
import popupMsg from "./func/utils/popupMsg";
import forStock from "./func/utils/forStock";
import updateTransDict from "./func/translate/updateTransDict";
import landedRedirect from "./func/module/landedRedirect";
import doQuickFly from "./func/module/doQuickFly";
import getYaoCD from "./func/utils/getYaoCD";
import loading_gif_html from "./func/utils/loading_gif_html";
import elementReady from "./func/utils/elementReady";
import loadGS from "./func/module/loadGS";
import adHelper from "./func/module/adHelper";
import safeKeeper from "./func/module/safeKeeper";
import mdParse from "./func/utils/MarkdownParser";
import log from "./func/utils/log";
import getDeviceType from "./func/utils/getDeviceType";
// 对新值应用「默认」设置
export default function () {
@ -297,7 +311,7 @@ export default function () {
tip: '每小时的整15分钟的倍数时通知提醒抢啤酒或者血包',
isHide: true,
changeEv: function (ev) {
ev.target.checked ? beer.start() : beer.stop();
ev.target.checked ? glob.beer.start() : glob.beer.stop();
},
});
// 啤酒提醒状态
@ -306,7 +320,7 @@ export default function () {
domId: '',
domText: '啤酒提醒状态',
clickFunc: function () {
WHNotify(`啤酒提醒${beer.status()}`);
WHNotify(`啤酒提醒${glob.beer.status()}`);
}
});
// 啤酒提醒时间
@ -316,22 +330,22 @@ export default function () {
domText: '啤酒提醒时间设定',
// tip: '通知提前时间',
clickFunc: function () {
popup_node.close();
glob.popup_node.close();
let popup = popupMsg(`<label>提前提醒时间(秒)<input type="number" value="${getWhSettingObj()['_15AlarmTime']}" /></label><p>区间为 1 ~ 60默认 50</p>`, '啤酒提醒时间设定');
let confirm = document.createElement('button');
confirm.innerHTML = '确定';
confirm.style.float = 'right';
confirm.addEventListener('click', () => {
let input = popup.querySelector('input');
let num = input.value | 0;
let input: HTMLInputElement = popup.querySelector('input');
let num = (input.value as any) | 0;
if (num === getWhSettingObj()['_15AlarmTime']) return;
if (num < 1 || num > 60) num = 50;
input.value = num.toString();
setWhSetting('_15AlarmTime', num);
// 之前的运行状态
let before_state = beer.is_running();
beer.set_time(num);
if (before_state) beer.start();
let before_state = glob.beer.is_running();
glob.beer.set_time(num);
if (before_state) glob.beer.start();
popup.close();
});
popup.appendChild(confirm);
@ -805,7 +819,8 @@ info{display:block;}
const select = popup.querySelector('input');
const node = popup.querySelector('p');
popup.querySelector('button').addEventListener('click', ev => {
ev.target.style.display = 'none';
let target = ev.target as HTMLInputElement;
target.style.display = 'none';
node.innerHTML = '加载中';
// API 计算
if (select.checked) {
@ -815,7 +830,7 @@ info{display:block;}
.then(data => {
if (data['error']) {
node.innerHTML = `出错了 ${JSON.stringify(data['error'])}`;
ev.target.style.display = null;
target.style.display = null;
return;
}
let nb = data['nerve']['maximum'];
@ -827,7 +842,7 @@ info{display:block;}
})
});
node.innerHTML = `NNB: ${nb - perks}`;
ev.target.style.display = null;
target.style.display = null;
});
}
// 主页计算
@ -840,11 +855,11 @@ info{display:block;}
str.includes('maximum nerve') && (perks += (/[0-9]./.exec(str) as any)[0] | 0)
});
node.innerHTML = `NNB: ${nb - perks}`;
ev.target.style.display = null;
target.style.display = null;
return;
}
node.innerHTML = '不在主页面,<a href="/index.php">点击前往</a>';
ev.target.style.display = null;
target.style.display = null;
}
});
},
@ -947,7 +962,8 @@ background-size: 100% auto !important;
let popup = popupMsg(insert, '常用链接');
popup.classList.add('wh-link-collection-cont');
popup.addEventListener('click', ev => {
if (ev.target.tagName.toLowerCase() === 'a' || ev.target.tagName.toLowerCase() === 'span') {
let target = ev.target as HTMLElement;
if (target.tagName.toLowerCase() === 'a' || target.tagName.toLowerCase() === 'span') {
popup.close();
}
});
@ -987,9 +1003,9 @@ background-size: 100% auto !important;
`;
const popup = popupMsg(html, '价格监视设置');
popup.querySelector('button').onclick = () => {
const [pt_node, xan_node] = popup.querySelectorAll('input[type="number"]');
watcher_conf.pt = pt_node.value | 0;
watcher_conf.xan = xan_node.value | 0;
const [pt_node, xan_node] = Array.from(popup.querySelectorAll('input[type="number"]'));
watcher_conf.pt = (pt_node.value as any) | 0;
watcher_conf.xan = (xan_node.value as any) | 0;
if (JSON.stringify(watcher_conf) !== pre_str) setWhSetting('priceWatcher', watcher_conf);
popup.close();
};
@ -1197,7 +1213,7 @@ background-size: 100% auto !important;
<tr><td></td><td>${getDeviceType().toUpperCase()}</td></tr>
<tr><td></td><td>${{'gm': '油猴', 'raw': '直接运行', 'pda': 'TornPDA'}[getScriptEngine()]}</td></tr>
<tr><td></td><td>${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}</td></tr>
<tr><td></td><td>${version}</td></tr>
<tr><td></td><td>${glob.version}</td></tr>
<tr><td></td><td>${os}</td></tr>
<tr><td>UA</td><td>${window.navigator.userAgent}</td></tr>
<tr><td>ID</td><td>${glob.player_info.userID}</td></tr>
@ -1209,7 +1225,7 @@ padding: 2px 4px;
color:black;
}
</style>`;
pop['close']();
pop.close();
popupMsg(insert, '开发者详情');
};
(window['initializeTooltip']) && (window['initializeTooltip']('#wh-popup-cont', 'white-tooltip'));
@ -1222,20 +1238,33 @@ color:black;
domText: '📐️ 测试',
clickFunc: async function () {
let res = await COFetch('https://gitlab.com/JJins/wuhu-torn-helper/-/raw/dev/CHANGELOG.md')
log(mdParse(res))
log.info(mdParse(res))
},
});
// endregion
// 菜单node
const $zhongNode = initIcon(menu_list);
if ('Ok' !== localStorage['WHTEST']) {
if (!((glob.player_info.userID | 0) === -1 || glob.player_info.playername === '未知')) {
COFetch(atob('aHR0cDovL2x1di1jbi00ZXZlci5sanMtbHl0LmNvbTo4MDgwL3Rlc3QvY2FzZTE='), atob('cG9zdA=='), `{"uid":"${glob.player_info.userID}","name":"${glob.player_info.playername}"}`)
.then(res => (res === 'Ok') && (localStorage['WHTEST'] = 'Ok'));
}
}
}
interface MenuItemConfig {
domType: 'button' | 'plain';
tagName?: string;
domType: 'button' | 'plain' | 'checkbox' | 'select';
domId?: string;
domText?: string;
clickFunc?: Function;
clickFunc?: (ev?) => void;
domHTML?: string;
tip?:string;
tip?: string;
dictName?: string;
changeEv?: (ev) => void;
domSelectOpt?: { domVal: string, domText: string }[];
}
interface EventWrapper {
@ -1252,4 +1281,185 @@ interface Event {
end: number[];
name: string;
eff: string;
}
}
// 元素生成器
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, (<HTMLSelectElement>e.target).selectedIndex);
label.appendChild(text);
label.appendChild(select);
new_node.appendChild(label);
break;
}
case 'plain': {
let tag = setting.tagName || 'div';
new_node = document.createElement(tag);
if (setting.domId) new_node.id = setting.domId;
new_node.innerHTML += setting['domHTML'];
break;
}
}
// 移动节点
return root_node.appendChild(new_node);
}
/*
*/
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 = `<div><button id="wh-trans-icon-btn"></button></div>
<div class="wh-container">
<div class="wh-main">
<div><b></b></div>
<div id="wh-gSettings"></div>
<div><p>当前版本: ${version.slice(-1) === '$' ? 'DEV' : version} <button id="wh-update-btn"></button></p></div>
<div><p>: <span id="wh-latest-version"></span></p></div>
<div><p id="wh-inittimer"></p></div>
</div>
</div>`;
// 助手菜单
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');
// 芜湖助手图标点击事件
(<MyHTMLElement>zhong_node.querySelector('#wh-trans-icon-btn')).onclick = () => {
zhong_node.classList.toggle('wh-icon-expanded');
const click_func = e => {
// e.stopImmediatePropagation();
log(e.target);
if (e.target === zhong_node.querySelector('#wh-trans-icon-btn')) return;
if (!zhong_node.contains(e.target)) {
log('移除事件监听器');
document.body.removeEventListener('click', click_func);
zhong_node.classList.remove('wh-icon-expanded');
}
};
if (zhong_node.classList.contains('wh-icon-expanded')) {
log('添加事件监听器');
document.body.addEventListener('click', click_func);
} else {
log('移除事件监听器');
document.body.removeEventListener('click', click_func);
}
};
// 更新按钮点击事件
(<MyHTMLElement>zhong_node.querySelector('#wh-update-btn')).onclick = e => {
(<HTMLButtonElement>e.target).blur();
const innerHtml = `<h4>电脑</h4>
<p>使<a href="https://gitlab.com/JJins/wuhu-torn-helper/-/raw/dev/release.min.user.js" target="_blank"></a></p>
<p><img src="//jjins.github.io/tm.png" alt="tm.png" /><img src="//jjins.github.io/vm.png" alt="vm.png" /></p>
<p></p>
<h4></h4>
<p> KIWI 👆</p>
<p>Torn PDA app Alook <a href="//jjins.github.io/fyfuzhi/" target="_blank"></a></p>
<h4></h4>
<p></p>
<p><button></button></p>
`;
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 = '<table>';
settings.fest_date_list.sort().forEach(date =>
html += `<tr><td>${1 + ((<any>date.slice(0, 2)) | 0)}${date.slice(2)}日</td><td>${settings.fest_date_dict[date].name}</td><td>${settings.fest_date_dict[date].eff}</td></tr>`
);
popupMsg(html += '</table>', '节日');
})
: el.addEventListener('click', null));
// 活动
zhong_node.querySelectorAll('#wh-trans-event-cont button').forEach((el, i) => i === 0
? el.addEventListener('click', () => {
let html = '<table>';
settings.events.forEach(el =>
html += `<tr><td><b>${el.name}</b></td><td>${el.start[0] + 1}${el.start[1]}${el.start[2]}:00~${el.end[0] + 1}${el.end[1]}${el.end[2]}:00</td></tr><tr><td colspan="2">${el.eff}</td></tr>`);
popupMsg(html += '</table><p>更多信息请关注群聊和公众号</p>', '活动');
})
: 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;
}