This commit is contained in:
Liwanyi 2022-09-14 18:14:52 +08:00
parent 8b540b46fb
commit 26c9227461
45 changed files with 1609 additions and 2364 deletions

View File

@ -8,6 +8,11 @@
<JetCodeStyleSettings> <JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings> </JetCodeStyleSettings>
<TypeScriptCodeStyleSettings version="0">
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
<option name="SPACES_WITHIN_IMPORTS" value="true" />
<option name="SPACES_WITHIN_INTERPOLATION_EXPRESSIONS" value="true" />
</TypeScriptCodeStyleSettings>
<codeStyleSettings language="kotlin"> <codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</codeStyleSettings> </codeStyleSettings>

13
global.d.ts vendored
View File

@ -77,12 +77,13 @@ declare interface Navigator {
declare interface TornGetActionParams { declare interface TornGetActionParams {
type: 'post' | 'get', type: 'post' | 'get',
data: { action?: string,
step: string, data?: {
id: number, step?: string,
key: string, id?: number,
type: string key?: string,
type?: string
}, },
success: Function, success: Function,
before: Function before?: Function
} }

75
src/WHNext.ts Normal file
View File

@ -0,0 +1,75 @@
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";
export default function (glob: Global) {
// 啤酒提醒
if (getWhSettingObj()['_15Alarm']) glob.beer.start();
// 点击4条转跳对应的页面
if (getWhSettingObj()['barsRedirect']) {
const eb = document.getElementById('barEnergy') as HTMLAnchorElement;
const nb = document.getElementById('barNerve') as HTMLAnchorElement;
const hb = document.getElementById('barHappy') as HTMLAnchorElement;
const lb = document.getElementById('barLife') as HTMLAnchorElement;
if (eb) {
eb.addEventListener('click', () => location.href = '/gym.php');
eb.href = '/gym.php';
} else {
elementReady('#barEnergy').then(eb => {
eb.addEventListener('click', () => location.href = '/gym.php');
(eb as HTMLAnchorElement).href = '/gym.php';
});
}
if (nb) {
nb.addEventListener('click', () => location.href = '/crimes.php');
nb.href = '/crimes.php';
} else {
elementReady('#barNerve').then(nb => {
nb.addEventListener('click', () => location.href = '/crimes.php');
(nb as HTMLAnchorElement).href = '/crimes.php';
});
}
if (hb) {
hb.addEventListener('click', () => location.href = '/item.php#boosters-items');
hb.href = '/item.php#boosters-items';
} else {
elementReady('#barHappy').then(hb => {
hb.addEventListener('click', () => location.href = '/item.php#boosters-items');
(hb as HTMLAnchorElement).href = '/item.php#boosters-items';
});
}
if (lb) {
lb.addEventListener('click', () => location.href = '/item.php#medical-items');
lb.href = '/item.php#medical-items';
} else {
elementReady('#barLife').then(lb => {
lb.addEventListener('click', () => location.href = '/item.php#medical-items');
(lb as HTMLAnchorElement).href = '/item.php#medical-items';
});
}
}
/**
*
* TODO
*/
if (getWhSettingObj()['removeScripts']) {
document.querySelectorAll('script[src*="google"]').forEach(item => item.remove());
document.querySelectorAll('#gtm_tag').forEach(item => item.remove());
document.querySelectorAll('script[src*="chat/gonline"]').forEach(item => item.remove());
document.querySelectorAll('head script[nonce]').forEach(item => item.remove());
}
// 存钱相关
depoHelper(glob);
// 飞行相关
travelHelper(glob).then();
// 战斗相关
attackHelper(glob).then();
}

View File

@ -0,0 +1,271 @@
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";
export default async function attackHelper(glob: Global): Promise<null> {
let { href, device, $zhongNode } = glob;
// 攻击页面
if (href.contains(/loader\.php\?sid=attack/)) {
let stop_reload = false;
const { quickAttIndex, quickFinishAtt, attReload } = getWhSettingObj();
// 光速刷新按钮
addActionBtn('光速刷新', doAttackReload, $zhongNode);
// 自刷新
let audio_played_flag;
// @ts-ignore
if (attReload !== 6 && stop_reload !== true) {
const selector_device_map = {
'pc': '#defender div[class^="modal___"]',
'mobile': '#attacker div[class^="modal___"]',
'tablet': '',
};
const selector = selector_device_map[device];
elementReady(selector).then(elem => {
if (!elem.querySelector('button')) {
if (getWhSettingObj().attReload === 0 && stop_reload !== true) {
// window.location.reload();
doAttackReload();
} else {
let reload_flag;
const timeout = getWhSettingObj().attReload * 1000 + getRandomInt(-500, 500);
log.info(`[WH] ${ timeout / 1000 }s 后自动刷新`);
window.setInterval(() => {
if (reload_flag === undefined) {
reload_flag = true;
} else if (stop_reload !== true) {
// window.location.reload();
doAttackReload();
}
}, timeout);
}
} else if (audio_played_flag === undefined) {
audio_played_flag = true;
let play_time = 0;
const audio_play_id = window.setInterval(() => {
const $audio = document.createElement('audio');
$audio.src = 'https://www.torn.com/js/chat/sounds/Warble_1.mp3';
$audio.play().then();
play_time++;
if (play_time === 3) clearInterval(audio_play_id);
}, 600);
}
});
}
// 光速拔刀
if (quickAttIndex !== 6) {
const btn = await elementReady('div[class^="modal___"] button');
log.info(btn);
if (!btn.innerText.toLowerCase().includes('fight')) return;
// 判断是否存在脚踢
const hasKick = !!document.querySelector('#weapon_boots');
// modal层
const modal: HTMLElement = document.querySelector('div[class^="modal___"]');
log.info(`当前设备类型是${ device }`);
// 区分设备
switch (device) {
case Device.PC: {
log.info(`开始调整按钮位置`);
// 隐藏modal层
modal.style.display = 'none';
// 根据选择的武器调整css
let css_top = '0';
switch (getWhSettingObj()['quickAttIndex']) {
case 1: { // weapon_second
css_top = '97px';
break;
}
case 2: { // weapon_melee
css_top = '194px';
break;
}
case 3: { // weapon_temp
css_top = '291px';
break;
}
case 4: // weapon_fists
case 5: { // weapon_boots
css_top = '375px';
break;
}
}
const css_rule = `
.wh-move-btn #defender div[class^="modal___"]{display: block;width: 0 !important;top: ${ css_top };left: -169px !important;}
.wh-move-btn #defender div[class^="dialog___"]{border:0;width:159px;height:96px;}
.wh-move-btn #defender div[class^="colored___"]{display:block;padding:0;}
.wh-move-btn #defender div[class^="title___"]{height:0;}
.wh-move-btn #defender button{width: 100%;margin:17px 0;height: 60px;}
`;
addStyle(css_rule);
document.body.classList.add('wh-move-btn');
// 绑定点击事件 联动【光速跑路】
btn.onclick = () => {
if (quickFinishAtt !== 3) {
btn.remove();
// 停止自动刷新
stop_reload = true;
} else {
document.body.classList.remove('wh-move-btn');
}
};
break;
}
case Device.MOBILE: {
log.info(`开始调整按钮位置`);
// 加入css
let css_top = '0';
let slot_height = '76px';
// 判断有没有脚踢
if (hasKick) {
// 根据选择的武器调整
switch (getWhSettingObj()['quickAttIndex']) {
case 1: { // weapon_second
css_top = '76px';
break;
}
case 2: { // weapon_melee
css_top = '152px';
break;
}
case 3: { // weapon_temp
css_top = '228px';
break;
}
case 4: { // weapon_fists
css_top = '304px';
break;
}
case 5: { // weapon_boots
css_top = '380px';
break;
}
}
} else {
const slot = document.querySelector('#weapon_main') as HTMLElement;
const height = slot.offsetHeight + 1;
// TODO 待验证
slot_height = height + 'px';
// 根据选择的武器调整
switch (getWhSettingObj().quickAttIndex) {
case 1: { // weapon_second
css_top = `${ height }px`;
break;
}
case 2: { // weapon_melee
css_top = `${ height * 2 }px`;
break;
}
case 3: { // weapon_temp
css_top = `${ height * 3 }px`;
break;
}
case 4: { // weapon_fists
css_top = `${ height * 4 }px`;
break;
}
case 5: { // weapon_boots
css_top = `${ height * 5 }px`;
break;
}
}
}
const css_rule = `
.wh-move-btn #attacker div[class^="modal___"]{display: block;width: 0;top: ${ css_top };left:0;height:0;}
.wh-move-btn #attacker div[class^="dialog___"]{border:0;width:80px;height:${ slot_height };}
.wh-move-btn #attacker div[class^="colored___"]{display:block;padding:0;}
.wh-move-btn #attacker div[class^="title___"]{height:0;}
.wh-move-btn #attacker button{width:100%;margin:0;height:63px;white-space:normal;}
`;
addStyle(css_rule);
document.body.classList.toggle('wh-move-btn');
btn.onclick = () => {
if (getWhSettingObj().quickFinishAtt !== 3) {
btn.remove();
// 停止自动刷新
stop_reload = true;
} else {
document.body.classList.toggle('wh-move-btn');
}
};
break;
}
case Device.TABLET: {
break;
}
}
// 自动开打
if (getWhSettingObj()['autoStartFinish'] === true) {
if (btn.innerText.includes('(')) {
let interval_id = window.setInterval(() => {
if (!btn) {
clearInterval(interval_id);
return;
}
try {
btn.click();
} catch {
}
}, 100);
} else {
btn.click();
}
}
}
// 光速跑路
if (quickFinishAtt !== 3) {
const user_btn_select = ['leave', 'mug', 'hosp'][getWhSettingObj()['quickFinishAtt']];
const wrap = document.querySelector('#react-root');
log.info('光速跑路选项选中:', user_btn_select);
new MutationObserver(() => {
const btn_arr = document.querySelectorAll('div[class^="dialogButtons___"] button') as unknown as HTMLButtonElement[];
if (btn_arr.length > 1) btn_arr.forEach(btn => {
btn = btn as HTMLButtonElement;
const flag = btn.innerText.toLowerCase().includes(user_btn_select);
log.info('按钮内容:', btn.innerText, ',是否包含选中:', flag);
if (!flag) btn.style.display = 'none';
// 自动结束
else if (getWhSettingObj()['autoStartFinish'] === true) {
try {
btn.click();
} catch {
}
}
});
}).observe(wrap, { subtree: true, attributes: true, childList: true });
}
return;
}
// 错误的攻击页面
if (getWhSettingObj()['attRelocate'] && href.includes('loader2.php')) {
const spl = window.location.href.trim().split('=');
const uid = spl[spl.length - 1];
if (!/^\d+$/.test(uid)) return;
window.location.href = `https://www.torn.com/loader.php?sid=attack&user2ID=${ uid }`;
return;
}
}
// 战斗页面快速刷新
function doAttackReload() {
if (!window.ReactDOM) return;
let react_root = document.querySelector('#react-root');
if (!react_root.querySelector('#attacker')) return;
let script = document.querySelector('script[src*="/builds/attack/"]');
let url = script.src;
if (!url.contains('app.js')) return;
window.ReactDOM.unmountComponentAtNode(react_root);
script.remove();
let node = document.createElement('script');
node.src = url;
node.type = 'text/javascript';
document.head.appendChild(node);
}

View File

@ -0,0 +1,261 @@
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";
export default function (glob: Global) {
let { href, $zhongNode } = glob;
let channel: 'CMPY' | 'FAC';
const selector = { 'CMPY': "div#funds div.deposit", 'FAC': "div#armoury-donate div.cash" };
// 公司
if (href.includes('companies.php')) {
channel = "CMPY";
// 公司转跳存钱
if (!href.includes('funds') && getWhSettingObj()['companyRedirect']) {
const btn = document.getElementById('ui-id-9');
if (btn) {
btn.click();
log.info('已自动打开存钱页面');
}
}
// 收起冰蛙表格
if (getWhSettingObj()['companyBWCollapse']) {
elementReady('#effectiveness-wrap').then(BWtable_node => {
document.body.classList.add('wh-bwtable-ctrl');
addStyle(`.wh-bwtable-ctrl #effectiveness-wrap {display:none !important;}`);
const btn = document.createElement('button');
btn.innerHTML = '展开冰蛙表格';
btn.addEventListener('click', () => {
document.body.classList.toggle('wh-bwtable-ctrl');
btn.innerText = btn.innerText === '展开冰蛙表格' ? '收起冰蛙表格' : '展开冰蛙表格';
});
BWtable_node.before(btn);
});
}
// 一键存钱按钮
addActionBtn('一键存钱', companyDeposit, $zhongNode);
}
// 帮派
if (href.includes('factions.php')) {
channel = "FAC";
// 一键存钱按钮
addActionBtn('一键存钱', factionDeposit, $zhongNode);
}
// 存钱框浮动
if (getWhSettingObj()['floatDepo'] && channel) {
document.body.classList.add('wh-depo-helper');
addStyle(`.wh-depo-helper div#funds div.deposit,
.wh-depo-helper div#armoury-donate div.cash{position: fixed !important;
top: 150px;
right: 12px;
box-shadow: 0 0 8px 1px #00000091;
background: #f2f2f2;
z-index: 999999;}`);
elementReady(selector[channel]).then(node => {
const close_btn = document.createElement('button');
close_btn.addEventListener('click', () => {
document.body.classList.remove('wh-depo-helper');
close_btn.remove();
});
close_btn.innerHTML = '恢复原位';
close_btn.style.float = 'right';
node.prepend(close_btn);
});
}
// GT交易存钱
if (location.pathname.startsWith('/trade.php')) {
// GT助手
let node_link = null;
let handle = () => {
let { addRFC } = window;
// 不重复加载、已关闭的交易不加载
if (node_link !== null || location.hash.includes('logview')) return;
log.info('已添加GT助手');
// 获取交易id
let query_params = location.hash.slice(1);
let traceId;
query_params.split('&')
.forEach(param =>
(param.startsWith('ID=')) && (traceId = param.slice(3))
);
log.info('交易id为', traceId);
// 获取全部的钱数
let getTraceMoney = async () => {
if (typeof addRFC === 'function') {
let url = addRFC('/trade.php?step=getFullMoney&ID=' + traceId);
return (await ajaxFetch({ url: url, method: 'GET', referrer: 'trade.php' })).text();
}
};
// 监听jquery ajax请求
if (log.debug()) $(document).ajaxComplete((_, xhr, settings) => log.info({ xhr, settings }));
// react 加载完成后将节点加入视图中
elementReady('#trade-container').then(() =>
document.querySelector('#trade-container').before(node)
);
// 构建dom节点
let node = document.createElement('div');
node_link = node;
let nodeTitle = document.createElement('div');
let nodeCont = document.createElement('div');
let inputMoney = document.createElement('input');
let buttonDepositAll = document.createElement('button');
let buttonWithdraw = document.createElement('button');
let buttonWithdrawAll = document.createElement('button');
let style = document.createElement('style');
inputMoney.placeholder = '定额取钱';
inputMoney.type = 'number';
inputMoney.style.padding = '7px';
inputMoney.style.paddingLeft = '14px';
inputMoney.classList.add('m-right10');
buttonDepositAll.innerHTML = '全存';
buttonDepositAll.style.color = 'green';
buttonWithdraw.innerHTML = '定取';
buttonWithdrawAll.innerHTML = '全取';
buttonWithdrawAll.style.color = 'red';
nodeTitle.innerHTML = 'GT助手';
nodeTitle.classList.add('title-black', 'top-round');
style.innerHTML = '#WHGTHelper button{cursor:pointer;}#WHGTHelper button:hover{opacity:0.5;}';
nodeCont.append(inputMoney, buttonWithdraw, buttonDepositAll, buttonWithdrawAll);
nodeCont.classList.add('cont-gray', 'bottom-round');
nodeCont.style.padding = '10px';
node.id = 'WHGTHelper';
node.classList.add('m-bottom10');
node.append(nodeTitle, nodeCont, style);
// 定取
buttonWithdraw.addEventListener('click', async () => {
if (parseInt(inputMoney.value) < 1) {
WHNotify('无法定额取钱,原因:输入有误');
return;
}
let money = await getTraceMoney();
let int = { 'input': parseInt(inputMoney.value), 'all': parseInt(money) };
let diff = int.all - int.input;
if (diff < 1) {
WHNotify('无法定额取钱,原因:数不对');
return;
}
await ajaxFetch({
url: addRFC('/trade.php'),
method: 'POST',
referrer: 'trade.php',
body: `step=view&sub_step=addmoney2&ID=${ traceId }&amount=${ diff }&ajax=true`,
});
WHNotify(`已取 ${ int.input }`);
});
// 全存
buttonDepositAll.addEventListener('click', async () => {
let money = await getTraceMoney();
if (money === '0') return;
await ajaxFetch({
url: addRFC('/trade.php'),
method: 'POST',
referrer: 'trade.php',
body: `step=view&sub_step=addmoney2&ID=${ traceId }&amount=${ money }&ajax=true`,
});
WHNotify(`$${ money } 全部存入GT`);
});
// 全取
buttonWithdrawAll.addEventListener('click', async () => {
await ajaxFetch({
url: addRFC('/trade.php'),
method: 'POST',
referrer: 'trade.php',
body: `step=view&sub_step=addmoney2&ID=${ traceId }&amount=0&ajax=true`,
});
WHNotify('已全取');
});
};
if (location.hash.includes('ID=')) handle();
addEventListener('hashchange', () => {
if (location.hash.includes('ID=')) handle();
else {
node_link.remove();
node_link = null;
log('已移除GT助手');
}
});
}
// 任何位置公司一键存钱
if (getWhSettingObj()['companyDepositAnywhere']) {
addActionBtn('公司存钱', companyDepositAnywhere, $zhongNode);
}
}
// 公司一键存钱
async function companyDeposit() {
if (!location.href.contains('option=funds')) {
WHNotify('请先打开公司金库');
return;
}
let { addRFC } = window;
if (typeof addRFC !== 'function') return;
let url = addRFC('https://www.torn.com/inputMoneyAction.php?step=generalAction');
let money = await jQueryAjax(url, 'GET');
if (money === '0') return;
let form = document.querySelector('#funds .deposit form');
let funds_input = form.querySelectorAll('input.input-money');
funds_input.forEach(input => {
(input as HTMLInputElement).value = money;
input.attributes['data-money'].value = money;
});
$(form).trigger('submit');
WHNotify('存钱成功');
}
// 帮派一键存钱
async function factionDeposit() {
let form = document.querySelector('#armoury-donate form');
if (!location.hash.includes('tab=armoury') || !form) {
WHNotify('请先打开金库');
return;
}
if (typeof window.addRFC !== 'function') return;
let url = window.addRFC('https://www.torn.com/inputMoneyAction.php?step=generalAction');
let money = await jQueryAjax(url, 'POST');
if (money === '0') return;
let funds_input = form.querySelectorAll('input.input-money');
funds_input.forEach(input => {
(input as HTMLInputElement).value = money;
input.attributes['data-money'].value = money;
});
$(form).trigger('submit');
let dataStr = `ajax=true&step=armouryDonate&type=cash&amount=${ money }`;
let res = await (await fetch(window.addRFC('https://www.torn.com/factions.php'), {
method: 'POST',
body: dataStr,
headers: { 'X-Requested-With': 'XMLHttpRequest', 'Content-Type': 'application/x-www-form-urlencoded' }
})).json();
if (res.success === true) {
WHNotify('存钱成功');
WHNotify(`${ res.text }`);
}
}
// 所有页面公司一键存钱
async function companyDepositAnywhere() {
let { addRFC } = window;
if (typeof addRFC !== 'function') return;
let url = addRFC('https://www.torn.com/inputMoneyAction.php?step=generalAction');
let money = await jQueryAjax(url, 'GET');
if (money === '0') return;
let res = await (await fetch(addRFC('https://www.torn.com/companies.php?step=funds'), {
method: 'POST',
referrer: 'companies.php',
body: 'deposit=' + money,
headers: { 'X-Requested-With': 'XMLHttpRequest', 'Content-Type': 'application/x-www-form-urlencoded' }
})).text();
log.info(res);
let node = document.createElement('div');
node.innerHTML = res;
let success = node.querySelector('.success-message');
if (success) WHNotify(success.innerHTML);
}

View File

@ -2,7 +2,7 @@ import getWhSettingObj from "../utils/getWhSettingObj";
import setWhSetting from "../utils/setWhSetting"; import setWhSetting from "../utils/setWhSetting";
import popupMsg from "../utils/popupMsg"; import popupMsg from "../utils/popupMsg";
// 落地转跳 // 落地转跳设置
export default function landedRedirect() { export default function landedRedirect() {
let p = document.createElement('p'); let p = document.createElement('p');
let input = document.createElement('input'); let input = document.createElement('input');

View File

@ -0,0 +1,409 @@
import Global from "../../interface/GlobalVars";
import titleTrans from "../translate/titleTrans";
import contentTitleLinksTrans from "../translate/contentTitleLinksTrans";
import Device from "../../enum/Device";
import WHNotify from "../utils/WHNotify";
import getWhSettingObj from "../utils/getWhSettingObj";
import addActionBtn from "../utils/addActionBtn";
import addStyle from "../utils/addStyle";
import log from "../utils/log";
import doQuickFly from "./doQuickFly";
export default async function travelHelper(glob: Global): Promise<null> {
let { href, bodyAttrs, $zhongNode, device } = glob;
// URL判断 + 人不在城内
if (href.includes('index.php') && bodyAttrs['data-abroad'] === 'true') {
// 飞行中
if (bodyAttrs["data-traveling"] === 'true') {
// 飞行闹钟
if (device === Device.PC && getWhSettingObj()['trvAlarm']) {
// 获取目的地
let dest_cn;
let country = document.body.getAttribute('data-country');
if (country === 'torn') {
dest_cn = '回城';
} else {
dest_cn = {
'uk': "英国", 'switzerland': "瑞士", 'mexico': '墨西哥', 'canada': '加拿大', 'cayman': '开曼',
'hawaii': '夏威夷', 'argentina': '阿根廷',
'japan': '日本', 'china': '中国', 'uae': 'UAE', 'sa': '南非',
}[country] || country;
}
// 剩余时间
const remaining_arr = document.querySelector('#countrTravel').getAttribute('data-to');
const wh_trv_alarm = localStorage.getItem('wh_trv_alarm')
? JSON.parse(localStorage.getItem('wh_trv_alarm'))
: { 'enable': true, 'alert_time': 30, 'node_pos': [240, 240] };
const save_trv_settings = () => localStorage.setItem('wh_trv_alarm', JSON.stringify(wh_trv_alarm));
const wh_trv_alarm_node = document.createElement('div');
wh_trv_alarm_node.id = 'wh-trv-alarm';
wh_trv_alarm_node.style.left = `${ wh_trv_alarm.node_pos[0] }px`;
wh_trv_alarm_node.style.top = `${ wh_trv_alarm.node_pos[1] }px`;
wh_trv_alarm_node.innerHTML = `<div id="wh-trv-error"><p><b>❌ 没有权限</b><br/>点击网页内任意位置以激活闹钟</p></div>
<div id="wh-trv-alarm-title">
<h5 id="wh-trv-alarm-header"></h5>
</div>
<div id="wh-trv-alarm-bottom">
<div id="wh-trv-alarm-cont">
<p id="wh-trv-alarm-remaining"></p>
<p><span id="wh-trv-status">${ dest_cn === '回城' ? dest_cn : '飞往' + dest_cn } </span><span></span></p>
<div><label><input type="checkbox" ${ wh_trv_alarm.enable ? 'checked ' : ' ' }/> </label></div>
<div><label>(): <input type="number" value="${ wh_trv_alarm.alert_time || 30 }" /></label><button></button></div>
<div class="wh-trv-alarm-stop-hide"><button></button></div>
</div>
</div>
`;
addStyle(`
#wh-trv-alarm{
position:absolute;
width:248px;
/*left:${ wh_trv_alarm.node_pos[0] || 240 }px;
top:${ wh_trv_alarm.node_pos[1] || 240 }px;*/
background:white;
border-radius:4px;
box-shadow:#0000001f 0 0 10px 4px;
border:solid 1px #aaa;
z-index:100001;
margin:2em;
}
#wh-trv-alarm button{
margin:0;
}
#wh-trv-error{
position:absolute;
width:100%;
height:100%;
/*display: table;*/
display:none;
}
#wh-trv-error p{
background:#ffd0d0;
color:red;
display:table-cell;
vertical-align:middle;
padding:1em;
text-align:center;
}
#wh-trv-alarm-title{
height: 30px;
border-bottom: solid #aaa 1px;
cursor: move;
}
/*#wh-trv-alarm-move-btn span{
background:url(/images/v2/home_main/move.svg);
width: 30px;
height: 30px;
float: right;
cursor: move;
}*/
h5#wh-trv-alarm-header{
height: 100%;
line-height: 30px;
padding:0 12px;
font-weight: bold;
text-align: center;
}
#wh-trv-alarm-bottom{
padding: 12px;
}
#wh-trv-alarm-remaining{
float:right;
color:red;
}
#wh-trv-alarm-cont input[type="number"]{
width: 42px;
border-bottom: solid 1px #aaa;
}
.wh-trv-alarm-stop-hide{
display:none;
}
`);
document.body.append(wh_trv_alarm_node);
// 报错dom
const error_node = wh_trv_alarm_node.querySelector('#wh-trv-error') as HTMLElement;
// jquery拖动
// @ts-ignore
$(wh_trv_alarm_node).draggable({
containment: "body",
distance: 5,
handle: "#wh-trv-alarm-title",
stop: () => {
wh_trv_alarm.node_pos = [parseInt(wh_trv_alarm_node.style.left), parseInt(wh_trv_alarm_node.style.top)];
save_trv_settings();
},
scroll: false,
});
// 剩余时间dom
const remaining_node = wh_trv_alarm_node.querySelector('#wh-trv-alarm-remaining');
// 设定闹钟响的按钮
const set_node = wh_trv_alarm_node.querySelectorAll('#wh-trv-alarm-cont button')[0] as HTMLButtonElement;
// 落地前响铃时长
const cd_time = wh_trv_alarm_node.querySelector('input[type="number"]') as HTMLInputElement;
let count_down_notify: MyHTMLElement | { close: Function } = {
close: () => {
}
};
set_node.onclick = () => {
try {
wh_trv_alarm.alert_time = parseInt(cd_time.value);
} catch {
wh_trv_alarm.alert_time = 30;
}
save_trv_settings();
set_node.value = wh_trv_alarm.alert_time;
count_down_notify.close();
count_down_notify = WHNotify('设置已更新');
};
// 停止响铃按钮
const stop_node = wh_trv_alarm_node.querySelectorAll('#wh-trv-alarm-cont button')[1] as HTMLButtonElement;
stop_node.onclick = () => {
user_stop_alert = true;
stop_node.innerText = '本次已关闭';
stop_node.disabled = true;
}
// 开启闹钟勾选
const enable_node = wh_trv_alarm_node.querySelector('#wh-trv-alarm-cont input[type="checkbox"]') as HTMLInputElement;
let on_off_notify: MyHTMLElement | { close: Function } = {
close: () => {
}
};
enable_node.onchange = ev => {
wh_trv_alarm.enable = (<HTMLInputElement>ev.target).checked;
save_trv_settings();
on_off_notify.close();
on_off_notify = WHNotify(wh_trv_alarm.enable ? '闹钟已开启' : '闹钟已关闭');
};
// 剩余时间 秒
const remaining_sec = parseInt(remaining_arr);
// 落地时timestamp
const land_timestamp = Date.now() + remaining_sec * 1000;
// 音频dom
const audio = document.createElement('audio');
audio.src = 'https://www.torn.com/js/chat/sounds/Warble_1.mp3';
audio.play()
.then(() => audio.pause())
.catch(() => {
error_node.style.display = 'table';
const func = () => {
error_node.remove();
document.body.removeEventListener('click', func);
};
document.body.addEventListener('click', func);
});
// 是否正在响铃
let audio_play_flag = false;
// 用户是否停止当前响铃
let user_stop_alert = false;
// 响铃循环id
let audio_play_id = null;
// 响铃的方法
let audio_play_handle = () => {
if (user_stop_alert) {
clearInterval(audio_play_id);
audio_play_id = null;
return;
}
if (!audio_play_flag || !wh_trv_alarm.enable) return;
audio.play().then();
};
// 飞机小动画字符
const flying_arr = [
'✈ ',
'&nbsp;&nbsp;✈ ',
'&nbsp;&nbsp;&nbsp;&nbsp;✈ ',
'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;✈ ',
'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;✈ ',
'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;✈ ',
'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;✈ ',
'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;✈ ',
'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;✈ ',
'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;✈ ',
];
// 飞行的状态dom
const flying_status = wh_trv_alarm_node.querySelector('#wh-trv-status');
// 飞机的小动画dom
const flying_ani = flying_status.nextElementSibling;
// 飞机的计数
let flying_index = 0;
const id = window.setInterval(() => {
const remaining_time = (land_timestamp - Date.now()) / 1000 | 0;
remaining_node.innerText = `${ remaining_time / 3600 | 0 }${ remaining_time % 3600 / 60 | 0 }${ remaining_time % 60 }`;
if (remaining_time < wh_trv_alarm.alert_time) {
// flying_status.innerHTML = `即将落地...`;
if (wh_trv_alarm.enable) {
// 播放提示音
audio_play_flag = true;
if (audio_play_id === null && !user_stop_alert) audio_play_id = window.setInterval(audio_play_handle, 750);
stop_node.parentElement.classList.remove('wh-trv-alarm-stop-hide');
}
} else {
// flying_status.innerHTML = `飞行中...`;
if (wh_trv_alarm.enable) {
clearInterval(audio_play_id);
audio_play_id = null;
stop_node.parentElement.classList.add('wh-trv-alarm-stop-hide');
}
}
flying_ani.innerHTML = `${ flying_arr[flying_index] }`;
flying_index = (flying_index + 1) % flying_arr.length;
}, 1000);
log.info({
dest_cn,
remaining_arr,
wh_trv_alarm,
wh_trv_alarm_node,
error_node,
remaining_node,
set_node,
cd_time,
count_down_notify,
stop_node,
enable_node,
remaining_sec,
land_timestamp,
audio,
audio_play_flag,
user_stop_alert,
audio_play_id,
flying_status,
flying_index,
id
});
}
// 落地转跳 落地前事件
if (getWhSettingObj()['landedRedirect'] && document.querySelector('#tcLogo[title]') === null) {
window.addEventListener('beforeunload', () => {
let obj = { url: getWhSettingObj()['landedRedirect'], timestamp: Date.now() };
sessionStorage['wh-landed-redirect'] = JSON.stringify(obj);
});
}
}
// 不在飞行中 海外落地页面
else {
// 一键回城
addActionBtn('直接回城', travelBack, $zhongNode);
// 海外警告
if (getWhSettingObj()['abroadWarning']) {
let c = 1;
setInterval(() => WHNotify(`警告:您已海外落地${ c++ * 30 }`, {
timeout: 30,
sysNotify: true
}), 30000);
}
// 解毒提醒
if (bodyAttrs['data-country'] === 'switzerland') {
let page_title = document.querySelector('h4#skip-to-content');
let msg = document.createElement('div');
msg.innerHTML = `<div class="info-msg border-round">
<i class="info-icon"></i>
<div class="delimiter">
<div class="msg right-round" tabindex="0" role="alert">
<p><a href="/index.php?page=rehab"> </a></p>
</div>
</div>
</div>`;
msg.classList.add('info-msg-cont', 'green', 'border-round', 'm-bottom10');
page_title.before(msg);
}
}
return;
}
// URL判断 + 人在城内
else if (href.includes('index.php') && bodyAttrs['data-abroad'] === 'false') {
// 落地转跳
if (sessionStorage['wh-landed-redirect']) {
let { url, timestamp } = JSON.parse(sessionStorage['wh-landed-redirect']);
if (Date.now() - timestamp < 30000) {
sessionStorage.removeItem('wh-landed-redirect');
location.href = url;
}
}
}
// 起飞页面
if (href.contains(/travelagency\.php/)) {
// 起飞提醒
if (getWhSettingObj()['energyAlert']) {
const $$ = $('.content-wrapper');
const OB = new MutationObserver(() => {
OB.disconnect();
titleTrans();
contentTitleLinksTrans();
trans();
OB.observe($$.get(0), {
characterData: true,
attributes: true,
subtree: true,
childList: true
});
});
const trans = () => {
// 当前能量e
const energyBarStr = $('#barEnergy p[class^="bar-value__"]').text().trim();
const [curE, maxE] = energyBarStr.split('/').length === 2
? [parseInt(energyBarStr.split('/')[0]), parseInt(energyBarStr.split('/')[1])]
: [NaN, NaN];
const incTime = maxE === 150 ? 10 : 15;
const fullEnergyTime = !(isNaN(curE) || isNaN(maxE)) ? (maxE - 5 - curE) / 5 * incTime
+ (incTime - new Date().getMinutes() % incTime) : NaN;
// 起飞前提示
$('.travel-confirm .travel-question .q-wrap span:nth-of-type(2)').each((i, e) => {
if (isNaN(fullEnergyTime)) return;
const spl = e.innerText.trim().split(' ');
const [hours, minutes] = spl.length === 5
? [parseInt(spl[0]), parseInt(spl[3])]
: [0, parseInt(spl[0])];
if (fullEnergyTime < (hours * 60 + minutes) * 2) {
if (!$(e).parent().hasClass('wh-translated')) {
$(e).parent()
.prepend(`<div style="color: red">警告:该次飞行往返时间大于体力回复时间,将会爆体!</div>`)
.addClass('wh-translated');
}
}
});
};
trans();
OB.observe($$.get(0), {
characterData: true,
attributes: true,
subtree: true,
childList: true
});
}
// 一键起飞
if (sessionStorage['wh-quick-fly']) {
doQuickFly();
}
}
}
async function travelBack(): Promise<null> {
if (typeof window['getAction'] !== 'function') return;
let backHomeAction = function (): Promise<string> {
return new Promise(resolve => {
window.getAction({
type: "post",
action: 'travelagency.php',
data: {
step: 'backHomeAction'
},
success: function (msg) {
resolve(msg);
}
});
});
};
let res = await backHomeAction();
WHNotify(res);
if (!res.includes('error')) {
WHNotify('成功,即将刷新');
setTimeout(() => location.reload(), 3000);
} else {
WHNotify('出错了');
}
}

View File

@ -1,4 +1,4 @@
import {eventsDict, ocList, gymList} from "../../dictionary/translation"; import { eventsDict, gymList, ocList } from "../../dictionary/translation";
import log from "../utils/log"; import log from "../utils/log";
export default function eventsTrans(events: JQuery = $('span.mail-link')) { export default function eventsTrans(events: JQuery = $('span.mail-link')) {
@ -49,20 +49,18 @@ export default function eventsTrans(events: JQuery = $('span.mail-link')) {
}); });
// 通知翻译的开关 // 通知翻译的开关
if (!$('div#event-trans-msg').get(0) && !window.location.href.contains(/index\.php/)) { // if (!$('div#event-trans-msg').get(0) && !window.location.href.contains(/index\.php/)) {
// msgBox(`<div id="event-trans-msg">插件暂时不能翻译全部通知。<br> // let node = $('input#eventTransCheck');
// 如发现问题请发送通知并联系 <a href="profiles.php?XID=2687093">Woohoo[2687093]</a><br> // node.attr('checked', localStorage.getItem('wh_trans_event') === 'true');
// <input type="checkbox" id="eventTransCheck" name="eventTransCheck" /><label for="eventTransCheck">开启通知翻译</label> 可能会出现卡顿,默认开启</div>`); // node.change(function () {
$('input#eventTransCheck').attr('checked', localStorage.getItem('wh_trans_event') === 'true'); // if ($(this).attr('checked') === undefined) {
$('input#eventTransCheck').change(function () { // localStorage.setItem('wh_trans_event', 'false');
if ($(this).attr('checked') === undefined) { // } else {
localStorage.setItem('wh_trans_event', 'false'); // localStorage.setItem('wh_trans_event', 'true');
} else { // }
localStorage.setItem('wh_trans_event', 'true'); // eventsTrans();
} // });
eventsTrans(); // }
});
}
if (localStorage.getItem('wh_trans_event') === 'false') return; if (localStorage.getItem('wh_trans_event') === 'false') return;
if (events.length === 0) return; if (events.length === 0) return;

View File

@ -0,0 +1,16 @@
import { missionDict } from "../../dictionary/translation";
/**
*
*/
function getTaskHint(task_name): string {
task_name = task_name
.toLowerCase()
.replaceAll(' ', '_')
.replaceAll('-', '_')
.replaceAll(',', '');
if (!missionDict._taskHint[task_name]) return '暂无,请联系开发者';
const task = missionDict._taskHint[task_name].task || null;
const hint = missionDict._taskHint[task_name].hint || null;
return `${ task ? '任务要求:' + task : '暂无,请联系<a href="profiles.php?XID=2687093">Woohoo</a>' }${ hint ? '<br>提示:' + hint : '' }`;
}

View File

@ -0,0 +1,10 @@
/**
*
* @param {Node} node
* @param {Function} handler
*/
export default function walkNode(node, handler) {
let list = node.childNodes;
if (list.length === 0) handler(node);
else list.forEach(n => walkNode(n, handler));
}

View File

@ -1,51 +1,51 @@
import UserScriptEngine from "../../enum/UserScriptEngine"; import UserScriptEngine from "../../enum/UserScriptEngine";
import getScriptEngine from "./getScriptEngine"; import getScriptEngine from "./getScriptEngine";
import log from "./log";
// 跨域get请求 返回text // 跨域get请求 返回text
function COFetch(url:URL|string, method:string = 'get', body:any = null):Promise<string> { export default function COFetch(url: URL | string, method: 'get' | 'post' = 'get', body: any = null): Promise<string> {
return new Promise<string>((resolve, reject) => {
const engine = getScriptEngine(); const engine = getScriptEngine();
switch (engine) { switch (engine) {
case UserScriptEngine.RAW: { case UserScriptEngine.RAW: {
return new Promise((_, reject) => {
console.error(`[wh] 跨域请求错误:${ UserScriptEngine.RAW }环境下无法进行跨域请求`); console.error(`[wh] 跨域请求错误:${ UserScriptEngine.RAW }环境下无法进行跨域请求`);
reject(`错误:${ UserScriptEngine.RAW }环境下无法进行跨域请求`); reject(`错误:${ UserScriptEngine.RAW }环境下无法进行跨域请求`);
}); break;
} }
case UserScriptEngine.PDA: { case UserScriptEngine.PDA: {
const { PDA_httpGet, PDA_httpPost } = window; const { PDA_httpGet, PDA_httpPost } = window;
return method === 'get' ?
// get // get
new Promise((resolve, reject) => { if (method === 'get') {
if (typeof PDA_httpGet !== 'function') { if (typeof PDA_httpGet !== 'function') {
console.error('[wh] 跨域请求错误PDA版本不支持'); log.error('COFetch网络错误PDA版本不支持');
reject('错误PDA版本不支持'); reject('COFetch网络错误PDA版本不支持');
} }
PDA_httpGet(url) PDA_httpGet(url)
.then(res => resolve(res.responseText)) .then(res => resolve(res.responseText))
.catch(e => { .catch(e => {
console.error('[wh] 网络错误', e); log.error('COFetch网络错误', e);
reject(`[wh] 网络错误 ${e}`); reject(`COFetch网络错误 ${ e }`);
}); })
}) : }
// post // post
new Promise((resolve, reject) => { else {
if (typeof PDA_httpPost !== 'function') { if (typeof PDA_httpPost !== 'function') {
console.error('[wh] 跨域请求错误PDA版本不支持'); log.error('COFetch网络错误PDA版本不支持');
reject('错误PDA版本不支持'); reject('COFetch网络错误PDA版本不支持');
} }
PDA_httpPost(url, { 'content-type': 'application/json' }, body) PDA_httpPost(url, { 'content-type': 'application/json' }, body)
.then(res => resolve(res.responseText)) .then(res => resolve(res.responseText))
.catch(e => { .catch(e => {
console.error('[wh] 网络错误', e); log.error('COFetch网络错误', e);
reject(`[wh] 网络错误 ${e}`); reject(`COFetch网络错误 ${ e }`);
});
}); });
} }
break;
}
case UserScriptEngine.GM: { case UserScriptEngine.GM: {
let { GM_xmlhttpRequest } = window; let { GM_xmlhttpRequest } = window;
return new Promise((resolve, reject) => {
if (typeof GM_xmlhttpRequest !== 'function') { if (typeof GM_xmlhttpRequest !== 'function') {
console.error('[wh] 跨域请求错误用户脚本扩展API错误'); log.error('COFetch网络错误用户脚本扩展API错误');
reject('错误用户脚本扩展API错误'); reject('错误用户脚本扩展API错误');
} }
GM_xmlhttpRequest({ GM_xmlhttpRequest({
@ -57,9 +57,7 @@ function COFetch(url:URL|string, method:string = 'get', body:any = null):Promise
onerror: res => reject(`连接错误 ${ JSON.stringify(res) }`), onerror: res => reject(`连接错误 ${ JSON.stringify(res) }`),
ontimeout: res => reject(`连接超时 ${ JSON.stringify(res) }`), ontimeout: res => reject(`连接超时 ${ JSON.stringify(res) }`),
}); });
}
}
}); });
} }
}
}
export default COFetch

View File

@ -0,0 +1,20 @@
import log from "./log";
// 菜单 附加按钮
export default function addActionBtn(txt: string, func: (ev: Event) => void, mainBtnNode: Element): void {
addActionBtn.proxy(txt, func, mainBtnNode);
}
addActionBtn.proxy = (txt: string, func: (ev: Event) => void, mainBtnNode: Element) => {
if (mainBtnNode.querySelector('#wh-trans-icon-btn').nextSibling !== null) return;
let btn = document.createElement('button');
btn.style.padding = '8px 13px 8px 0';
btn.style.verticalAlign = 'bottom';
btn.style.color = '#4CAF50';
btn.innerHTML = txt;
btn.addEventListener('click', func);
mainBtnNode.querySelector('button').after(btn);
addActionBtn.proxy = () => {
log.error('错误:附加按钮已存在')
};
}

View File

@ -0,0 +1,29 @@
/**
* fetch ajax包装
* @param {Object} opt
* @param {String} opt.url
* @param {String} opt.referrer
* @param {String} opt.method
* @param {String} [opt.body]
* @returns {Promise<Response>}
*/
export default function ajaxFetch(opt) {
let { url, referrer, method, body = null } = opt;
let req_params: AjaxFetchOpt = {
headers: { 'X-Requested-With': 'XMLHttpRequest' },
referrer,
method,
};
if (method === 'POST') {
req_params.headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';
req_params.body = body;
}
return fetch(url, req_params);
}
interface AjaxFetchOpt {
headers: { 'X-Requested-With'?: string, 'Content-Type'?: string };
referrer: string;
method: string;
body?: any;
}

View File

@ -2,23 +2,26 @@
* mutation.observe * mutation.observe
* @param {String} selector - CSS规则的HTML元素选择器 * @param {String} selector - CSS规则的HTML元素选择器
* @param {Document} content - * @param {Document} content -
* @returns {Promise} * @param {number} timeout -
* @returns {Promise<HTMLElement|null>}
*/ */
export default function elementReady(selector, content = document): Promise<HTMLElement> { export default function elementReady(selector, content = document, timeout: number = 30000): Promise<HTMLElement | null> {
return new Promise(resolve => { return new Promise((resolve, reject) => {
let el = content.querySelector(selector); let el = content.querySelector(selector);
if (el) { if (el) {
resolve(el); resolve(el);
return return
} }
new MutationObserver((mutationRecords, observer) => { let observer = new MutationObserver((_, observer) => {
// Query for elements matching the specified selector content.querySelectorAll(selector).forEach((element) => {
Array.from(content.querySelectorAll(selector)).forEach((element) => {
resolve(element);
//Once we have resolved we don't need the observer anymore.
observer.disconnect(); observer.disconnect();
resolve(element);
}); });
}) });
.observe(content.documentElement, {childList: true, subtree: true}); setTimeout(() => {
observer.disconnect();
reject(`等待元素超时! [${ selector }]\n${ content.documentElement.tagName }`);
}, timeout);
observer.observe(content.documentElement, { childList: true, subtree: true });
}); });
} }

View File

@ -58,7 +58,7 @@ export default async function forStock() {
stocks: { 'African Violet': '花', 'Lion Plushie': '偶', 'Xanax': 'XAN' }, stocks: { 'African Violet': '花', 'Lion Plushie': '偶', 'Xanax': 'XAN' },
}]; }];
const now = new Date(); const now = new Date();
const res = await glob.fstock.get(); const res = await glob.fStock.get();
if (!res['stocks']) return; if (!res['stocks']) return;
dest.forEach(el => { dest.forEach(el => {
const update = (now.getTime() - new Date(res.stocks[el.name]['update'] * 1000).getTime()) / 1000 | 0 const update = (now.getTime() - new Date(res.stocks[el.name]['update'] * 1000).getTime()) / 1000 | 0

View File

@ -1,5 +1,4 @@
import UserScriptEngine from "../../enum/UserScriptEngine"; import UserScriptEngine from "../../enum/UserScriptEngine";
import Global from "../../interface/GlobalVars";
// 用户脚本平台类型 // 用户脚本平台类型
export default function () { export default function () {

View File

@ -0,0 +1,43 @@
import elementReady from "./elementReady";
import log from "./log";
/**
*
* @deprecated
* @returns {any}
*/
export default async function getSidebarData() {
let ret = {};
let sidebar_id = null;
let sessionKeys = Object.keys(sessionStorage);
if (sessionKeys.length < 2) {
// dom获取
let sidebar_menu_list = document.querySelectorAll('#sidebar a span[class*="linkName___"]');
log.info({ sidebar_menu_list })
if (sidebar_menu_list.length === 0) {
// TODO 当前根据侧边栏等待 sessionData
await elementReady('#sidebar a span[class*="linkName___"]');
sidebar_menu_list = document.querySelectorAll('#sidebar a span[class*="linkName___"]');
}
sidebar_menu_list.forEach(node => ret[node.innerHTML.trim().toLowerCase().replaceAll(' ', '_')] = true);
} else {
// session storage获取
for (let key of sessionKeys) {
if (key.startsWith('sidebarData')) {
sidebar_id = JSON.parse(sessionStorage.getItem(key));
break;
}
}
if (sidebar_id !== null) {
for (let area of Object.keys(sidebar_id['areas'])) {
ret[area] = true;
}
}
}
log.info({ ret, sidebar_id, sessionKeys })
if (Object.keys(ret).length === 0) {
log.error('无法获取数据,建议刷新重试');
}
return ret;
}

View File

@ -1,6 +1,9 @@
// 插件的配置 getter // 插件的配置 getter
const getWhSettingObj = function (): Object { export default function getWhSettingObj (): WHSettings {
return JSON.parse(localStorage.getItem('wh_trans_settings')) || {} return JSON.parse(localStorage.getItem('wh_trans_settings')) || {}
} }
export default getWhSettingObj export interface WHSettings {
// TODO 补全
[key: string]: any;
}

View File

@ -1,12 +0,0 @@
import getWhSettingObj from "./getWhSettingObj";
const isDev = function () : boolean {
try {
return getWhSettingObj()['isDev'] || false;
} catch (e) {
console.error(`[wh] dev状态错误 ${e}`);
return false;
}
}
export default isDev

View File

@ -0,0 +1,15 @@
// 包装jquery ajax异步 返回string
export default function jQueryAjax(url: string, method: 'GET' | 'POST'): Promise<string> {
return new Promise((res, rej) => {
$.ajax({
method: method,
url: url,
success: function (data) {
res(data)
},
error: function (e) {
rej(e)
}
});
});
}

8
src/func/utils/sleep.ts Normal file
View File

@ -0,0 +1,8 @@
/**
*
* @param {Number} ms
* @returns {Promise<unknown>}
*/
export default function sleep(ms) {
return new Promise(resolve => setTimeout(() => resolve(null), ms));
}

View File

@ -10,11 +10,28 @@ import priceWatcherHandle from "./func/utils/priceWatcherHandle";
import BuyBeer from "./func/utils/BuyBeer"; import BuyBeer from "./func/utils/BuyBeer";
import WindowActiveState from "./func/utils/WindowActiveState"; import WindowActiveState from "./func/utils/WindowActiveState";
import addStyle from "./func/utils/addStyle"; import addStyle from "./func/utils/addStyle";
import COFetch from "./func/utils/COFetch";
// 初始化方法,获取必要全局参数 // 初始化方法,获取必要全局参数
export default function init(): Global { export default function init(): Global {
let glob: Global = {}; let glob: Global = {
glob.window = window; window,
UWCopy: window.unsafeWindow,
version: '$$WUHU_DEV_VERSION$$',
isIframe: self !== top,
PDA_APIKey: '###PDA-APIKEY###',
isPDA: this.PDA_APIKey.slice(-1) !== '#',
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: this.isIframe ? null : priceWatcherHandle(),
beer: BuyBeer(),
popup_node: null,
notifies: { count: 0 },
isWindowActive: WindowActiveState(),
href: window.location.href,
bodyAttrs: {},
};
window.WHPARAMS = glob; window.WHPARAMS = glob;
let UWCopy = null; let UWCopy = null;
if (window.hasOwnProperty('unsafeWindow')) { if (window.hasOwnProperty('unsafeWindow')) {
@ -24,21 +41,12 @@ export default function init(): Global {
} catch { } catch {
} }
} }
glob.UWCopy = UWCopy;
// 脚本版本
glob.version = '$$WUHU_DEV_VERSION$$';
// iframe运行
glob.isIframe = self !== top;
// PDA
glob.PDA_APIKey = '###PDA-APIKEY###';
glob.isPDA = glob.PDA_APIKey.slice(-1) !== '#';
// 请求通知权限 // 请求通知权限
if (window.Notification) { if (window.Notification && Notification.permission !== 'granted') {
Notification.requestPermission().then(); Notification.requestPermission().then();
} else {
log.info({ Notification });
} }
// 扩展正则方法 // 扩展正则方法
@ -69,26 +77,7 @@ export default function init(): Global {
return clone; return clone;
}; };
glob.device = window.innerWidth >= 1000
? Device.PC : window.innerWidth <= 600 ? Device.MOBILE : Device.TABLET;
// 玩家信息
glob.player_info = getPlayerInfo();
// 海外库存对象
glob.fstock = autoFetchJSON('https://yata.yt/api/v1/travel/export/');
// 价格监视实例对象
glob.priceWatcher = glob.isIframe ? null : priceWatcherHandle();
// 抢啤酒
glob.beer = BuyBeer();
// 当前的弹出窗口
glob.popup_node = null;
// 当窗口关闭时关闭所有还存在的通知 // 当窗口关闭时关闭所有还存在的通知
glob.notifies = {count: 0};
window.addEventListener( window.addEventListener(
'beforeunload', 'beforeunload',
() => { () => {
@ -100,11 +89,6 @@ export default function init(): Global {
} }
); );
// 记录当前窗口唯一id
glob.isWindowActive = WindowActiveState();
glob.href = window.location.href;
addStyle(` addStyle(`
.wh-hide{display:none;} .wh-hide{display:none;}
#wh-trans-icon{ #wh-trans-icon{
@ -252,5 +236,19 @@ 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; return glob;
} }

View File

@ -2,23 +2,47 @@ import Device from "../enum/Device";
import { BeerMonitorLoop } from "../func/utils/BuyBeer"; import { BeerMonitorLoop } from "../func/utils/BuyBeer";
export default interface Global { export default interface Global {
href?: string; href: string;
// 插件图标
$zhongNode?: MyHTMLElement; $zhongNode?: MyHTMLElement;
// 弹窗
popup_node: MyHTMLElement;
// 啤酒助手
beer: BeerMonitorLoop;
// 留存的通知
notifies: NotifyWrapper;
// 价格监控
priceWatcher: { status: boolean };
// 海外库存
fStock: { get: () => Promise<any> };
// 玩家名和数字id
player_info: PlayerInfo;
// 设备类型
device: Device;
// PDA运行环境
isPDA: boolean;
// PDA自带apikey
PDA_APIKey: string;
/**
*
* @deprecated
*/
isIframe: boolean;
// 脚本版本
version: string;
// window 副本
window: Window;
// unsafeWindow 副本
UWCopy: Window & typeof globalThis;
// document body 属性
bodyAttrs: {
'data-country'?: string;
'data-celebration'?: string;
'data-traveling'?: string;
'data-abroad'?: string;
// [key: string]: string;
};
isWindowActive?(): boolean; // 窗口活动状态
isWindowActive(): boolean;
popup_node?: MyHTMLElement;
beer?: BeerMonitorLoop;
notifies?: NotifyWrapper;
priceWatcher?: { status: boolean };
fstock?: { get: () => Promise<any> };
player_info?: PlayerInfo;
device?: Device;
isPDA?: boolean;
PDA_APIKey?: string;
isIframe?: boolean;
version?: string;
window?: Window;
UWCopy?: Window & typeof globalThis;
// startTimestamp: number;
} }

View File

@ -3,6 +3,7 @@ import zhongIcon from "./zhongIcon";
import init from "./init"; import init from "./init";
import getWhSettingObj from "./func/utils/getWhSettingObj"; import getWhSettingObj from "./func/utils/getWhSettingObj";
import translateMain from "./func/translate/translateMain"; import translateMain from "./func/translate/translateMain";
import WHNext from "./WHNext";
(function main() { (function main() {
let started = new Date().getTime(); let started = new Date().getTime();
@ -15,6 +16,8 @@ import translateMain from "./func/translate/translateMain";
if (getWhSettingObj()['transEnable']) translateMain(glob.href); if (getWhSettingObj()['transEnable']) translateMain(glob.href);
WHNext(glob);
userscript(); userscript();
let runTime = new Date().getTime() - started; let runTime = new Date().getTime() - started;

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,6 @@ import addStyle from "./func/utils/addStyle";
import WHNotify from "./func/utils/WHNotify"; import WHNotify from "./func/utils/WHNotify";
import getScriptEngine from "./func/utils/getScriptEngine"; import getScriptEngine from "./func/utils/getScriptEngine";
import COFetch from "./func/utils/COFetch"; import COFetch from "./func/utils/COFetch";
import isDev from "./func/utils/isDev";
import popupMsg from "./func/utils/popupMsg"; import popupMsg from "./func/utils/popupMsg";
import forStock from "./func/utils/forStock"; import forStock from "./func/utils/forStock";
import updateTransDict from "./func/translate/updateTransDict"; import updateTransDict from "./func/translate/updateTransDict";
@ -22,18 +21,9 @@ import getDeviceType from "./func/utils/getDeviceType";
import Global from "./interface/GlobalVars"; import Global from "./interface/GlobalVars";
export default function zhongIcon(glob: Global) { export default function zhongIcon(glob: Global) {
setDefaultSettings(); setDefaultSettings();
// 菜单node // 菜单node
glob.$zhongNode = initIcon(getMenuItems(glob)); glob.$zhongNode = initIcon(getMenuItems(glob));
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 { interface MenuItemConfig {
@ -958,7 +948,7 @@ background-size: 100% auto !important;
// 本日不提醒 // 本日不提醒
$zhongNode.setting_root.querySelector('#wh-qua-alarm-check-btn').addEventListener('click', glob.beer.skip_today); $zhongNode.setting_root.querySelector('#wh-qua-alarm-check-btn').addEventListener('click', glob.beer.skip_today);
// 开发详情按钮 // 开发详情按钮
if (isDev()) $zhongNode.setting_root.querySelector('button#wh-devInfo').onclick = () => { if (log.debug()) $zhongNode.setting_root.querySelector('button#wh-devInfo').onclick = () => {
const date = new Date(); const date = new Date();
let os = '未知'; let os = '未知';
try { try {
@ -991,7 +981,7 @@ color:black;
}, },
}); });
// 测试 // 测试
if (isDev()) menu_list.push({ if (log.debug()) menu_list.push({
domType: 'button', domType: 'button',
domId: '', domId: '',
domText: '📐️ 测试', domText: '📐️ 测试',
@ -1399,12 +1389,12 @@ function getSettingItems(glob): MenuItemConfig[] {
setting_list.push({ setting_list.push({
domType: 'checkbox', domType: 'checkbox',
domId: 'wh-dev-mode', domId: 'wh-dev-mode',
domText: ` 开发者模式${isDev() ? ' <button id="wh-devInfo">详情</button>' : ''}`, domText: ` 开发者模式${ log.debug() ? ' <button id="wh-devInfo">详情</button>' : '' }`,
dictName: 'isDev', dictName: 'isDev',
isHide: true, isHide: true,
}); });
// 更多设定 // 更多设定
if (isDev()) setting_list.push({ if (log.debug()) setting_list.push({
domType: 'button', domId: 'wh-otherBtn', domText: '更多设定', clickFunc: () => { domType: 'button', domId: 'wh-otherBtn', domText: '更多设定', clickFunc: () => {
const html = `清空设置数据、请求通知权限、测试跨域请求`; const html = `清空设置数据、请求通知权限、测试跨域请求`;
const popup = popupMsg(html, '更多设定'); const popup = popupMsg(html, '更多设定');

View File

@ -3,7 +3,8 @@
"lib": [ "lib": [
"es6", "es6",
"dom", "dom",
"es2015" "es2015",
"ES2021.String"
], ],
"target": "es5" "target": "es5"
} }