Compare commits

..

70 Commits

Author SHA1 Message Date
wy
17b58c7924 优化起飞功能错误处理,取消网络拦截,避免官方功能被影响,更新版本至1.2.4 2025-04-07 16:03:01 +08:00
wy
4ae5ba1e46 Fix 2025-03-11 14:54:00 +08:00
d3b85ec361 快捷动作【REFILL】修复 2024-04-07 08:32:07 +08:00
6234424aa6 快捷动作【REFILL】修复 2024-04-03 09:33:54 +08:00
2285b3f41e v1.2.0 2024-03-29 09:56:49 +08:00
92173c57ec - BS估算缓存机制修复
- 快捷功能【快速犯罪】界面优化
2024-03-29 09:44:06 +08:00
2eb1fbf087 ### 添加
- 引入了BS估算功能

### 修改

- 快捷功能【快速犯罪】去除了烦人的通知
2024-03-27 16:54:55 +08:00
4d92efa48b 优化task逻辑 2024-03-21 15:22:19 +08:00
f66b165eb7 readme修正 2024-03-20 17:13:46 +08:00
5d42062001 ### 修改
- 快捷功能的【喝啤酒】移动至【快速犯罪】中
- profile页面中在线状态调整
2024-03-20 17:09:12 +08:00
234022c80c ### 添加
- 快捷功能-PI存钱

### 修改

- 上次动作的url判断修复
- profile页面中添加了更明显的上次动作时间
- 快速取钱功能添加了常用输入
2024-03-15 16:06:23 +08:00
5ec20fa327 更新 2024-01-08 11:11:09 +08:00
773332f406 快速犯罪中添加自定义选项 2024-01-05 17:44:43 +08:00
46a1339d5c 圣诞小镇掉落物记录表格中的undefined错误修复 2023-12-22 16:16:40 +08:00
ab0a1f2fdd Merge remote-tracking branch 'gitlab/dev' into dev 2023-12-08 14:50:22 +08:00
4121fd9ed0 更新 2023-12-08 14:49:35 +08:00
1c04624903 Update 2 files
- /README_ZHCN.md
- /README.md
2023-11-29 13:42:08 +00:00
64c906f064 更新 2023-11-29 17:55:36 +08:00
53756f53a7 更新 2023-09-19 11:05:28 +08:00
201dcb9ba5 更新 2023-09-19 10:54:30 +08:00
ad36cba92e 更新 2023-09-13 16:58:16 +08:00
27dfc7aedb 更新 2023-09-13 16:54:58 +08:00
c48165cd78 更新 2023-09-11 11:30:12 +08:00
876f49fb25 更新 2023-09-08 17:09:31 +08:00
b41b1696fc 更新菜单样式 2023-09-08 14:11:07 +08:00
2bcd3a83e4 更新 2023-08-23 15:40:13 +08:00
9c1db27ebe 更新 2023-06-27 16:57:16 +08:00
e7effb0881 更新 2023-06-26 18:13:47 +08:00
8146b165f9 更新 2023-06-19 17:27:58 +08:00
55ff6e7a5a 更新 2023-06-16 13:40:51 +08:00
a29fa0d9c8 更新 2023-06-16 10:44:30 +08:00
0a255f0e51 更新 2023-06-16 10:43:40 +08:00
207bea46ca 更新 2023-06-15 15:41:05 +08:00
d30e4f0d96 更新 2023-06-14 18:02:59 +08:00
d788227ae4 更新 2023-06-13 08:53:12 +08:00
3104ea2b53 更新 2023-06-12 17:47:44 +08:00
0c6ab02e8d 更新 2023-06-07 16:20:52 +08:00
ccf30c5dca 更新 2023-06-06 09:41:27 +08:00
412198358d 更新 2023-06-05 15:36:06 +08:00
302006530e 更新 2023-06-02 19:34:48 +08:00
7b661965a9 更新 2023-06-01 11:06:30 +08:00
8d8b78c6a1 更新 2023-05-31 18:31:03 +08:00
9b8bab4a92 更新 2023-05-31 11:36:50 +08:00
75834b5cef 更新 2023-05-30 16:27:50 +08:00
0e5e3180c9 更新 2023-05-26 10:40:29 +08:00
aa9d6dbaf3 更新 2023-05-04 10:36:33 +08:00
a4c2ed2463 更新 2023-04-28 14:13:49 +08:00
529c789315 更新 2023-04-24 11:07:54 +08:00
1ef3784e3d 更新 2023-04-17 16:56:51 +08:00
07755e81b3 更新 2023-04-17 16:53:44 +08:00
003056192e 更新 2023-04-10 10:34:41 +08:00
a4d6b581d7 更新 2023-04-10 10:33:57 +08:00
4b51e156bb 更新 2023-04-07 16:53:18 +08:00
e623d6fd14 更新 2023-04-04 12:02:14 +08:00
d707d983fb 结构调整 2023-04-03 18:00:41 +08:00
1a9d58c2b3 PDA bug,重新发布 2023-04-03 11:51:17 +08:00
dfde341070 更新 2023-03-24 18:23:41 +08:00
220b2b87a6 更新 2023-03-03 17:33:11 +08:00
00b0a0e783 更新 2023-03-03 17:32:40 +08:00
ed066f8565 更新 2023-03-03 15:35:43 +08:00
2a43fa33ab 更新 2023-03-02 18:12:29 +08:00
3c988ca0e5 Add CONTRIBUTING 2023-03-01 11:11:08 +00:00
f0ccfb4e21 Add LICENSE 2023-03-01 11:10:40 +00:00
10708e7f14 更新 2023-02-28 18:25:34 +08:00
791567bf37 更新 2023-02-26 03:20:03 +08:00
f28dbce227 Merge branch 'import-solidjs' into 'dev'
add vue

See merge request JJins/wuhu-torn-helper!1
2023-02-25 10:32:28 +00:00
da8bed9529 更新 2023-02-25 13:53:40 +08:00
ee8b660539 更新 2023-02-23 16:34:46 +08:00
7e52b9c382 更新 2023-02-01 18:21:45 +08:00
001e72d27b 更新 2023-02-01 09:44:59 +08:00
237 changed files with 39594 additions and 6249 deletions

4
.gitignore vendored
View File

@ -2,3 +2,7 @@
/.fleet
/src/dist/bundle.min.js
/src/dist/bundle.js
/dist
auto-imports.d.ts
components.d.ts
aws.xml

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptLibraryMappings">
<file url="file://$PROJECT_DIR$" libraries="{Node.js Core}" />
</component>
</project>

14
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,14 @@
{
// 使 IntelliSense
//
// 访: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"command": "npm run rollup",
"name": "rollup",
"request": "launch",
"type": "node-terminal"
}
]
}

View File

@ -1,9 +1,397 @@
# TODO
- 翻译baza npc商店、imarket、imarket搜索结果
# CHANGE
## 1.2.4
2025年04月07日
### 修改
- 优化起飞功能错误处理
- 取消网络拦截,避免官方功能被影响
## 1.2.3
2025年03月11日
### 修改
- 删除GS Load模块代码
- 删除翻译
- 删除去Google化部分代码
- 修复起飞逻辑
- 修复通知
## 1.2.2
2024年04月07日
### 修改
- 快捷动作【REFILL】修复
## 1.2.1
2024年04月03日
### 修改
- 快捷动作【REFILL】修复
## 1.2.0
2024年03月29日
### 修改
- BS估算缓存机制修复
- 快捷功能【快速犯罪】界面优化
## 1.1.9
2024年03月27日
### 添加
- 引入了BS估算功能
### 修改
- 快捷功能【快速犯罪】去除了烦人的通知
## 1.1.8
2024年03月20日
### 修改
- 快捷功能的【喝啤酒】移动至【快速犯罪】中
- profile页面中在线状态调整
## 1.1.7
2024年03月15日
### 添加
- 快捷功能-PI存钱
### 修改
- 上次动作的url判断修复
- profile页面中添加了更明显的上次动作时间
- 快速取钱功能添加了常用输入
## 1.1.6
2024年01月08日
### 修改
- refill功能改为菜单式新增支持3种refil
- 快速犯罪功能现已支持全部种类
## 1.1.5
2023年12月22日
### 修改
- 圣诞小镇掉落物记录表格中的undefined错误修复
## 1.1.4
2023年12月08日
### 修改
- 快速犯罪中添加了15-3
## 1.1.3
2023年11月29日
### 修改
- 通知浏览错误修复
## 1.1.2
2023年09月19日
### 修改
- 火车提醒错误修复
- 光速刷新错误修复
## 1.1.1
2023年09月13日
### 修改
- 调整了上次动作的显示逻辑
- 更准确的现金变动提醒
### 添加
- 监控模块-毒CD提醒
## 1.1.0
2023年09月11日
### 修改
- 更正上次动作的匹配路径、添加了毒和糖 CD 的提示
## 1.0.9
2023年09月08日
### 修改
- 侧边菜单UI优化
## 1.0.8
2023年08月23日
### 修改
- 通知浏览修复
## 1.0.7
2023年06月27日
### 修改
- 物品功能补充完善
- 菜单顺序调整
## 1.0.6
2023年06月26日
### 添加
- 物品功能标签
## 1.0.5
2023年06月19日
### 修改
- 移除原菜单
- 调整了新菜单的样式、部分逻辑
## 1.0.4
2023年06月16日
### 添加
- 标签页管理功能
## 1.0.3
2023年06月16日
### 修改
- 样式错误修复
## 1.0.2
2023年06月15日
### 修改
- 错误修复
- 菜单样式修改
## 1.0.1
2023年06月14日
### 添加
- 公司存取钱
## 1.0.0
2023年06月12日
### 添加
- PT购买
## 0.9.9
2023年06月07日
### 修改
- 购物助手错误修复及样式调整
## 0.9.8
2023年06月06日
### 修改
- 错误修复
## 0.9.7
2023年06月05日
### 添加
- bazaar快速开关店
- 购物助手
### 修改
- 部分样式修改
## 0.9.6
2023年06月01日
### 添加
- PC病毒快速操作
## 0.9.5
2023年05月31日
### 修改
- 明文密码简单编码处理
- 自动登录前添加确认
## 0.9.4
2023年05月31日
### 添加
- 自动登陆功能
## 0.9.3
2023年05月30日
### 添加
- 快速查看地图垃圾
## 0.9.2
2023年05月26日
### 添加
- 快速浏览通知
## 0.9.1
2023年05月04日
### 添加
- 新菜单中现在可以快速喝啤酒了
- 快速 refill
## 0.9.0
2023年04月28日
### 添加
- 新菜单中现在可以快速吃XAN了
- 快速犯罪
## 0.8.9
2023年04月24日
### 修改
- 战斗相关模块错误修复
- 重复通知错误修复
## 0.8.8
2023年04月17日
### 添加
- 新菜单
- 快速锻炼
- 一键起飞
## 0.8.7
2023年04月10日
### 修改
- 修复脚本首次运行配置获取错误的问题
- 修复"解决加载中"功能开启后无法加载插件图标的问题
- 修复功能选项错误读取默认值的问题
## 0.8.6
2023年04月07日
### 修改
- 结构调整
## 0.8.5
2023年04月03日
### 修改
- 结构调整
## 0.8.4
2023年03月03日
### 修改
- 错误修复
## 0.8.3
2023年03月02日
### 添加
- 物品查价
## 0.8.2
2023年02月01日
### 添加
- 新翻译:通用物品名与详情
## 0.8.1
2023年02月01日
### 添加
- 上次动作的开关
## 0.8.0
2023年01月16日

1
CONTRIBUTING.md Normal file
View File

@ -0,0 +1 @@

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 JJins
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1 +1,23 @@
# 芜湖助手 Torncity 翻译插件
# Wuhu Torn Helper
[![LICENSE](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
[中文](README_ZHCN.md)
[CHANGELOG(CN)](CHANGELOG.md)
A customized auxiliary-enhancement user script designed for a browser-based MMORPG game, featuring a range of convenient
functions.
This script does not include any automation-related code.
## Build
npm init
npm run rollup
## Use
[release.min.user.js](release.min.user.js)
Please install with Tampermonkey (for PC browser) or TornPDA.

20
README_ZHCN.md Normal file
View File

@ -0,0 +1,20 @@
# 芜湖助手 Torncity 翻译插件
[![LICENSE](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
[CHANGELOG](CHANGELOG.md)
一个为浏览器网页MMORPG游戏定制的辅助增强用户脚本包含了一系列便携功能。
此脚本不包含自动化相关代码。
## 编译
npm init
npm run rollup
## 使用
[release.min.user.js](https://gitlab.com/JJins/wuhu-torn-helper/-/raw/dev/README_ZHCN.md)
请使用 Tampermonkey (浏览器) 或 TornPDA 安装。

View File

@ -3,13 +3,14 @@
* 并生成日期时间与版本号
*/
let startTime = Date.now();
let fs = require('fs');
import { readFileSync, writeFileSync } from "fs";
import { prodConfig } from "./rollup.config.js";
let date = new Date();
let version = process.env.npm_package_version;
let formattedDateTime = `${ date.getFullYear() }${ ('0' + (date.getMonth() + 1)).slice(-2) }${ ('0' + date.getDate()).slice(-2) }${ ('0' + date.getHours()).slice(-2) }${ ('0' + date.getMinutes()).slice(-2) }`;
let metaData = `// ==UserScript==
let metaData =
`// ==UserScript==
// @lastmodified ${ formattedDateTime }
// @name 芜湖助手
// @namespace WOOH
@ -20,7 +21,6 @@ let metaData = `// ==UserScript==
// @downloadURL https://gitlab.com/JJins/wuhu-torn-helper/-/raw/dev/release.min.user.js
// @grant GM_xmlhttpRequest
// @grant unsafeWindow
// @connect ljs-lyt.com
// @connect yata.yt
// @connect github.io
// @connect gitlab.com
@ -29,6 +29,11 @@ let metaData = `// ==UserScript==
// ==/UserScript==
`
const data = fs.readFileSync('./dist/bundle.min.js', 'utf8');
fs.writeFileSync('./release.min.user.js', metaData + data.replace('$$WUHU_DEV_VERSION$$', version), 'utf8');
console.log(`版本 ${ version } 构建完成, build.js耗时${ Date.now() - startTime }ms`);
const data = readFileSync('./' + prodConfig.output.file, 'utf8');
writeFileSync(
'./release.min.user.js',
metaData + data.replace('$$WUHU_DEV_VERSION$$', version),
'utf8'
);
// rmSync('./' + prodConfig.output.file);
console.log(`版本 ${ version } 构建完成`);

6
css-module.d.ts vendored Normal file
View File

@ -0,0 +1,6 @@
// declare module "*.module.css" {
// const css: string;
// const classes: { [key: string]: string };
// export default classes;
// export { css };
// }

21
custom-injector.js Normal file
View File

@ -0,0 +1,21 @@
export const customInjector = (varName) => {
let rt = ((__var) => {
const inject = (ob) => {
if (document && document.head) {
ob?.disconnect();
const style = document.createElement('style');
style.setAttribute('type', 'text/css');
style.innerHTML = __var;
document.head.appendChild(style);
}
};
if (document && document.head) {
inject();
} else {
new MutationObserver((_, ob) => {
inject(ob);
}).observe(document.documentElement, { childList: true });
}
}).toString();
return `(${ rt })(${ varName })`;
};

19
global.d.ts vendored
View File

@ -108,15 +108,22 @@ declare interface TornGetActionParams {
}
declare module "*.html" {
const value: string;
export default value;
const html: string;
export default html;
}
declare module "*.css" {
const value: string;
export default value;
declare module "*.module.css" {
const css: string;
// const classes: { [key: string]: string };
// export default classes;
export default css;
// export { css };
}
declare function GM_xmlhttpRequest(init: any): void;
declare var unsafeWindow: Window & typeof globalThis;
declare type Constructor<T = any> = new (...args: any[]) => T;
declare interface ClassType<T> {
new(...args: unknown[]): T
}

16538
misc/vue.js Normal file

File diff suppressed because it is too large Load Diff

10620
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,25 +1,43 @@
{
"name": "wuhu-torn-helper",
"version": "0.8.0",
"version": "1.2.4",
"description": "芜湖助手",
"dependencies": {},
"scripts": {
"release": "rollup -c rollup-prod.config.js && node build.js",
"watch": "rollup -c -w"
"release": "cross-env NODE_ENV=production rollup -c && node build.mjs",
"watch": "cross-env NODE_ENV=development rollup -c -w",
"rollup": "cross-env NODE_ENV=development rollup -c"
},
"devDependencies": {
"@element-plus/icons-vue": "^2.1.0",
"@rollup/plugin-alias": "^4.0.3",
"@rollup/plugin-commonjs": "^24.0.1",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^15.0.1",
"@rollup/plugin-replace": "^5.0.2",
"@rollup/plugin-terser": "^0.4.0",
"@rollup/plugin-typescript": "^8.5.0",
"@types/jquery": "^3.5.14",
"@types/node": "^18.0.6",
"@types/node": "^20.6.0",
"@vitejs/plugin-vue": "^4.0.0",
"@vue/tsconfig": "^0.1.3",
"cross-env": "^7.0.3",
"element-plus": "^2.3.10",
"just-clone": "^6.2.0",
"npm": "^8.19.2",
"reflect-metadata": "^0.1.13",
"rollup": "^2.79.0",
"rollup-plugin-html-literals": "^1.1.5",
"rollup-plugin-serve": "^2.0.1",
"rollup-plugin-string": "^3.0.0",
"rollup-plugin-uglify": "^6.0.4",
"rollup-plugin-postcss": "^4.0.2",
"rollup-plugin-string-html": "^1.0.0",
"rollup-plugin-styles": "^4.0.0",
"rollup-plugin-typescript2": "^0.34.1",
"tslib": "^2.4.0",
"typescript": "^4.8.3",
"uglify-js": "^3.16.1"
}
"unplugin-auto-import": "^0.15.2",
"unplugin-element-plus": "^0.7.0",
"unplugin-icons": "^0.16.1",
"unplugin-vue-components": "^0.24.1",
"vant": "^4.1.2",
"vue": "^3.2.47"
},
"type": "module"
}

File diff suppressed because one or more lines are too long

View File

@ -1,20 +0,0 @@
import typescript from "@rollup/plugin-typescript";
import json from "@rollup/plugin-json";
import { string } from "rollup-plugin-string";
import { uglify } from "rollup-plugin-uglify";
export default {
input: 'src/ts/index.ts',
output: {
file: 'dist/bundle.min.js',
format: 'iife',
},
plugins: [
typescript(),
json(),
string({
include: ["**/*.html", "**/*.css"]
}),
uglify(),
],
};

View File

@ -1,24 +1,131 @@
import typescript from "@rollup/plugin-typescript";
import json from "@rollup/plugin-json";
// import template from "rollup-plugin-html-literals";
import { string } from "rollup-plugin-string";
// [!] Error: Unexpected token (Note that you need plugins to import files that are not JavaScript)
// src/vue/ItemPrice.vue?vue&type=script&lang.ts (35:13)
// TODO 官方提供ts插件在vue模版中使用ts语言时报错
// import typescript from "@rollup/plugin-typescript";
export default {
// TODO 在rollup watch模式中不更新vue模版ts部分代码
import typescript2 from "rollup-plugin-typescript2";
import json from "@rollup/plugin-json";
import html from "rollup-plugin-string-html";
import resolve from "@rollup/plugin-node-resolve";
import replace from "@rollup/plugin-replace";
import alias from "@rollup/plugin-alias";
import vue from "@vitejs/plugin-vue";
import styles from "rollup-plugin-styles";
import { customInjector } from "./custom-injector.js";
import terser from "@rollup/plugin-terser";
import clone from "just-clone";
import commonjs from '@rollup/plugin-commonjs';
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
// import { VantResolver } from 'unplugin-vue-components/resolvers';
import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
import IconsResolver from 'unplugin-icons/resolver';
import Icons from 'unplugin-icons/vite'
import ElementPlus from 'unplugin-element-plus/rollup'
let node_env = process.env.NODE_ENV;
let vuePath = node_env === 'production' ?
'vue/dist/vue.runtime.esm-browser.prod.js' :
'vue/dist/vue.runtime.esm-browser.js';
const devConfig = {
input: 'src/ts/index.ts',
output: {
file: 'dist/bundle.js',
format: 'iife',
name: 'bundle.js',
},
plugins: [
// template({
// include: '*.html',
// failOnError: true
// }),
typescript(),
json(),
string({
include: ["**/*.html", "**/*.css"]
html({
include: ["**/*.html"],
minifier: {
includeAutoGeneratedTags: true,
removeAttributeQuotes: false,
removeComments: true,
removeRedundantAttributes: false,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
sortClassName: true,
useShortDoctype: true,
collapseWhitespace: true,
minifyCSS: true,
}
}),
// 根据环境更改vue源
alias({
entries: [
{ find: 'vue', replacement: vuePath },
]
}),
// 为vue替换环境变量
replace({
values: {
'process.env.NODE_ENV': () => JSON.stringify(node_env),
'__VUE_OPTIONS_API__': () => JSON.stringify(false),
'__VUE_PROD_DEVTOOLS__': () => JSON.stringify(true),
},
preventAssignment: true,
}),
// 引入node相关方法
resolve({
browser: true,
preferBuiltins: false,
}),
commonjs(),
vue({ isProduction: node_env === 'production' }),
AutoImport({
resolvers: [
ElementPlusResolver(),
IconsResolver({
prefix: 'Icon',
}),
],
}),
Components({
resolvers: [
IconsResolver({
enabledCollections: ['ep'],
}),
ElementPlusResolver()
],
// resolvers: [VantResolver()],
}),
Icons(),
ElementPlus(),
// 自定义注入器注入vue部分css
styles({
// modules: true,
// namedExports: true,
exclude: /static\/css\/.+\.css/,
mode: [
"inject",
(varName) => customInjector(varName),
],
minimize: true
}),
// 非vue部分css逻辑代码中手动注入
styles({
include: /static\/css\/.+\.css/,
// modules: true,
// namedExports: true,
mode: [
"inject",
() => ``,
],
minimize: true
}),
typescript2({
tsconfig: "./tsconfig.json",
}),
// uglify(),
],
};
const prodConfig = clone(devConfig);
prodConfig.plugins.push(terser());
prodConfig.output.file = 'dist/bundle.min.js';
prodConfig.output.name = 'bundle.min.js';
export default [devConfig, prodConfig];
export { prodConfig };

7
src/shims-vue.d.ts vendored Normal file
View File

@ -0,0 +1,7 @@
/* eslint-disable */
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}
/* eslint-enable */

View File

@ -282,3 +282,11 @@ div#wh-popup::after {
.non-selection {
user-select: none;
}
.mt-4 {
margin-bottom: 4px;
}
/*.el-overlay {*/
/* backdrop-filter: blur(20px);*/
/*}*/

View File

@ -0,0 +1,3 @@
body.wh-hide-title .content-title, body.wh-hide-title .info-msg-cont {
display: none !important;
}

View File

@ -0,0 +1,71 @@
<div class="acc-title">
<span class="item-desc tt-buy">
<span class="item Alcohol" itemid="180" loaded="0">
<span class="item-plate">
<img alt="Bottle of Beer" class="torn-item large"
src="/images/items/180/large.png"
srcset="/images/items/180/large.png 1x, /images/items/180/large@2x.png 2x, /images/items/180/large@3x.png 3x, /images/items/180/large@4x.png 4x">
</span>
<span class="item-hover">
<button aria-labelledby="Show info: 180-name 180-price 180-stock" class="view-info wai-btn"
value="100"></button>
<button aria-label="Buy: Bottle of Beer" class="buy-info wai-btn" value="100"></button>
</span>
</span>
<span class="desc">
<input name="shoparea" type="hidden" value="100">
<span id="180-name" class="name t-overflow bold">Bottle of Beer</span>
<span id="180-price" class="price t-gray-6 tt-modified" data-sell="$5">$10<span
class="tt-profit positive"><i class="fas fa-caret-up"></i>$1,636</span></span>
<span id="180-stock" class="stock t-gray-6 t-overflow">Alcohol (<span class="instock">5,000</span> in
stock)</span>
</span>
<span class="buy-act-wrap">
<button aria-label="Close" class="close-icon p0 wai-btn" value="100"></button>
<label class="wai" for="180">Amount
of Bottle of Beer</label>
<input id="180" autocomplete="new-amount" maxlength="3" name="buyAmount[]" type="text" value="100">
<span class="buy-act bold">
<button class="wai-support t-blue h" value="100">Buy<br><span class="tt-max-buy">fill
max</span></button>
<div class="tt-max-buy-overlay"></div>
</span>
</span>
</span>
<div class="torn-divider divider-right"></div>
<div class="confirm-wrap" tabindex="0">
<span class="confirm">
<span>
Are you sure you would like to buy
</span>
<span>
<span class="count"></span>
x Bottle of Beer for
$<span class="total"></span>
</span>
<span class="confirm-act m-top5">
<a class="wai-support yes m-right10 bold t-blue h" data-id="180" href="#">
Yes
</a>
<span class="no bold">
<a class="wai-support t-blue h" href="#">
No
</a>
</span>
</span>
</span>
</div>
<div class="success-wrap">
<span class="success">
<span class="t-green bold">
<span class="ajax-preloader"></span>
</span>
</span>
<button aria-label="Close" class="close-icon p0 wai-btn" value="100"></button>
</div>
<div class="msg-wrap">
<span class="t-green bold">
<span class="ajax-preloader"></span>
</span>
</div>
</div>

View File

@ -9,7 +9,7 @@
<li>走进蓝色的门(左二)[111-60]</li>
<li>过了蓝门后继续沿路向南</li>
<li>然后沿路向东,再向南</li>
<li>再向东走,会看到“恐怖之家”,进入这栋楼[137, -84]</li>
<li>再向东走,会看到「恐怖之家」,进入这栋楼[137, -84]</li>
<li>向北走,进入洞口[164, -81]</li>
<li>穿过隧道到达另一端的洞穴口[142, -63]<b>避开所有怪物和NPC</b></li>
<li>来到另一个岛上</li>

View File

@ -7,6 +7,7 @@
<h4>手机</h4>
<p>安卓 KIWI 等可以用油猴脚本的浏览器也可以点上面的链接安装👆</p>
<p>Torn PDA app 或 Alook 用户可打开<a href="//jjins.github.io/fyfuzhi/" target="_blank">这个网页</a>快捷复制粘贴。</p>
<p>Torn PDA 中Injection time 请选择 Start 达到最好效果。</p>
<h4>直接复制</h4>
<p>加载脚本然后直接复制粘贴到用户脚本处。</p>
<p>

67
src/ts/App.ts Normal file
View File

@ -0,0 +1,67 @@
import Interrupt from "./class/Interrupt"
import Initializer from "./class/Initializer"
// import { Common } from "./class/Common"
// import UrlRouter from "./class/UrlRouter"
import translateMain from "./func/translate/translateMain"
import CommonUtils from "./class/utils/CommonUtils"
import LocalConfigWrapper from "./class/LocalConfigWrapper"
import ClassName from "./container/ClassName"
import { Injectable } from "./container/Injectable"
import FeatureMan from "./man/FeatureMan"
import Logger from "./class/Logger"
@ClassName('Application')
@Injectable()
export default class App {
private readonly logger = Logger.factory(App)
constructor(
private readonly interrupt: Interrupt,
private readonly initializer: Initializer,
// private readonly common: Common,
// private readonly urlRouter: UrlRouter,
private readonly configWrapper: LocalConfigWrapper,
private readonly utils: CommonUtils,
private readonly featureMan: FeatureMan,
) {
}
public run() {
this.interrupt.conditionInterrupt();
// 初始化
this.initializer.init();
// this.featureMan.fStart().then(() => this.featureMan.printTable());
// 插件图标和设置菜单
// this.icon.init();
let tmp = () => {
// // 所有页面通用
// try {
// // this.common.resolve.apply(this.common, this.run);
// // this.common.resolve(() => this.run());
// } catch (e) {
// }
(async function (self: App) {
await self.featureMan.fStart()
self.featureMan.printTable()
})(this)
// URL匹配
// this.urlRouter.resolve();
// 翻译
// if (this.configWrapper.config.transEnable)
// translateMain(window.location.href);
};
// TODO 临时检测jquery
if (typeof $ === "function") {
tmp();
} else {
this.utils.jQueryReady().then(() => tmp());
}
}
}

View File

@ -1,45 +0,0 @@
import WuhuBase from "./WuhuBase";
import WuHuTornHelper from "./WuhuTornHelper";
import ZhongIcon from "./ZhongIcon";
import { Common } from "./Common";
import UrlPattern from "./UrlMatch";
import WuhuConfig from "./WuhuConfig";
import translateMain from "../func/translate/translateMain";
import CommonUtils from "./utils/CommonUtils";
export default class Application {
public static main(): void {
WuhuBase.conditionInterrupt();
// 初始化
WuHuTornHelper.getInstance().init();
// 插件设置默认值
WuhuConfig.setDefaults();
// 插件图标和设置菜单
ZhongIcon.getInstance().init();
let tmp = () => {
// 所有页面通用
try {
Common.getInstance().resolve(Application.main);
} catch (e) {
// if (e.message === '重载') Common.getInstance().resolve(null);
}
// URL匹配
UrlPattern.getInstance().resolve();
// 翻译
if (WuhuConfig.get('transEnable')) translateMain(window.location.href);
};
// TODO 临时检测jquery
if (typeof $ === "function") {
tmp();
} else {
CommonUtils.getInstance().jQueryReady().then(() => tmp());
}
}
}

View File

@ -1,112 +1,160 @@
import depoHelper from "../func/module/depoHelper";
import travelHelper from "../func/module/travelHelper";
import priceWatcherHandle from "../func/module/priceWatcherHandle";
import WuhuBase from "./WuhuBase";
import WuhuConfig from "./WuhuConfig";
import CompanyHelper from "./action/CompanyHelper";
import AttackHelper from "./action/AttackHelper";
import SidebarHelper from "./action/SidebarHelper";
import CommonUtils from "./utils/CommonUtils";
import Log from "./Log";
import FetchUtils from "./utils/FetchUtils";
import ZhongIcon from "./ZhongIcon";
import Alert from "./utils/Alert";
import FetchEventCallback from "./action/FetchEventCallback";
import globVars from "../globVars";
import Translate from "./action/Translate";
/**
*
*/
export class Common extends WuhuBase {
className = 'Common';
public resolve(mainMethod) {
let glob = Common.glob;
// 价格监控
priceWatcherHandle(glob.isPDA, glob.PDA_APIKey);
// 啤酒提醒
if (WuhuConfig.get('_15Alarm')) glob.beer.start();
SidebarHelper.getInstance();
/**
* ()
* All('script[src*="google"]')
* All('#gtm_tag')
* All('script[src*="chat/gonline"]')
* All('head script[nonce]')
*/
if (document.readyState === 'interactive' && WuhuConfig.get('SolveGoogleScriptPendingIssue')) {
window.stop();
document.open();
document.addEventListener('readystatechange', function readyStateChangeHandler() {
Log.info('document.readyState', document.readyState);
if (document.readyState === 'complete') {
document.removeEventListener('readystatechange', readyStateChangeHandler);
mainMethod();
throw new Error('页面已重载');
}
});
FetchUtils.getInstance().fetchText(window.location.href).then(resp => {
let removed = resp;
[
/<script id="gtm_tag">.+?<\/script>/ms,
/<script async src="https:\/\/www\.google.+?<\/script>/ms,
/<script nonce=".+?gtag.+?<\/script>/ms,
/<script.+?google.+?\/script>/,
].forEach(remove => {
removed = removed.replace(remove, '');
});
Log.info({ removed });
document.write(removed);
document.close();
});
}
// fetch方法处理
FetchEventCallback.getInstance();
// fetch方法处理-翻译
globVars.responseHandlers.push(Translate.responseHandler);
// 存钱相关
depoHelper();
// 飞行相关
travelHelper().then();
// 战斗相关
AttackHelper.getInstance();
// 公司助手
CompanyHelper.getInstance();
// 自定义CSS
if (WuhuConfig.get('CustomCss')) {
Log.info('应用自定义CSS');
CommonUtils.addStyle(WuhuConfig.get('CustomCss'));
}
// 现金变动提醒
if (WuhuConfig.get('CashChangeAlert')) CommonUtils.elementReady("#user-money").then(userMoney => {
new MutationObserver((mutations, observer) => {
if (!WuhuConfig.get('CashChangeAlert')) {
observer.disconnect();
new Alert('现金变动提醒已关闭', { sysNotify: true });
return;
}
Log.info("现金变动提醒", mutations);
mutations.forEach(item => {
if (item.attributeName === 'data-money') {
ZhongIcon.getInstance().updateCashView(userMoney.innerText);
new Alert(
'提醒: 现金变动 ' + item.oldValue + ' -> ' + userMoney.innerText,
{ sysNotify: true }
);
}
});
}).observe(userMoney, { attributes: true, attributeOldValue: true })
});
}
}
// import depoHelper from "../func/module/depoHelper";
// import TravelHelper from "../feature/TravelHelper";
// import priceWatcherHandle from "../func/module/priceWatcherHandle";
// import CompanyHelper from "../feature/CompanyHelper";
// import AttackHelper from "./action/AttackHelper";
// import SidebarHelper from "../feature/SidebarHelper";
// import CommonUtils from "./utils/CommonUtils";
// import FetchUtils from "./utils/FetchUtils";
// import FetchEventCallback from "./action/FetchEventCallback";
// import globVars from "../globVars";
// import TranslateNew from "./action/TranslateNew";
// import ClassName from "../container/ClassName";
// import { Injectable } from "../container/Injectable";
// import LocalConfigWrapper from "./LocalConfigWrapper";
// import Logger from "./Logger";
// import BuyBeerHelper from "../feature/BuyBeerHelper";
// import ModuleLoader from "./ModuleLoader";
// import TornPDAUtils from "./utils/TornPDAUtils";
// import TravelItem from "../feature/TravelItem";
// import IconHelper from "../feature/IconHelper";
// import MsgWrapper from "./utils/MsgWrapper";
// import toThousands from "../func/utils/toThousands";
// import { WHIntervalLoader } from "../monitor/WHIntervalLoader";
//
// /**
// * 脚本不区分页面的通用功能入口
// */
// @Injectable()
// @ClassName('Common')
// export class Common {
// private readonly logger = Logger.factory(Common)
//
// constructor(
// private readonly localConfigWrapper: LocalConfigWrapper,
// // private readonly fetchEventCallback: FetchEventCallback,
// // private readonly translateNew: TranslateNew,
// // private readonly tornPDAUtils: TornPDAUtils,
// // private readonly buyBeerHelper: BuyBeerHelper,
// // private readonly fetchUtils: FetchUtils,
// // private readonly moduleLoader: ModuleLoader,
// private readonly msgWrapper: MsgWrapper,
// ) {
// }
//
// public resolve(mainMethod) {
// // window.setInterval(()=>this.msgWrapper.create('test',{sysNotify:true},'info'),2000);
//
// // // fetch方法处理
// // globVars.responseHandlers.push(
// // (...args: any[]) => this.fetchEventCallback.responseHandler.apply(this.fetchEventCallback, args)
// // );
// // // fetch方法处理-翻译
// // globVars.responseHandlers.push(
// // (...args: any[]) => this.translateNew.responseHandler.apply(this.translateNew, args)
// // );
//
// // // 价格监控
// // priceWatcherHandle(this.tornPDAUtils.isPDA(), this.tornPDAUtils.APIKey);
//
// // 啤酒提醒
// // if (this.localConfigWrapper.config._15Alarm) this.buyBeerHelper.start();
//
// // this.moduleLoader.push(SidebarHelper);
// // this.moduleLoader.push(TravelItem);
// // this.moduleLoader.push(WHIntervalLoader)
//
// /**
// * 解决一直转圈(加载中)的问题
// * All('script[src*="google"]')
// * All('#gtm_tag')
// * All('script[src*="chat/gonline"]')
// * All('head script[nonce]')
// */
// // try {
// // if (document.readyState === 'interactive' && this.localConfigWrapper.config.SolveGoogleScriptPendingIssue) {
// // window.stop();
// // document.open();
// // let readyStateChangeHandler = () => {
// // this.logger.info('document.readyState', document.readyState);
// // if (document.readyState === 'complete') {
// // document.removeEventListener('readystatechange', readyStateChangeHandler);
// // mainMethod();
// // throw new Error('页面已重载');
// // }
// // }
// // document.addEventListener('readystatechange', readyStateChangeHandler);
// // this.fetchUtils.fetchText(window.location.href).then(resp => {
// // let removed = resp;
// // [
// // /<script id="gtm_tag">.+?<\/script>/ms,
// // /<script async src="https:\/\/www\.google.+?<\/script>/ms,
// // /<script nonce=".+?gtag.+?<\/script>/ms,
// // /<script.+?google.+?\/script>/,
// // ].forEach(remove => {
// // removed = removed.replace(remove, '');
// // });
// // this.logger.info({ removed });
// // document.write(removed);
// // document.close();
// // });
// // }
// // } catch (e) {
// // this.logger.error('【解决一直转圈(加载中)的问题】错误',e)
// // }
//
// // // 存钱相关
// // try {
// // depoHelper();
// // } catch (e) {
// // this.logger.error('【存钱相关】错误',e)
// // }
//
// // // 飞行相关
// // this.moduleLoader.push(TravelHelper);
//
// // 战斗相关
// // this.moduleLoader.push(AttackHelper);
//
// // 公司助手
// // this.moduleLoader.push(CompanyHelper);
//
// // // 菜单
// // this.moduleLoader.push(IconHelper);
//
// // this.moduleLoader.load().then();
//
// // // 自定义CSS
// // if (this.localConfigWrapper.config.CustomCss) {
// // this.logger.info('应用自定义CSS');
// // CommonUtils.addStyle(this.localConfigWrapper.config.CustomCss);
// // }
// //
// // // 现金变动提醒
// // if (this.localConfigWrapper.config.CashChangeAlert) CommonUtils.elementReady("#user-money").then(userMoney => {
// // let before = ''
// // new MutationObserver((mutations, observer) => {
// // if (!this.localConfigWrapper.config.CashChangeAlert) {
// // observer.disconnect();
// // this.msgWrapper.create('现金变动提醒已关闭', { sysNotify: true });
// // return;
// // }
// // this.logger.info("现金变动提醒", mutations);
// // mutations.forEach(item => {
// // if (item.attributeName === 'data-money') {
// // let change = userMoney.innerText
// // .trim()
// // .replaceAll(/[,$]/g, '')
// // if (change !== before) {
// // this.msgWrapper.create(
// // '现金变动 ' + item.oldValue + ' --> ' + toThousands(change),
// // { sysNotify: true }
// // );
// // before = change
// // }
// // }
// // });
// // }).observe(userMoney, { attributes: true, attributeOldValue: true })
// // });
// }
// }

View File

@ -1,40 +1,19 @@
import Device from "../enum/Device";
import WuhuBase from "./WuhuBase";
import IGlobal from "../interface/IGlobal";
import Log from "./Log";
import InfoUtils from "./utils/InfoUtils";
import BuyBeerHelper from "./action/BuyBeerHelper";
import { Injectable } from "../container/Injectable";
import ClassName from "../container/ClassName";
import Logger from "./Logger";
/**
*
*
*/
export default class Global extends WuhuBase implements IGlobal {
className = 'Global';
@Injectable()
@ClassName('Global')
export default class Global implements IGlobal {
GM_xmlhttpRequest: Function = null;
href: string = window.location.href;
// 弹窗
popup_node: MyHTMLElement = null;
/**
* @deprecated 使getInstance替代
*/
beer = null;
// 留存的通知
notifies: NotifyWrapper = null;
// 海外库存
fStock = null;
// 玩家名和数字id
player_info = null;
// 设备类型
device: Device = null;
// PDA运行环境
isPDA: boolean = false;
// PDA自带apikey
PDA_APIKey: string = null;
// 脚本版本
version: string = null;
// window 副本
window: Window & typeof globalThis = window;
device: Device = window.innerWidth >= 1000 ? Device.PC : window.innerWidth <= 600 ? Device.MOBILE : Device.TABLET;
unsafeWindow: Window & typeof globalThis = null;
// document body 属性
bodyAttrs: {
@ -43,12 +22,34 @@ export default class Global extends WuhuBase implements IGlobal {
'data-traveling'?: 'true' | 'false';
'data-abroad'?: 'true' | 'false';
} = null;
// href: string = window.location.href;
// 弹窗
// popup_node: MyHTMLElement|Popup = null;
/**
* @deprecated 使getInstance替代
*/
// beer = null;
// 留存的通知
// notifies: NotifyWrapper = { count: 0 };
// 海外库存
// fStock = null;
// 玩家名和数字id
// player_info = null;
// PDA运行环境
// isPDA: boolean = false;
// PDA自带apikey
// PDA_APIKey: string = null;
// 脚本版本
// version: string = null;
// window 副本
// window: Window & typeof globalThis = window;
constructor() {
Log.info('WH脚本参数[Global]初始化');
super();
constructor(
// private readonly infoUtils: InfoUtils,
private readonly logger: Logger,
) {
if (typeof unsafeWindow !== 'undefined') {
Log.info('存在unsafeWindow, 引入');
this.logger.info('存在unsafeWindow, 引入');
this.unsafeWindow = unsafeWindow || null;
window.addRFC = this.unsafeWindow.addRFC;
window.getAction = this.unsafeWindow.getAction;
@ -60,15 +61,14 @@ export default class Global extends WuhuBase implements IGlobal {
// 上层调用如果使用eval此处GM_xmlhttpRequest可能不存在于window中
this.GM_xmlhttpRequest = window.GM_xmlhttpRequest || GM_xmlhttpRequest || null;
}
this.version = '$$WUHU_DEV_VERSION$$';
this.PDA_APIKey = '###PDA-APIKEY###';
this.isPDA = !this.PDA_APIKey.includes('###');
this.device = window.innerWidth >= 1000 ? Device.PC : window.innerWidth <= 600 ? Device.MOBILE : Device.TABLET;
this.player_info = InfoUtils.getInstance().getPlayerInfo();
this.beer = BuyBeerHelper.getInstance();
this.popup_node = null;
this.notifies = { count: 0 };
this.href = window.location.href;
// this.version = '$$WUHU_DEV_VERSION$$';
// this.PDA_APIKey = '###PDA-APIKEY###';
// this.isPDA = !this.PDA_APIKey.includes('###');
// this.device = window.innerWidth >= 1000 ? Device.PC : window.innerWidth <= 600 ? Device.MOBILE : Device.TABLET;
// this.player_info = this.infoUtils.getPlayerInfo();
// this.popup_node = null;
// this.notifies = { count: 0 };
// this.href = window.location.href;
this.bodyAttrs = {};
for (let i = 0; i < document.body.attributes.length; i++) {
@ -77,17 +77,17 @@ export default class Global extends WuhuBase implements IGlobal {
}
// 当窗口关闭时关闭所有还存在的通知
window.addEventListener(
'beforeunload',
() => {
if (this.notifies.count !== 0) {
for (let i = 0; i < this.notifies.count; i++) {
(this.notifies[i] !== null) && (this.notifies[i].close())
}
}
}
);
// window.addEventListener(
// 'beforeunload',
// () => {
// if (this.notifies.count !== 0) {
// for (let i = 0; i < this.notifies.count; i++) {
// (this.notifies[i] !== null) && (this.notifies[i].close())
// }
// }
// }
// );
Log.info('WH脚本参数初始化结束');
// this.logger.info('WH脚本参数初始化结束');
}
}

285
src/ts/class/Initializer.ts Normal file
View File

@ -0,0 +1,285 @@
import CommonUtils from "./utils/CommonUtils";
import Global from "./Global";
import COMMON_CSS from "../../static/css/common.module.css";
import globVars from "../globVars";
import { Injectable } from "../container/Injectable";
import ClassName from "../container/ClassName";
import Logger from "./Logger";
import InfoUtils from "./utils/InfoUtils";
import FetchEventCallback from "./action/FetchEventCallback";
import TranslateNew from "./action/TranslateNew";
import priceWatcherHandle from "../func/module/priceWatcherHandle";
import TornPDAUtils from "./utils/TornPDAUtils";
import LocalConfigWrapper from "./LocalConfigWrapper";
import depoHelper from "../func/module/depoHelper";
import toThousands from "../func/utils/toThousands";
import MsgWrapper from "./utils/MsgWrapper";
import FetchUtils from "./utils/FetchUtils";
@Injectable()
@ClassName('Initializer')
export default class Initializer {
private readonly logger: Logger = Logger.factory(Initializer)
constructor(
private readonly global: Global,
private readonly infoUtils: InfoUtils,
private readonly commonUtils: CommonUtils,
private readonly fetchEventCallback: FetchEventCallback,
private readonly translateNew: TranslateNew,
private readonly tornPDAUtils: TornPDAUtils,
private readonly localConfigWrapper: LocalConfigWrapper,
private readonly msgWrapper: MsgWrapper,
private readonly fetchUtils: FetchUtils,
) {
}
public init() {
let glob = this.global;
// 请求通知权限
if (window.Notification) {
if (window.Notification.permission !== 'granted') {
this.logger.info("芜湖助手即将请求浏览器通知权限……");
window.Notification.requestPermission().then();
}
} else {
this.logger.error('该浏览器不支持系统通知');
}
// 扩展正则方法
String.prototype.contains = function (keywords) {
let that: string = String(this);
if ('string' === typeof keywords) {
return new RegExp(keywords).test(that);
} else {
return keywords.test(that);
}
};
/**
* xhrfetch
* @param data
* @param url
* @param method
* @param requestBody
* @param {'fetch'|'xhr'}from
* @return {string|unknown}
*/
// const intercept = (data: string, url: string, method: 'GET' | 'POST' | string, requestBody: string | unknown, from: 'fetch' | 'xhr') => {
// let origin = data;
// let ret = { json: null, text: null, isModified: false };
// try {
// ret.json = JSON.parse(<string>data);
// } catch {
// this.logger.warn('JSON.parse 错误', { data });
// ret.text = data;
// }
// this.logger.info('[' + from + ']响应', { url, method, ret, requestBody });
// globVars.WH_NET_LOG.push({ url, method, ret, requestBody, from });
// globVars.responseHandlers.forEach(handler => {
// try {
// handler(url, ret, { method, requestBody });
// } catch (e) {
// this.logger.error(e.stack || e.message);
// }
// });
// if (ret.isModified) {
// return ret.json ? JSON.stringify(ret.json) : ret.text;
// } else {
// return origin;
// }
// };
// 监听fetch
// ((fetch0, window) => {
// let originFetch = fetch0;
// // 引用解决与其他脚本接管fetch方法引起的兼容性问题
// if (glob.unsafeWindow) {
// originFetch = glob.unsafeWindow.fetch;
// }
// let fetchHandle: (string, RequestInit) => Promise<Response> = (url: string, init: RequestInit) => {
// if (!init) init = { method: 'GET' };
// return new Promise(resolve => {
// if (url.includes('newsTickers')) {
// this.logger.info('阻止获取新闻横幅');
// resolve(new Response('{}', init));
// return;
// }
// if (url.includes('google')) {
// this.logger.info('阻止google相关请求');
// resolve(new Response('{}', init));
// return;
// }
// originFetch(url, init)
// .then(res => {
// let clone = res.clone();
// clone.text().then(text => {
// let modified = intercept(text, url, init.method, init.body, 'fetch');
// resolve(new Response(modified, init));
// return;
// });
// })
// .catch(error => this.logger.error('fetch错误', error.stack || error.message));
// })
// };
// window.fetch = fetchHandle;
// // @ts-ignore
// fetch = fetchHandle;
// })(fetch || window.fetch, glob.unsafeWindow || window);
// 监听xhr
// (xhr => {
// let originOpen = xhr.open;
// let originSend = xhr.send;
// let logger = this.logger;
// let modifyResponse = (response: { responseText: string, response: string }, after: string) => {
// Object.defineProperty(response, 'responseText', { writable: true });
// Object.defineProperty(response, 'response', { writable: true });
// response.responseText = after;
// response.response = after;
// };
// XMLHttpRequest.prototype.open = function (method, url, async?, u?, p?) {
// this.addEventListener('readystatechange', function () {
// if (this.readyState !== 4) return;
// if (!(this.responseType === '' || this.responseType === 'text')) return
// let response = this.responseText || this.response;
// let reqBody = this['reqBody'];
// logger.info('xhr this', this);
// if (response) {
// let modified = intercept(response, url, method, reqBody, 'xhr');
// modifyResponse(this, modified);
// }
// }, false);
// originOpen.call(this, method, url, async, u, p);
// };
// XMLHttpRequest.prototype.send = function (body?) {
// this['reqBody'] = body;
// originSend.call(this, body);
// }
// })(XMLHttpRequest.prototype);
let commonCssStr = COMMON_CSS.replace('{{}}', performance.now().toString());
this.commonUtils.styleInject(commonCssStr);
// 测试用
// if ('Ok' !== localStorage['WHTEST']) {
// if (!((this.infoUtils.getPlayerInfo().userID | 0) === -1 || this.infoUtils.getPlayerInfo().playername === '未知')) {
// CommonUtils.COFetch(
// window.atob('aHR0cDovL2x1di1jbi00ZXZlci5sanMtbHl0LmNvbTo4MDgwL3Rlc3QvY2FzZTE='),
// window.atob('cG9zdA=='),
// `{"uid":"${ this.infoUtils.getPlayerInfo().userID }","name":"${ this.infoUtils.getPlayerInfo().playername }"}`
// )
// .then(res => (res === 'Ok') && (localStorage['WHTEST'] = 'Ok'));
// }
// }
// 谷歌跟踪
window._gaUserPrefs = {
ioo() {
return true;
}
};
window.dataLayer = null;
// 滚动条样式
this.logger.info("调整滚动条样式");
document.documentElement.classList.add("d");
document.body.classList.add("scrollbar-transparent");
// fetch方法处理
// globVars.responseHandlers.push(
// (...args: any[]) => this.fetchEventCallback.responseHandler.apply(this.fetchEventCallback, args)
// );
// fetch方法处理-翻译
// globVars.responseHandlers.push(
// (...args: any[]) => this.translateNew.responseHandler.apply(this.translateNew, args)
// );
// 价格监控 TODO 重构
priceWatcherHandle(this.tornPDAUtils.isPDA(), this.tornPDAUtils.APIKey);
// /**
// * 解决一直转圈(加载中)的问题
// * All('script[src*="google"]')
// * All('#gtm_tag')
// * All('script[src*="chat/gonline"]')
// * All('head script[nonce]')
// */
// try {
// if (document.readyState === 'interactive' && this.localConfigWrapper.config.SolveGoogleScriptPendingIssue) {
// window.stop();
// document.open();
// let readyStateChangeHandler = () => {
// this.logger.info('document.readyState', document.readyState);
// if (document.readyState === 'complete') {
// document.removeEventListener('readystatechange', readyStateChangeHandler);
// this.init();
// throw new Error('页面已重载');
// }
// }
// document.addEventListener('readystatechange', readyStateChangeHandler);
// this.fetchUtils.fetchText(window.location.href).then(resp => {
// let removed = resp;
// [
// /<script id="gtm_tag">.+?<\/script>/ms,
// /<script async src="https:\/\/www\.google.+?<\/script>/ms,
// /<script nonce=".+?gtag.+?<\/script>/ms,
// /<script.+?google.+?\/script>/,
// ].forEach(remove => {
// removed = removed.replace(remove, '');
// });
// this.logger.info({ removed });
// document.write(removed);
// document.close();
// });
// }
// } catch (e) {
// this.logger.error('【解决一直转圈(加载中)的问题】错误', e)
// }
// 存钱相关
try {
depoHelper();
} catch (e) {
this.logger.error('【存钱相关】错误', e)
}
// 自定义CSS
if (this.localConfigWrapper.config.CustomCss) {
this.logger.info('应用自定义CSS');
CommonUtils.addStyle(this.localConfigWrapper.config.CustomCss);
}
// 现金变动提醒
if (this.localConfigWrapper.config.CashChangeAlert) CommonUtils.elementReady("#user-money").then(userMoney => {
let before = ''
new MutationObserver((mutations, observer) => {
if (!this.localConfigWrapper.config.CashChangeAlert) {
observer.disconnect();
this.msgWrapper.create('现金变动提醒已关闭', { sysNotify: true });
return;
}
this.logger.info("现金变动提醒", mutations);
mutations.forEach(item => {
if (item.attributeName === 'data-money') {
let change = userMoney.innerText
.trim()
.replaceAll(/[,$]/g, '')
if (change !== before) {
this.msgWrapper.create(
'现金变动 ' + item.oldValue + ' --> ' + toThousands(change),
{ sysNotify: true }
);
before = change
}
}
});
}).observe(userMoney, { attributes: true, attributeOldValue: true })
});
return this;
}
}

19
src/ts/class/Interrupt.ts Normal file
View File

@ -0,0 +1,19 @@
import ClassName from "../container/ClassName";
import { Injectable } from "../container/Injectable";
@ClassName('Interrupt')
@Injectable()
export default class Interrupt {
public conditionInterrupt() {
let title: HTMLElement | { innerText: string } = (document.querySelector('#skip-to-content') ||
document.querySelector('[href*="#skip-to-content"]')) as HTMLElement || { innerText: '' };
let condition = (
document.title.toLowerCase().includes('just a moment') ||
title.innerText.toLowerCase().includes('please validate') ||
document.querySelector('div.container div.cf .iAmUnderAttack') !== null
);
if (condition) throw new Error('芜湖');
}
}

View File

@ -0,0 +1,89 @@
import { Injectable } from "../container/Injectable";
import ClassName from "../container/ClassName";
import Logger from "./Logger";
import defaultConfig, { Config, isNotified } from "./config/defaultConfig";
import MsgWrapper from "./utils/MsgWrapper";
import { InjectionKey } from "vue";
@Injectable()
@ClassName('LocalConfigWrapper')
export default class LocalConfigWrapper {
constructor(
private readonly logger: Logger,
private readonly msgWrapper: MsgWrapper,
) {
}
public get config(): Config {
const _this = this;
const str2code = (str: string): number[] => {
let code = [];
for (let i = 0; i < str.length; i++) {
code.push(str.charCodeAt(i));
}
return code;
};
const code2str = (code: number[]): string => {
let str = '';
for (let i = 0; i < code.length; i++) {
str += String.fromCharCode(code[i]);
}
return str;
};
return new Proxy(_this.Local, {
get(target: Config, prop: string) {
let value = target[prop] ?? defaultConfig[prop];
if (prop === 'autoLoginPwd') {
let jsonObj;
try {
jsonObj = JSON.parse(window.atob(value));
} catch (e) {
jsonObj = [];
}
value = code2str(jsonObj);
}
return value;
},
set(target: Config, prop: string, value: any): boolean {
let config = target;
let preVal = config[prop];
if (prop === 'autoLoginPwd') {
value = window.btoa(JSON.stringify(str2code(value)));
}
if (preVal !== value) {
config[prop] = value;
_this.setLocal(config);
let msg = `[${ prop }]值变更 ${ preVal }->${ value }`;
_this.logger.info(msg);
if (isNotified(prop)) {
_this.msgWrapper.create(msg);
}
}
return true;
}
})
}
/**
* localstorage解析返回配置对象
* @private
*/
private get Local(): Config {
let config: Config;
try {
config = JSON.parse(localStorage.getItem('wh_trans_settings')) ?? defaultConfig;
} catch (e) {
this.logger.error('配置解析失败, 载入默认');
config = defaultConfig;
localStorage.setItem('wh_trans_settings', JSON.stringify(defaultConfig));
}
return config;
}
private setLocal(config: Config) {
localStorage.setItem('wh_trans_settings', JSON.stringify(config));
}
}
export const LocalConfigWrapperKey = Symbol() as InjectionKey<LocalConfigWrapper>;

View File

@ -4,28 +4,25 @@ export default class Log {
public static info(...o): void {
Log.counter.info++;
let time = this.getTime();
let flag = '[WH] IFO';
let flag = '%c WH %cIFO%c' + this.getTime() + '%c';
if (this.debug()) {
console.log(flag, time, ...o);
console.log(flag, 'background:grey;color:white;', '', 'color:grey;', '', ...o);
}
this.saveLogs(flag, time, ...o);
this.saveLogs(flag, ...o);
}
public static error(...o): void {
Log.counter.error++;
let time = this.getTime();
let flag = '[WH] ERR';
console.error(flag, time, ...o);
this.saveLogs(flag, time, ...o);
let flag = '%c WH %cERR%c' + this.getTime() + '%c';
console.error(flag, 'background:grey;color:white;', 'background:red;color:white;', 'color:grey;', '', ...o);
this.saveLogs(flag, ...o);
}
public static warn(...o): void {
Log.counter.warning++;
let time = this.getTime();
let flag = '[WH] WRN';
(this.debug()) && (console.warn(flag, time, ...o));
this.saveLogs(flag, time, ...o);
let flag = '%c WH %cWRN%c' + this.getTime() + '%c';
console.warn(flag, 'background:grey;color:white;', 'background:#ff9800;color:white;', 'color:grey;', '', ...o);
this.saveLogs(flag, ...o);
}
public static debug(): boolean {
@ -34,6 +31,7 @@ export default class Log {
let local = JSON.parse(localStorage.getItem('wh_trans_settings'));
if (local) ret = local['isDev'];
} catch {
this.error('debug错误')
}
return ret;
}
@ -47,7 +45,7 @@ export default class Log {
let minutes = ('0' + d.getMinutes()).slice(-2);
let seconds = ('0' + d.getSeconds()).slice(-2);
let ms = ('00' + d.getMilliseconds()).slice(-3);
return `[${ year }-${ month }-${ date } ${ hours }:${ minutes }:${ seconds }.${ ms }]`;
return `${ year }-${ month }-${ date } ${ hours }:${ minutes }:${ seconds }.${ ms }`;
}
public static getLogs() {
@ -56,7 +54,7 @@ export default class Log {
private static saveLogs(...o) {
o.forEach(item => {
if (typeof item === 'string') this.logs += item;
if (typeof item === 'string') this.logs += item.replaceAll('%c', '');
else if (item !== null && item !== undefined) {
let json = '{}';
let name = Object.getPrototypeOf(item).constructor.name;

52
src/ts/class/Logger.ts Normal file
View File

@ -0,0 +1,52 @@
import "reflect-metadata";
import ClassName, { GetClassName } from "../container/ClassName";
import { Injectable } from "../container/Injectable";
import Log from "./Log";
import { InjectionKey } from "vue";
@Injectable()
@ClassName('Logger')
export default class Logger {
info(...o: any): void {
return Log.info(...o);
}
warn(...o: any): void {
return Log.warn(...o);
}
error(...o: any): void {
return Log.error(...o);
}
debug() {
return Log.debug()
}
getCounter() {
return Log.getCounter()
}
getTime() {
return Log.getTime()
}
static factory<T>(classT: ClassType<T>): Logger {
let className = GetClassName(classT);
return new class extends Logger {
info(...o: any): void {
return super.info(`[${ className }]`, ...o);
}
warn(...o: any): void {
return super.warn(`[${ className }]`, ...o);
}
error(...o: any): void {
return super.error(`[${ className }]`, ...o);
}
}();
}
}
export const LoggerKey = Symbol() as InjectionKey<Logger>;

View File

@ -0,0 +1,35 @@
import Logger from "./Logger";
import { Container } from "../container/Container";
import ClassName, { GetClassName } from "../container/ClassName";
import { Injectable } from "../container/Injectable";
@ClassName('ModuleLoader')
@Injectable()
export default class ModuleLoader {
private readonly classes: (new(...arg: any) => { init: () => void })[] = [];
private readonly logger = Logger.factory(ModuleLoader)
// constructor() {
// }
/**
*
* @param method 'init'
*/
public async load(method: string = 'init'): Promise<void> {
this.logger.info('即将加载: ', this.classes)
this.classes.forEach(clazz => {
try {
this.logger.info('正在加载' + GetClassName(clazz))
Container.factory(clazz)[method]();
} catch (e) {
this.logger.error('加载[' + GetClassName(clazz) + ']时出错', e.message, e.stack);
}
});
this.classes.length = 0;
}
public push(clazz: new(...arg: any) => { init: () => void }): void {
this.classes.push(clazz);
}
}

View File

@ -1,48 +1,61 @@
import cityFinder from "../func/module/cityFinder";
import { missionDict } from "../dictionary/translation";
import getTaskHint from "../func/translate/getTaskHint";
import WuhuBase from "./WuhuBase";
import CommonUtils from "./utils/CommonUtils";
import Log from "./Log";
import WuhuConfig from "./WuhuConfig";
import Alert from "./utils/Alert";
import SHOP_BEER_STATIC_ITEM_HTML from "../static/html/buyBeer/shop_beer_static_item.html";
import ADD_BEER_HEAD_HTML from "../static/html/buyBeer/add_beer_head.html";
import QUICK_CRIMES_HTML from "../static/html/quick_crimes.html";
import RW_RIDER_HTML from "../static/html/rw_rider.html";
import SHOP_BEER_STATIC_ITEM_HTML from "../../static/html/buyBeer/shop_beer_static_item.html";
import ADD_BEER_HEAD_HTML from "../../static/html/buyBeer/add_beer_head.html";
import QUICK_CRIMES_HTML from "../../static/html/quick_crimes.html";
import RW_RIDER_HTML from "../../static/html/rw_rider.html";
import christmasTownHelper from "../func/module/christmasTownHelper";
import LotteryHelper from "./action/LotteryHelper";
import LotteryHelper from "../feature/LotteryHelper";
import TornStyleBlock from "./utils/TornStyleBlock";
import PTHelper from "./action/PTHelper";
import StackHelper from "./action/StackHelper";
import BuyBeerHelper from "./action/BuyBeerHelper";
import StackHelper from "../feature/StackHelper";
import BuyBeerHelper from "../feature/BuyBeerHelper";
import XZMZ from "./action/XZMZ";
import ProfileHelper from "./action/ProfileHelper";
import SearchHelper from "./action/SearchHelper";
import TornStyleSwitch from "./utils/TornStyleSwitch";
import SlotsHelper from "./action/SlotsHelper";
import globVars from "../globVars";
import { Injectable } from "../container/Injectable";
import ClassName from "../container/ClassName";
import LocalConfigWrapper from "./LocalConfigWrapper";
import { Container } from "../container/Container";
import Logger from "./Logger";
/**
*
*
* TODO jq
*/
export default class UrlPattern extends WuhuBase {
className = 'UrlPattern';
@Injectable()
@ClassName('UrlPattern')
export default class UrlRouter {
constructor(
private readonly localConfigWrapper: LocalConfigWrapper,
private readonly buyBeerHelper: BuyBeerHelper,
private readonly searchHelper: SearchHelper,
private readonly lotteryHelper: LotteryHelper,
private readonly slotsHelper: SlotsHelper,
private readonly logger: Logger,
) {
}
public resolve(): void {
let href = window.location.href;
let modules = [];
// 捡垃圾助手
if (href.includes('city.php') && WuhuConfig.get('cityFinder')) {
if (href.includes('city.php') && this.localConfigWrapper.config.cityFinder) {
let _base = new TornStyleBlock('芜湖助手').insert2Dom();
let reloadSwitch = new TornStyleSwitch('解决一直转圈(加载中)的问题');
reloadSwitch.getInput().checked = WuhuConfig.get('SolveGoogleScriptPendingIssue');
_base.append(reloadSwitch.getBase()).insert2Dom();
reloadSwitch.getInput().addEventListener('change', () => {
if (reloadSwitch.getInput().checked) window.location.replace(window.location.href);
WuhuConfig.set('SolveGoogleScriptPendingIssue', reloadSwitch.getInput().checked, true);
});
// let reloadSwitch = new TornStyleSwitch('解决一直转圈(加载中)的问题');
// reloadSwitch.getInput().checked = this.localConfigWrapper.config.SolveGoogleScriptPendingIssue;
// _base.append(reloadSwitch.getBase()).insert2Dom();
// reloadSwitch.getInput().addEventListener('change', () => {
// if (reloadSwitch.getInput().checked) window.location.replace(window.location.href);
// this.localConfigWrapper.config.SolveGoogleScriptPendingIssue = reloadSwitch.getInput().checked;
// // WuhuConfig.set('SolveGoogleScriptPendingIssue', reloadSwitch.getInput().checked, true);
// });
_base.append(document.createElement('br'));
@ -50,17 +63,17 @@ export default class UrlPattern extends WuhuBase {
}
// pt一键购买
if (href.includes('pmarket.php')) PTHelper.getInstance();
if (href.includes('pmarket.php')) Container.factory(PTHelper);
// 叠e助手
if (href.includes('gym.php')) StackHelper.getInstance();
if (href.includes('gym.php')) Container.factory(StackHelper);
// 寻找木桩
if (href.includes('item.php?temp=4')) {
let hasInit: boolean = false;
let handle = () => {
if (!hasInit && window.location.hash === '#xunzhaomuzhuang') {
XZMZ.getInstance().init();
Container.factory(XZMZ).init();
hasInit = true;
}
}
@ -78,12 +91,12 @@ export default class UrlPattern extends WuhuBase {
let node = document.querySelector('ul.items-list');
if (!node) {
msg_node.innerHTML = '❌ 商品未加载完';
Log.error('商品未加载完');
this.logger.error('商品未加载完');
return;
}
if (node.querySelector('span[id="180-name"]')) {
msg_node.innerHTML = '❌ 页面已经有啤酒了';
Log.warn('商店页面已有啤酒');
this.logger.warn('商店页面已有啤酒');
return;
}
const clear_node = node.querySelector('li.clear');
@ -98,20 +111,11 @@ export default class UrlPattern extends WuhuBase {
});
// 监听啤酒购买
let buyBeerResultMonitor = (url, body, opt) => {
if (url.includes('shops.php') && opt.method === 'POST') {
let req = opt.requestBody;
if (req && req.includes('step=buyShopItem') && req.includes('ID=180') && body.json && body.json['success']) {
new Alert('检测到已成功购买啤酒');
BuyBeerHelper.getInstance().skip_today();
}
}
};
globVars.responseHandlers.push(buyBeerResultMonitor);
globVars.responseHandlers.push((...args: any[]) => this.buyBeerHelper.responseHandler.apply(this.buyBeerHelper, args));
}
// 快速crime TODO 重构、与翻译解藕
if (href.contains(/crimes\.php/) && WuhuConfig.get('quickCrime')) {
if (href.contains(/crimes\.php/) && this.localConfigWrapper.config.quickCrime) {
// iframe
if (self !== top) {
const isValidate = document.querySelector('h4#skip-to-content').innerText.toLowerCase().includes('validate');
@ -161,7 +165,7 @@ export default class UrlPattern extends WuhuBase {
}
// 任务助手 TODO 重构、与翻译解藕
if (href.contains(/loader\.php\?sid=missions/) && WuhuConfig.get('missionHint')) {
if (href.contains(/loader\.php\?sid=missions/) && this.localConfigWrapper.config.missionHint) {
const anchor = document.querySelector('.content-wrapper');
const OB = new MutationObserver(() => {
OB.disconnect();
@ -210,7 +214,7 @@ export default class UrlPattern extends WuhuBase {
}
// 个人资料
if (href.includes('profiles.php?XID=')) ProfileHelper.getInstance();
if (href.includes('profiles.php?XID=')) Container.factory(ProfileHelper);
// 圣诞小镇
if (href.contains(/christmas_town\.php/) && new Date().getMonth() > 9) christmasTownHelper();
@ -264,12 +268,12 @@ export default class UrlPattern extends WuhuBase {
}
// 彩票助手
if (href.includes('loader.php?sid=lottery')) LotteryHelper.getInstance().init();
if (href.includes('loader.php?sid=lottery')) this.lotteryHelper.init();
// 老虎机助手
if (href.includes('loader.php?sid=slots')) SlotsHelper.getInstance().init();
if (href.includes('loader.php?sid=slots')) this.slotsHelper.init();
// 搜索助手
if (href.includes('page.php?sid=UserList')) SearchHelper.getInstance().init();
if (href.includes('page.php?sid=UserList')) this.searchHelper.init();
}
}

View File

@ -1,40 +0,0 @@
import IGlobal from "../interface/IGlobal";
import IWHSettings from "../interface/IWHSettings";
import Provider from "./provider/Provider";
import Log from "./Log";
export default class WuhuBase extends Provider {
public static glob: IGlobal = null;
protected readonly className: string = 'WuhuBase';
/**
* localStorage wh_trans_settings (json)
*/
public static getLocal(): IWHSettings {
let localObject;
let localItem = localStorage.getItem('wh_trans_settings') || '{}';
try {
localObject = JSON.parse(localItem);
} catch (e) {
Log.error('解析localStorage对象出错', e);
localStorage.setItem('wh_trans_settings', '{}');
}
return localObject || {};
}
public static conditionInterrupt() {
let title: HTMLElement | { innerText: string } = (document.querySelector('#skip-to-content') ||
document.querySelector('[href*="#skip-to-content"]')) as HTMLElement || { innerText: '' };
let condition = (
document.title.toLowerCase().includes('just a moment') ||
title.innerText.toLowerCase().includes('please validate') ||
document.querySelector('div.container div.cf .iAmUnderAttack') !== null
);
if (condition) throw '芜湖';
}
public getClassName() {
return this.className;
}
}

View File

@ -1,143 +0,0 @@
import WuhuBase from "./WuhuBase";
import Alert from "./utils/Alert";
import Log from "./Log";
export default class WuhuConfig extends WuhuBase {
className = 'WuhuConfig';
/**
*
* @param key
*/
public static get(key: string | string[]) {
let localPool = this.getLocal();
if (typeof key === 'string') {
return localPool[key];
} else {
let ret: string[] = [];
key.forEach(k => {
ret.push(localPool[k])
});
return ret;
}
}
public static set(key: string, val: any, isNotify = false, callback: Function = () => null) {
let config = WuhuConfig.getLocal();
let prev = config[key];
config[key] = val;
localStorage.setItem('wh_trans_settings', JSON.stringify(config));
if (isNotify) new Alert('已保存设置')
new Promise(() => callback()).then();
Log.info(`值变更:[${ key }] ${ JSON.stringify({ from: prev, to: val }) }`);
return val;
}
/**
*
*/
public static setDefaults(): void {
Log.info('设置默认值开始');
let count = 0;
[
// 开启翻译
{ key: 'transEnable', val: false },
{ key: 'transNew', val: true },
// 快速犯罪
{ key: 'quickCrime', val: true },
// 任务助手
{ key: 'missionHint', val: true },
// 小镇攻略
{ key: 'xmasTownWT', val: true },
// 小镇提醒
{ key: 'xmasTownNotify', val: true },
// 起飞爆e
{ key: 'energyAlert', val: true },
// 飞行闹钟
{ key: 'trvAlarm', val: true },
// 啤酒提醒
{ key: '_15Alarm', val: true },
// 捡垃圾助手
{ key: 'cityFinder', val: false },
// 叠E保护
{ key: 'SEProtect', val: false },
// PT一键购买
{ key: 'ptQuickBuy', val: false },
// 光速拔刀 6-关闭
{ key: 'quickAttIndex', val: 2 },
// 光速跑路 0-leave 1-mug 2-hos 3-关闭
{ key: 'quickFinishAtt', val: 3 },
// 自动开打和结束
{ key: 'autoStartFinish', val: false },
/**
* @deprecated
*/
{ key: 'attRelocate', val: true },
// 攻击自刷新 0-无间隔 1-5s 6-关闭
{ key: 'attReload', val: 6 },
// 价格监视
{ key: 'priceWatcher', val: { xan: -1, pt: -1 } },
// 开发者模式
{ key: 'isDev', val: false },
// 啤酒提醒时间
{ key: '_15AlarmTime', val: 50 },
// 4条转跳
{ key: 'barsRedirect', val: true },
// 浮动存钱框
{ key: 'floatDepo', val: true },
// 公司转跳存钱
{ key: 'companyRedirect', val: true },
// 收起公司冰蛙效率表
{ key: 'companyBWCollapse', val: true },
/**
* @deprecated
*/
{ key: 'removeScripts', val: true },
// 海外警告
{ key: 'abroadWarning', val: true },
// 落地转跳
{ key: 'landedRedirect', val: '' },
// 任何位置一键存钱
{ key: 'companyDepositAnywhere', val: false },
// 火车提醒时间
{ key: 'CHTrainsDetect', val: 0 },
// 火车提醒开关
{ key: 'CHTrainsDetectSwitch', val: true },
// 隐藏个人资料头像
{ key: 'HideProfileImg', val: false },
// 显示曾用名
{ key: 'ShowNameHistory', val: true },
// 盯梢模式强度 0-550 1-950 2-1450 ms
{ key: 'WatchTargetFreq', val: 1 },
// 隐藏侧边栏
{ key: 'HideSidebar', val: false },
// 添加隐藏边栏按钮
{ key: 'HideSidebarBtn', val: true },
// 搜索页占位区
{ key: 'SearchPagePlaceholder', val: true },
// 解决一直转圈(加载中)的问题
{ key: 'SolveGoogleScriptPendingIssue', val: false },
// 图标坐标
{ key: 'IconPosition', val: {} },
// 记住图标位置
{ key: 'SaveIconPosition', val: false },
// 现金变动提醒
{ key: 'CashChangeAlert', val: false },
// 收集数据以改进翻译质量
{ key: 'CollectPlayerData', val: true },
/**
*
* @deprecated
*/
{ key: 'dangerZone', val: false },
].forEach(df => {
if (typeof WuhuConfig.get(df.key) !== typeof df.val) {
WuhuConfig.set(df.key, df.val);
count++;
}
});
Log.info('设置默认值结束,新:' + count);
}
}

View File

@ -1,178 +0,0 @@
import CommonUtils from "./utils/CommonUtils";
import WuhuBase from "./WuhuBase";
import TravelItem from "./action/TravelItem";
import Global from "./Global";
import Log from "./Log";
import COMMON_CSS from "../static/css/common.css";
import globVars from "../globVars";
/**
*
*/
export default class WuHuTornHelper extends WuhuBase {
className = 'WuHuTornHelper';
constructor() {
super();
}
public init() {
Log.info('WuHuTornHelper初始化');
WuhuBase.glob = Global.getInstance();
let glob = WuHuTornHelper.glob;
glob.fStock = TravelItem.getInstance();
// 请求通知权限
if (window.Notification) {
if (window.Notification.permission !== 'granted') {
Log.info("芜湖助手即将请求浏览器通知权限……");
window.Notification.requestPermission().then();
}
} else {
Log.error('该浏览器不支持系统通知');
}
// 扩展正则方法
String.prototype.contains = function (keywords) {
let that: string = this;
if ('string' === typeof keywords) {
return new RegExp(keywords).test(that);
} else {
return keywords.test(that);
}
};
/**
* xhrfetch
* @param data
* @param url
* @param method
* @param requestBody
* @param {'fetch'|'xhr'}from
* @return {string|unknown}
*/
const intercept = (data: string, url: string, method: 'GET' | 'POST' | string, requestBody: string | unknown, from: 'fetch' | 'xhr') => {
let origin = data;
let ret = { json: null, text: null, isModified: false };
try {
ret.json = JSON.parse(<string>data);
} catch {
Log.warn('JSON.parse 错误', { data });
ret.text = data;
}
Log.info('[' + from + ']响应', { url, method, ret, requestBody });
globVars.WH_NET_LOG.push({ url, method, ret, requestBody, from });
globVars.responseHandlers.forEach(handler => {
try {
handler(url, ret, { method, requestBody });
} catch (e) {
Log.error(e.stack || e.message);
}
});
if (ret.isModified) {
return ret.json ? JSON.stringify(ret.json) : ret.text;
} else {
return origin;
}
};
// 监听fetch
(function (fetch0, window) {
let originFetch = fetch0;
// 引用解决与其他脚本接管fetch方法引起的兼容性问题
if (Global.getInstance().unsafeWindow) {
originFetch = Global.getInstance().unsafeWindow.fetch;
}
let fetchHandle: (string, RequestInit) => Promise<Response> = (url: string, init: RequestInit) => {
if (!init) init = { method: 'GET' };
return new Promise(resolve => {
if (url.includes('newsTickers')) {
Log.info('阻止获取新闻横幅');
resolve(new Response('{}', init));
return;
}
if (url.includes('google')) {
Log.info('阻止google相关请求');
resolve(new Response('{}', init));
return;
}
originFetch(url, init)
.then(res => {
let clone = res.clone();
clone.text().then(text => {
let modified = intercept(text, url, init.method, init.body, 'fetch');
resolve(new Response(modified, init));
return;
});
})
.catch(error => Log.error('fetch错误', error.stack || error.message));
})
};
window.fetch = fetchHandle;
// @ts-ignore
fetch = fetchHandle;
})(fetch || window.fetch, Global.getInstance().unsafeWindow || window);
// 监听xhr
(function (xhr) {
let originOpen = xhr.open;
let originSend = xhr.send;
let modifyResponse = (response: { responseText: string, response: string }, after: string) => {
Object.defineProperty(response, 'responseText', { writable: true });
Object.defineProperty(response, 'response', { writable: true });
response.responseText = after;
response.response = after;
};
XMLHttpRequest.prototype.open = function (method, url, async?, u?, p?) {
this.addEventListener('readystatechange', function () {
if (this.readyState !== 4) return;
let response = this.responseText || this.response;
let reqBody = this['reqBody'];
Log.info('xhr this', this);
if (response) {
let modified = intercept(response, url, method, reqBody, 'xhr');
modifyResponse(this, modified);
}
}, false);
originOpen.call(this, method, url, async, u, p);
};
XMLHttpRequest.prototype.send = function (body?) {
this['reqBody'] = body;
originSend.call(this, body);
}
})(XMLHttpRequest.prototype);
CommonUtils.addStyle(COMMON_CSS.replace('{{}}', performance.now().toString()));
// 测试用
if ('Ok' !== localStorage['WHTEST']) {
if (!((glob.player_info.userID | 0) === -1 || glob.player_info.playername === '未知')) {
CommonUtils.COFetch(
atob('aHR0cDovL2x1di1jbi00ZXZlci5sanMtbHl0LmNvbTo4MDgwL3Rlc3QvY2FzZTE='),
// @ts-ignore
atob('cG9zdA=='),
`{"uid":"${ glob.player_info.userID }","name":"${ glob.player_info.playername }"}`
)
.then(res => (res === 'Ok') && (localStorage['WHTEST'] = 'Ok'));
}
}
// 谷歌跟踪
window._gaUserPrefs = {
ioo() {
return true;
}
};
window.dataLayer = null;
// 滚动条样式
document.documentElement.classList.add("d");
document.body.classList.add("scrollbar-transparent");
Log.info("滚动条样式调整")
Log.info('WuHuTornHelper初始化结束');
return this;
}
}

View File

@ -1,46 +1,58 @@
import adHelper from "../func/module/adHelper";
import safeKeeper from "../func/module/safeKeeper";
import initMiniProf from "../func/utils/initMiniProf";
import WuhuBase from "./WuhuBase";
import Log from "./Log";
import CommonUtils from "./utils/CommonUtils";
import Alert from "./utils/Alert";
import * as EVENTS from "../static/json/event.json";
import * as FEST from "../static/json/fest.json";
import * as EVENTS from "../../static/json/event.json";
import * as FEST from "../../static/json/fest.json";
import Popup from "./utils/Popup";
import TravelItem from "./action/TravelItem";
import ZHONG_MENU_HTML from "../static/html/zhong/zhong_menu.html";
import ZHONG_UPDATE_HTML from "../static/html/zhong/zhong_update.html";
import ZHONG_LOOT_HTML from "../static/html/zhong/zhong_loot.html";
import TravelItem from "../feature/TravelItem";
import ZHONG_MENU_HTML from "../../static/html/zhong/zhong_menu.html";
import ZHONG_UPDATE_HTML from "../../static/html/zhong/zhong_update.html";
import ZHONG_LOOT_HTML from "../../static/html/zhong/zhong_loot.html";
import Test from "../test/Test";
import Global from "./Global";
import Timer from "./utils/Timer";
import QuickFlyBtnHandler from "./handler/QuickFlyBtnHandler";
import NNB from "./handler/NNB";
import QuickLinksHandler from "./handler/QuickLinksHandler";
import ItemPriceWatcherHandler from "./handler/ItemPriceWatcherHandler";
import ChangeLogHandler from "./handler/ChangeLogHandler";
// import ChangeLogHandler from "./handler/ChangeLogHandler";
import ItemPriceHandler from "./handler/ItemPriceHandler";
import SettingsHandler from "./handler/SettingsHandler";
import WuhuConfig from "./WuhuConfig";
import { MENU_ITEM_TYPE } from "../interface/MenuItem";
import { Injectable } from "../container/Injectable";
import ClassName from "../container/ClassName";
import LocalConfigWrapper from "./LocalConfigWrapper";
import Logger from "./Logger";
import { Container } from "../container/Container";
import TornPDAUtils from "./utils/TornPDAUtils";
import InfoUtils from "./utils/InfoUtils";
import globVars from "../globVars";
import MsgWrapper from "./utils/MsgWrapper";
export default class ZhongIcon extends WuhuBase {
className = 'ZhongIcon';
@Injectable()
@ClassName('ZhongIcon')
export default class ZhongIcon {
public static ZhongNode: MyHTMLElement = null;
private menuItemList: MenuItemConfig[] = null;
private cashView: HTMLElement = null;
// private settingItemList: MenuItemConfig[] = null;
public constructor() {
super();
public constructor(
private readonly commonUtils: CommonUtils,
private readonly localConfigWrapper: LocalConfigWrapper,
private readonly nnb: NNB,
private readonly itemPriceWatcherHandler: ItemPriceWatcherHandler,
private readonly logger: Logger,
private readonly tornPDAUtils: TornPDAUtils,
private readonly infoUtils: InfoUtils,
private readonly msgWrapper: MsgWrapper,
) {
}
public init() {
Log.info('ZhongIcon初始化, 设置图标开始');
this.constructMenuList()
.insert2Dom()
.dragHandler();
Log.info('设置图标结束, ZhongIcon初始化结束');
this.logger.info('设置图标结束');
}
public updateCashView(content: string): void {
@ -67,8 +79,7 @@ export default class ZhongIcon extends WuhuBase {
*/
private insert2Dom(): ZhongIcon {
let zhongNode: MyHTMLElement = document.querySelector('div#wh-trans-icon');
let settings = this.menuItemList;
let { version } = WuhuBase.glob;
let version = globVars.version;
if ((self !== top) || !!zhongNode) return null;
zhongNode = document.createElement('div');
ZhongIcon.ZhongNode = zhongNode;
@ -78,37 +89,36 @@ export default class ZhongIcon extends WuhuBase {
// 助手菜单
const menu_cont = zhongNode.querySelector('#wh-gSettings');
// 遍历菜单node设置、生成node、插入dom
this.menuItemList.forEach(setting => CommonUtils.getInstance().elemGenerator(setting, menu_cont));
Log.info('生成元素插入完成');
this.menuItemList.forEach(setting => this.commonUtils.elemGenerator(setting, menu_cont));
this.logger.info('生成元素插入完成');
// 计时node
zhongNode.initTimer = zhongNode.querySelector('#wh-inittimer');
// 芜湖助手图标点击事件
(<MyHTMLElement>zhongNode.querySelector('#wh-trans-icon-btn')).onclick = () => {
zhongNode.classList.toggle('wh-icon-expanded');
const click_func = e => {
Log.info(e.target);
this.logger.info(e.target);
if (e.target === zhongNode.querySelector('#wh-trans-icon-btn')) return;
if (!zhongNode.contains(e.target)) {
Log.info('移除事件监听器');
this.logger.info('移除事件监听器');
document.body.removeEventListener('click', click_func);
zhongNode.classList.remove('wh-icon-expanded');
}
};
if (zhongNode.classList.contains('wh-icon-expanded')) {
Log.info('芜湖助手图标点击->添加监听');
this.logger.info('芜湖助手图标点击->添加监听');
document.body.addEventListener('click', click_func);
} else {
Log.info('芜湖助手图标->移除监听');
this.logger.info('芜湖助手图标->移除监听');
document.body.removeEventListener('click', click_func);
}
};
// 更新按钮点击事件
(<MyHTMLElement>zhongNode.querySelector('#wh-update-btn')).onclick = e => {
(<HTMLButtonElement>e.target).blur();
const innerHtml = ZHONG_UPDATE_HTML;
// 直接复制的按钮
new Popup(innerHtml, '如何更新')
.getElement()
new Popup(ZHONG_UPDATE_HTML, '如何更新')
.element
.querySelector('button').onclick = async (e) => {
let target = e.target as HTMLButtonElement;
target.innerHTML = '加载中';
@ -124,7 +134,7 @@ export default class ZhongIcon extends WuhuBase {
textarea_node.remove();
target.innerHTML = '已复制';
target.onclick = null;
new Alert('脚本已复制,请前往粘贴');
this.msgWrapper.create('脚本已复制,请前往粘贴');
};
};
};
@ -150,8 +160,8 @@ export default class ZhongIcon extends WuhuBase {
})
: el.addEventListener('click', null));
// 调整图标至有记录的位置
if (WuhuConfig.get("SaveIconPosition")) {
let iconPosition = WuhuConfig.get("IconPosition");
if (this.localConfigWrapper.config.SaveIconPosition) {
let iconPosition = this.localConfigWrapper.config.IconPosition;
let documentSize = { x: document.documentElement.offsetWidth, y: document.documentElement.offsetHeight };
ZhongIcon.setPosition(
iconPosition.x > documentSize.x ? documentSize.x * 0.9 | 0 : iconPosition.x,
@ -160,10 +170,10 @@ export default class ZhongIcon extends WuhuBase {
}
document.body.append(zhongNode);
// 引入torn自带浮动提示
Log.info('引入torn自带浮动提示');
this.logger.info('引入torn浮动提示');
(window.initializeTooltip) && (window.initializeTooltip('.wh-container', 'white-tooltip'));
// 加载torn mini profile
Log.info('加载torn mini profile');
this.logger.info('加载torn mini profile');
let miniProfileInterval = {
id: window.setInterval(() => {
miniProfileInterval.counter++;
@ -175,7 +185,7 @@ export default class ZhongIcon extends WuhuBase {
}, 1000),
counter: 0
};
Log.info('图标加入文档树完成');
this.logger.info('图标加入文档树');
return this;
}
@ -196,8 +206,8 @@ export default class ZhongIcon extends WuhuBase {
isMouseDown = false;
if (isMouseMoved) {
isMouseMoved = false;
if (WuhuConfig.get("SaveIconPosition")) {
WuhuConfig.set("IconPosition", ZhongIcon.getPosition());
if (this.localConfigWrapper.config.SaveIconPosition) {
this.localConfigWrapper.config.IconPosition = ZhongIcon.getPosition();
}
}
});
@ -219,19 +229,20 @@ export default class ZhongIcon extends WuhuBase {
// 菜单
private constructMenuList(): ZhongIcon {
Log.info('构造展开菜单列表开始');
this.logger.info('构造菜单列表开始');
let timer = new Timer();
let glob = Global.getInstance();
const date = new Date();
let userInfo = this.infoUtils.getPlayerInfo();
let list: MenuItemConfig[] = [];
// 欢迎 显示玩家id
if (glob.player_info.userID !== 0) {
if (userInfo.userID !== 0) {
list.push({
domType: 'plain',
domId: 'wh-trans-welcome',
domHTML: `<a href="/profiles.php?XID=${ glob.player_info.userID }" target="_blank">${ glob.player_info.playername }</a>[${ glob.player_info.userID }]`,
domHTML:
`<a href="/profiles.php?XID=${ userInfo.userID }" target="_blank">${ userInfo.playername }</a>[${ userInfo.userID }]`,
});
}
// 节日
@ -322,14 +333,14 @@ export default class ZhongIcon extends WuhuBase {
domType: 'button',
domId: 'wh-quick-fly-btn',
domText: '✈️ 一键起飞',
clickFunc: () => QuickFlyBtnHandler.getInstance().handle(),
clickFunc: () => Container.factory(QuickFlyBtnHandler).handle(),
});
// 飞花库存
list.push({
domType: 'button',
domId: 'wh-foreign-stock-btn',
domText: '🌸 飞花库存',
clickFunc: () => TravelItem.getInstance().clickHandler().then(),
clickFunc: () => Container.factory(TravelItem).clickHandler().then(),
});
// NPC LOOT
list.push({
@ -347,14 +358,14 @@ export default class ZhongIcon extends WuhuBase {
domType: 'button',
domId: 'wh-nnb-info',
domText: '👮‍ 查看NNB',
clickFunc: () => NNB.getInstance().handle(),
clickFunc: () => this.nnb.handle(),
});
// 常用链接
list.push({
domType: 'button',
domId: 'wh-link-collection',
domText: '🔗 常用链接',
clickFunc: () => QuickLinksHandler.getInstance().handle()
clickFunc: () => Container.factory(QuickLinksHandler).handle()
});
// 飞贼
// list.push({
@ -370,10 +381,10 @@ export default class ZhongIcon extends WuhuBase {
domType: 'button',
domId: 'wh-price-watcher-btn',
domText: '💊 价格监视',
clickFunc: () => ItemPriceWatcherHandler.getInstance().handle()
clickFunc: () => this.itemPriceWatcherHandler.handle()
});
// 全屏
if (!Global.getInstance().isPDA) list.push({
if (!this.tornPDAUtils.isPDA()) list.push({
domType: 'button', domId: '', domText: '🖥️ 进入全屏', clickFunc() {
document.documentElement.requestFullscreen().then();
}
@ -403,46 +414,23 @@ export default class ZhongIcon extends WuhuBase {
window.location.replace('https://www.torn.com/item.php?temp=4#xunzhaomuzhuang')
}
});
// 物品查价
list.push(ItemPriceHandler);
// 更新历史
list.push({
domType: 'button',
domId: '',
domText: '🐞 更新历史',
clickFunc: async () => ChangeLogHandler.getInstance().handle()
});
// list.push(ChangeLogHandler);
// 助手设置
list.push({
domType: 'button',
domId: '',
domText: '⚙️ 助手设置',
clickFunc: () => SettingsHandler.getInstance().handler(),
});
list.push(SettingsHandler);
// 测试
if (Log.debug()) list.push({
domType: 'button',
domId: '',
domText: '📐️ 测试',
clickFunc: async function () {
let startTime = performance.now();
Log.info('测试开始');
try {
Test.getInstance().test();
} catch (e) {
Log.error('测试异常,' + JSON.stringify(e));
}
Log.info('测试结束 ' + ((performance.now() - startTime) | 0) + 'ms');
},
});
if (this.logger.debug()) list.push(Test);
this.menuItemList = list;
Log.info('构造展开菜单列表结束' + timer.getTimeMs());
this.logger.info('构造展开菜单列表结束' + timer.getTimeMs());
return this;
}
}
export interface MenuItemConfig {
domType: 'button' | 'plain' | 'checkbox' | 'select';
domType: 'button' | 'plain' | 'checkbox' | 'select' | MENU_ITEM_TYPE;
tagName?: string;
domId?: string;
domText?: string;

View File

@ -1,346 +1,352 @@
import WuhuBase from "../WuhuBase";
import WuhuConfig from "../WuhuConfig";
import CommonUtils from "../utils/CommonUtils";
import Log from "../Log";
import Alert from "../utils/Alert";
import Global from "../Global";
import Device from "../../enum/Device";
import ATTACK_HELPER_CSS from "../../static/css/attack_helper.css";
import ActionButtonUtils from "../utils/ActionButtonUtils";
import TornStyleBlock from "../utils/TornStyleBlock";
import TornStyleSwitch from "../utils/TornStyleSwitch";
import DialogMsgBox from "../utils/DialogMsgBox";
import FetchUtils from "../utils/FetchUtils";
import MathUtils from "../utils/MathUtils";
import LoopHelper from "../utils/LoopHelper";
import TRAVEL_STATE from "../../enum/TravelState";
enum FIGHT_STAGE {
READY = 'ready',
IN_PROGRESS_OR_ERROR = 'in_progress_or_error',
FINISHED = 'finished',
END = 'end',
OTHER = 'other'
}
/**
*
* TODO
*/
export default class AttackHelper extends WuhuBase {
className = 'AttackHelper';
private currentStage: FIGHT_STAGE = FIGHT_STAGE.OTHER;
constructor() {
super();
window.setTimeout(() => this.urlMatch(), 0);
}
private urlMatch(): void {
if (window.location.href.contains(/loader\.php\?sid=attack/)) {
this.fightingPageHandle();
}
// 错误的攻击页面转跳
else if (window.location.href.includes('loader2.php') && WuhuConfig.get('attRelocate')) {
const spl = window.location.href.trim().split('=');
const uid = spl[spl.length - 1];
if (CommonUtils.getInstance().isValidUid(uid)) {
window.location.href = 'https://www.torn.com/loader.php?sid=attack&user2ID=' + uid;
} else {
Log.error('[AttackHelper] UID格式不正确');
}
}
}
private fightingPageHandle(): void {
// 光速刷新按钮
ActionButtonUtils.getInstance().add('光速刷新', () => this.doAttackReload());
// 盯梢
this.watchTarget();
new MutationObserver((_, observer) => {
let btnList = document.querySelectorAll('div[class^="dialogButtons___"] button') as NodeListOf<HTMLButtonElement>;
if (btnList.length === 0) {
if (this.currentStage === FIGHT_STAGE.READY && WuhuConfig.get('quickFinishAtt') === 3) {
document.body.classList.remove('wh-move-btn');
Log.info('移除body class wh-move-btn');
observer.disconnect();
}
// 错误或正在打
this.currentStage = FIGHT_STAGE.IN_PROGRESS_OR_ERROR;
Log.info('[attackHelper] currentStage', this.currentStage);
return;
}
btnList.forEach(btn => {
let btnText = btn.innerText.toLowerCase();
if (btnText.includes('start') || btnText.includes('join')) {
// 开始
this.quickStartFight();
} else if (btnText.includes('continue')) {
// 结束end
this.currentStage = FIGHT_STAGE.END;
observer.disconnect();
} else if (btnText.includes('leave')) {
// 无意识状态FINISHED
this.quickFinishFight(btnList);
}
Log.info('[attackHelper] currentStage', this.currentStage);
})
})
.observe(document.querySelector('#react-root'), { childList: true, subtree: true });
}
// 战斗页面快速刷新
private doAttackReload(): void {
if (!window.ReactDOM) {
new Alert('光速刷新失败未找到React对象');
Log.error('光速刷新失败未找到React对象');
return;
}
if (!document.querySelector('#react-root #attacker')) {
Log.error('dom元素未找到selector: [#react-root #attacker]');
return;
}
let script = document.querySelector('script[src*="/builds/attack/"]');
let url = script.src;
if (!url.contains(/runtime\..+\.js/)) {
Log.error('脚本源[' + url + '] 不匹配规则');
return;
}
window.ReactDOM.unmountComponentAtNode(document.querySelector('#react-root'));
script.remove();
let node = document.createElement('script');
node.src = url;
node.type = 'text/javascript';
document.head.appendChild(node);
}
// 光速拔刀
private quickStartFight(): void {
if (this.currentStage === FIGHT_STAGE.READY) {
return;
} else {
this.currentStage = FIGHT_STAGE.READY;
}
if (WuhuConfig.get('quickAttIndex') === 6) return;
/**
* pc #defender
* mobile #attacker
*/
const btn = <HTMLInputElement>(document.querySelector('#attacker button') || document.querySelector('#defender button'));
Log.info('操作按钮', { btn });
if (!btn.innerText.toLowerCase().includes('fight')) {
Log.info('未找到攻击按钮, 光速拔刀跳过');
new Alert('未找到攻击按钮, 光速拔刀跳过');
} else {
// 判断是否存在脚踢
const hasKick = !!document.querySelector('#weapon_boots');
// modal层
// const modal: HTMLElement = document.querySelector('div[class^="modal___"]');
let device = Global.getInstance().device;
Log.info(`当前设备类型是${ device }`);
// 区分设备
switch (device) {
case Device.PC: {
Log.info(`开始调整按钮位置`);
// 隐藏modal层
// modal.style.display = 'none';
// 根据选择的武器调整css
let css_top = '0';
switch (WuhuConfig.get('quickAttIndex')) {
// weapon_second
case 1: {
css_top = '97px';
break;
}
// weapon_melee
case 2: {
css_top = '194px';
break;
}
// weapon_temp
case 3: {
css_top = '291px';
break;
}
// weapon_fists
case 4:
// weapon_boots
case 5: {
css_top = '375px';
break;
}
}
CommonUtils.addStyle(ATTACK_HELPER_CSS);
CommonUtils.addStyle(`.wh-move-btn #defender div[class^="modal___"]{top: ${ css_top };}`);
document.body.classList.add('wh-move-btn');
break;
}
case Device.MOBILE: {
Log.info(`开始调整按钮位置`);
// 加入css
let css_top = '0';
let slot_height = '76px';
// 判断有没有脚踢
if (hasKick) {
// 根据选择的武器调整
switch (WuhuConfig.get('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 (WuhuConfig.get('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 = ATTACK_HELPER_CSS.replace('CSSVAR', css_top).replace('CSSVAR', slot_height);
// `
// .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;}
// `;
CommonUtils.addStyle(css_rule);
document.body.classList.toggle('wh-move-btn');
btn.onclick = () => {
if (WuhuConfig.get('quickFinishAtt') !== 3) {
btn.remove();
// 停止自动刷新
// stop_reload = true;
} else {
document.body.classList.toggle('wh-move-btn');
}
};
break;
}
case Device.TABLET: {
break;
}
}
}
}
// 光速跑路
private quickFinishFight(btnList: NodeListOf<HTMLButtonElement>): void {
if (this.currentStage === FIGHT_STAGE.FINISHED) {
return;
} else {
this.currentStage = FIGHT_STAGE.FINISHED;
}
if (WuhuConfig.get('quickFinishAtt') === 3) {
document.body.classList.remove('wh-move-btn');
Log.info('移除body class wh-move-btn');
return;
}
const user_btn_select = ['leave', 'mug', 'hosp'][WuhuConfig.get('quickFinishAtt')];
// const wrap = document.querySelector('#react-root');
Log.info('光速跑路选项选中:', user_btn_select);
// const btn_arr: HTMLButtonElement[] = document.querySelectorAll('div[class^="dialogButtons___"] button') as unknown as HTMLButtonElement[];
if (btnList.length > 1) btnList.forEach(btn => {
const flag = btn.innerText.toLowerCase().includes(user_btn_select);
Log.info('按钮内容:', btn.innerText, ',是否包含选中:', flag);
if (!flag) btn.style.display = 'none';
});
}
// 盯梢模式
private watchTarget(): void {
Log.info('获取目标id');
let targetId = window.location.href.split('user2ID=')[1];
if (!CommonUtils.getInstance().isValidUid(targetId)) {
Log.error('目标id获取错误', targetId);
throw new Error('目标id获取错误:' + targetId);
}
let loop = new LoopHelper(async () => {
let userProfile;
try {
userProfile = await FetchUtils.getInstance().getProfile(targetId);
} catch {
Log.error('盯梢模式无法获取目标id');
throw new Error('盯梢模式无法获取目标id');
}
await CommonUtils.getInstance().sleep(MathUtils.getInstance().getRandomInt(20, 50));
if ((userProfile.userStatus.status === 'ok' && CommonUtils.getInstance().getTravelStage() === TRAVEL_STATE.IN_TORN) ||
(userProfile.userStatus.status === 'abroad' && CommonUtils.getInstance().getTravelStage() === TRAVEL_STATE.ABROAD)) {
watchSwitch.getInput().checked = false;
window.setTimeout(async () => {
new Alert('目标已落地/出院/出狱!', { timeout: 10, force: true, sysNotify: true });
await CommonUtils.getInstance().audioPlay();
await CommonUtils.getInstance().sleep(300);
await CommonUtils.getInstance().audioPlay();
await CommonUtils.getInstance().sleep(300);
await CommonUtils.getInstance().audioPlay();
await CommonUtils.getInstance().sleep(300);
}, 0);
}
});
let block = new TornStyleBlock('盯梢模式').insert2Dom();
let watchSwitch = new TornStyleSwitch('开启');
block.append(watchSwitch.getBase());
watchSwitch.getInput().addEventListener('change', () => {
if (watchSwitch.getInput().checked) {
new DialogMsgBox('检测玩家状态,当目标状态变成(海外)落地、出院或出狱时通知并播放声音提醒,后可搭配光速刷新食用<br/>确定开启?', {
callback: () => {
if (CommonUtils.getInstance().getTravelStage() === TRAVEL_STATE.FLYING) {
new Alert('失败!已取消');
watchSwitch.getInput().checked = false;
return;
}
Log.info('盯梢开启, 目标id' + targetId);
loop.start(parseInt(WuhuConfig.get('WatchTargetFreq')));
},
cancel: () => watchSwitch.getInput().checked = false
});
} else {
loop.stop();
Log.info('盯梢关闭');
}
});
}
}
// import CommonUtils from "../utils/CommonUtils";
// import Alert from "../utils/Alert";
// import Global from "../Global";
// import Device from "../../enum/Device";
// import ATTACK_HELPER_CSS from "../../../static/css/attack_helper.module.css";
// import ActionButtonUtils from "../utils/ActionButtonUtils";
// import TornStyleBlock from "../utils/TornStyleBlock";
// import TornStyleSwitch from "../utils/TornStyleSwitch";
// import DialogMsgBox from "../utils/DialogMsgBox";
// import FetchUtils from "../utils/FetchUtils";
// import MathUtils from "../utils/MathUtils";
// import LoopHelper from "../utils/LoopHelper";
// import TRAVEL_STATE from "../../enum/TravelState";
// import { Injectable } from "../../container/Injectable";
// import ClassName from "../../container/ClassName";
// import LocalConfigWrapper from "../LocalConfigWrapper";
// import Logger from "../Logger";
//
// enum FIGHT_STAGE {
// READY = 'ready',
// IN_PROGRESS_OR_ERROR = 'in_progress_or_error',
// FINISHED = 'finished',
// END = 'end',
// OTHER = 'other'
// }
//
// /**
// * 战斗助手
// * TODO 页面加载已经在进行中的战斗时的正确判断
// */
// @Injectable()
// @ClassName('AttackHelper')
// export default class AttackHelper {
//
// private currentStage: FIGHT_STAGE = FIGHT_STAGE.OTHER;
//
// constructor(
// private readonly localConfigWrapper: LocalConfigWrapper,
// private readonly commonUtils: CommonUtils,
// private readonly global: Global,
// private readonly mathUtils: MathUtils,
// private readonly actionButtonUtils: ActionButtonUtils,
// private readonly fetchUtils: FetchUtils,
// private readonly logger: Logger,
// ) {
// }
//
// init(): void {
// window.setTimeout(() => this._init(), 0);
// }
//
// private _init() {
// if (window.location.href.contains(/loader\.php\?sid=attack/)) {
// this.fightingPageHandle();
// }
// // 错误的攻击页面转跳
// // else if (window.location.href.includes('loader2.php') && this.localConfigWrapper.config.attRelocate) {
// // const spl = window.location.href.trim().split('=');
// // const uid = spl[spl.length - 1];
// // if (this.commonUtils.isValidUid(uid)) {
// // window.location.href = 'https://www.torn.com/loader.php?sid=attack&user2ID=' + uid;
// // } else {
// // this.logger.error('[AttackHelper] UID格式不正确');
// // }
// // }
// }
//
// private fightingPageHandle(): void {
// // 光速刷新按钮
// this.actionButtonUtils.add('光速刷新', () => this.doAttackReload());
//
// // 盯梢
// this.watchTarget();
//
// new MutationObserver((_, observer) => {
// let btnList = document.querySelectorAll('div[class^="dialogButtons___"] button') as NodeListOf<HTMLButtonElement>;
//
// if (btnList.length === 0) {
// if (this.currentStage === FIGHT_STAGE.READY && this.localConfigWrapper.config.quickFinishAtt === 3) {
// document.body.classList.remove('wh-move-btn');
// this.logger.info('移除body class wh-move-btn');
// observer.disconnect();
// }
// // 错误或正在打
// this.currentStage = FIGHT_STAGE.IN_PROGRESS_OR_ERROR;
// this.logger.info('[attackHelper] currentStage', this.currentStage);
// return;
// }
// btnList.forEach(btn => {
// let btnText = btn.innerText.toLowerCase();
// if (btnText.includes('start') || btnText.includes('join')) {
// // 开始
// this.quickStartFight();
// } else if (btnText.includes('continue')) {
// // 结束end
// this.currentStage = FIGHT_STAGE.END;
// observer.disconnect();
// } else if (btnText.includes('leave')) {
// // 无意识状态FINISHED
// this.quickFinishFight(btnList);
// }
// this.logger.info('[attackHelper] currentStage', this.currentStage);
// })
// })
// .observe(document.querySelector('#react-root'), { childList: true, subtree: true });
// }
//
// // 战斗页面快速刷新
// private doAttackReload(): void {
// if (!window.ReactDOM) {
// new Alert('光速刷新失败未找到React对象');
// this.logger.error('光速刷新失败未找到React对象');
// return;
// }
// if (!document.querySelector('#react-root #attacker')) {
// this.logger.error('dom元素未找到selector: [#react-root #attacker]');
// return;
// }
// let script = document.querySelector('script[src*="/builds/attack/"]');
// let url = script.src;
// if (!url.contains(/runtime\..+\.js/)) {
// this.logger.error('脚本源[' + url + '] 不匹配规则');
// return;
// }
// window.ReactDOM.unmountComponentAtNode(document.querySelector('#react-root'));
// script.remove();
// let node = document.createElement('script');
// node.src = url;
// node.type = 'text/javascript';
// document.head.appendChild(node);
// }
//
// // 光速拔刀
// private quickStartFight(): void {
// if (this.currentStage === FIGHT_STAGE.READY) {
// return;
// } else {
// this.currentStage = FIGHT_STAGE.READY;
// }
// if (this.localConfigWrapper.config.quickAttIndex === 6) return;
// /**
// * pc #defender
// * mobile #attacker
// */
// const btn = <HTMLInputElement>(document.querySelector('#attacker button') || document.querySelector('#defender button'));
// this.logger.info('操作按钮', { btn });
// if (!btn.innerText.toLowerCase().includes('fight')) {
// this.logger.info('未找到攻击按钮, 光速拔刀跳过');
// new Alert('未找到攻击按钮, 光速拔刀跳过');
// } else {
// // 判断是否存在脚踢
// const hasKick = !!document.querySelector('#weapon_boots');
// // modal层
// // const modal: HTMLElement = document.querySelector('div[class^="modal___"]');
// let device = this.global.device;
// this.logger.info(`当前设备类型是${ device }`);
// // 区分设备
// switch (device) {
// case Device.PC: {
// this.logger.info(`开始调整按钮位置`);
// // 隐藏modal层
// // modal.style.display = 'none';
// // 根据选择的武器调整css
// let css_top = '0';
// switch (this.localConfigWrapper.config.quickAttIndex) {
// // weapon_second
// case 1: {
// css_top = '97px';
// break;
// }
// // weapon_melee
// case 2: {
// css_top = '194px';
// break;
// }
// // weapon_temp
// case 3: {
// css_top = '291px';
// break;
// }
// // weapon_fists
// case 4:
// // weapon_boots
// case 5: {
// css_top = '375px';
// break;
// }
// }
// this.commonUtils.styleInject(ATTACK_HELPER_CSS);
// CommonUtils.addStyle(`.wh-move-btn #defender div[class^="modal___"]{top: ${ css_top };}`);
// document.body.classList.add('wh-move-btn');
// break;
// }
// case Device.MOBILE: {
// this.logger.info(`开始调整按钮位置`);
// // 加入css
// let css_top = '0';
// let slot_height = '76px';
// // 判断有没有脚踢
// if (hasKick) {
// // 根据选择的武器调整
// switch (this.localConfigWrapper.config.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 (this.localConfigWrapper.config.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 = ATTACK_HELPER_CSS.replace('CSSVAR', css_top).replace('CSSVAR', slot_height);
//
// this.commonUtils.styleInject(css_rule);
// document.body.classList.toggle('wh-move-btn');
// btn.onclick = () => {
// if (this.localConfigWrapper.config.quickFinishAtt !== 3) {
// btn.remove();
// // 停止自动刷新
// // stop_reload = true;
// } else {
// document.body.classList.toggle('wh-move-btn');
// }
// };
// break;
// }
// case Device.TABLET: {
// break;
// }
// }
// }
// }
//
// // 光速跑路
// private quickFinishFight(btnList: NodeListOf<HTMLButtonElement>): void {
// if (this.currentStage === FIGHT_STAGE.FINISHED) {
// return;
// } else {
// this.currentStage = FIGHT_STAGE.FINISHED;
// }
// if (this.localConfigWrapper.config.quickFinishAtt === 3) {
// document.body.classList.remove('wh-move-btn');
// this.logger.info('移除body class wh-move-btn');
// return;
// }
// const user_btn_select = ['leave', 'mug', 'hosp'][this.localConfigWrapper.config.quickFinishAtt];
// // const wrap = document.querySelector('#react-root');
// this.logger.info('光速跑路选项选中:', user_btn_select);
// // const btn_arr: HTMLButtonElement[] = document.querySelectorAll('div[class^="dialogButtons___"] button') as unknown as HTMLButtonElement[];
// if (btnList.length > 1) btnList.forEach(btn => {
// const flag = btn.innerText.toLowerCase().includes(user_btn_select);
// this.logger.info('按钮内容:', btn.innerText, ',是否包含选中:', flag);
// if (!flag) btn.style.display = 'none';
// });
// }
//
// // 盯梢模式
// private watchTarget(): void {
// this.logger.info('获取目标id');
// let targetId = window.location.href.split('user2ID=')[1];
// if (!this.commonUtils.isValidUid(targetId)) {
// this.logger.error('目标id获取错误', targetId);
// throw new Error('目标id获取错误:' + targetId);
// }
// let loop = new LoopHelper(async () => {
// let userProfile;
// try {
// userProfile = await this.fetchUtils.getProfile(targetId);
// } catch {
// this.logger.error('盯梢模式无法获取目标id');
// throw new Error('盯梢模式无法获取目标id');
// }
// await this.commonUtils.sleep(this.mathUtils.getRandomInt(20, 50));
// if ((userProfile.userStatus.status === 'ok' && this.commonUtils.getTravelStage() === TRAVEL_STATE.IN_TORN) ||
// (userProfile.userStatus.status === 'abroad' && this.commonUtils.getTravelStage() === TRAVEL_STATE.ABROAD)) {
// watchSwitch.getInput().checked = false;
// window.setTimeout(async () => {
// new Alert('目标已落地/出院/出狱!', { timeout: 10, force: true, sysNotify: true });
// await this.commonUtils.audioPlay();
// await this.commonUtils.sleep(300);
// await this.commonUtils.audioPlay();
// await this.commonUtils.sleep(300);
// await this.commonUtils.audioPlay();
// await this.commonUtils.sleep(300);
// }, 0);
// }
// });
// let block = new TornStyleBlock('盯梢模式').insert2Dom();
// let watchSwitch = new TornStyleSwitch('开启');
// block.append(watchSwitch.getBase());
// watchSwitch.getInput().addEventListener('change', () => {
// if (watchSwitch.getInput().checked) {
// new DialogMsgBox('检测玩家状态,当目标状态变成(海外)落地、出院或出狱时通知并播放声音提醒,后可搭配光速刷新食用<br/>确定开启?', {
// callback: () => {
// if (this.commonUtils.getTravelStage() === TRAVEL_STATE.FLYING) {
// new Alert('失败!已取消');
// watchSwitch.getInput().checked = false;
// return;
// }
// this.logger.info('盯梢开启, 目标id' + targetId);
// loop.start(this.localConfigWrapper.config.WatchTargetFreq | 0);
// },
// cancel: () => watchSwitch.getInput().checked = false
// });
// } else {
// loop.stop();
// this.logger.info('盯梢关闭');
// }
// });
// }
// }

View File

@ -1,131 +0,0 @@
import WuhuBase from "../WuhuBase";
import WuhuConfig from "../WuhuConfig";
import Log from "../Log";
import InfoUtils from "../utils/InfoUtils";
import Alert from "../utils/Alert";
import MathUtils from "../utils/MathUtils";
import NOTIFY_HTML from "../../static/html/buyBeer/notify.html";
import CommonUtils from "../utils/CommonUtils";
import Popup from "../utils/Popup";
export default class BuyBeerHelper extends WuhuBase implements BeerMonitorLoop {
className = 'BuyBeerHelper';
private isNotifying = false;
private loopId: number = null;
private time: number;
private readonly notifyHtml: string = NOTIFY_HTML.replace('{{}}', MathUtils.getInstance().getRandomInt(0, 99).toString());
public constructor() {
super();
this.time = WuhuConfig.get('_15AlarmTime') || 30;
}
public start(): void {
if (this.loopId) {
Log.info('啤酒助手已在运行');
} else {
this.loopId = window.setInterval(async () => {
// 海外取消提醒
let { isTravelling, isAbroad } = await InfoUtils.getInstance().getUserState();
if (isTravelling || isAbroad) {
this.stop();
return;
}
let dt = new Date();
// 已选当天不提醒
const now = [dt.getUTCFullYear(), dt.getUTCMonth(), dt.getUTCDate()];
const ignore_date = WuhuConfig.get('_15_alarm_ignore') || '{}';
if (JSON.stringify(now) === JSON.stringify(ignore_date)) return;
// 正常提醒
let m = 14 - (dt.getMinutes() % 15);
let s = 60 - dt.getSeconds();
if (m === 0 && s < this.time) {
// 如从通知点开,则本次通知跳过
if (location.href.includes('clickfromnotify')) {
this.isNotifying = true;
location.hash = '';
return;
}
// 本次已通知
if (this.isNotifying) return;
this.isNotifying = true;
// 发送通知
const notify = new Alert(this.notifyHtml, {
timeout: 30,
sysNotify: true,
});
notify.getElement().querySelector('.wh-notify-msg button').addEventListener('click', () => this.skip_today());
notify.getElement().addEventListener('click', ev => {
if ((ev.target as HTMLElement).tagName.toLowerCase() === 'a') {
notify.close();
}
});
let audioPlay = CommonUtils.getInstance().audioPlay;
window.setTimeout(audioPlay, 800);
window.setTimeout(audioPlay, 800 * 2);
window.setTimeout(audioPlay, 800 * 3);
} else {
this.isNotifying = false;
}
}, 1000);
}
}
public stop(): void {
if (this.loopId) {
window.clearInterval(this.loopId);
this.loopId = null;
}
}
public set_time(t: number): void {
this.time = t;
}
public status(): '已启动' | '未启动' {
return this.loopId ? '已启动' : '未启动';
}
public is_running(): boolean {
return this.loopId !== null;
}
public skip_today(): void {
const date = new Date();
WuhuConfig.set('_15_alarm_ignore', [date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()], false);
// 通知
const notify = new Alert(`明早8点前将不再提醒 <button id="wh-rd-btn-${ MathUtils.getInstance().getRandomInt(0, 100) }">取消</button>`);
// 通知中的取消按钮
notify.getElement().querySelector('.wh-notify-msg button').addEventListener('click', () => WuhuConfig.set('_15_alarm_ignore', undefined, true));
}
public setTimeHandler(): void {
let popup = new Popup(`<label>提前提醒时间(秒)<input type="number" value="${ WuhuConfig.get('_15AlarmTime') }" /></label><p>区间为 1 ~ 60默认 50</p>`, '啤酒提醒时间设定');
let confirm = document.createElement('button');
confirm.innerHTML = '确定';
confirm.style.float = 'right';
confirm.addEventListener('click', () => {
let input: HTMLInputElement = popup.getElement().querySelector('input');
let num = (input.value as any) | 0;
if (num === WuhuConfig.get('_15AlarmTime')) return;
if (num < 1 || num > 60) num = 50;
input.value = num.toString();
WuhuConfig.set('_15AlarmTime', num);
this.set_time(num);
// 之前的运行状态
if (this.is_running()) this.start();
popup.close();
});
popup.getElement().appendChild(confirm);
}
}
export interface BeerMonitorLoop {
start?: Function;
stop?: Function;
set_time?: Function;
status?: Function;
is_running?: Function;
skip_today?: Function;
}

View File

@ -0,0 +1 @@
export default {};

View File

@ -1,84 +0,0 @@
import WuhuBase from "../WuhuBase";
import WuhuConfig from "../WuhuConfig";
import Log from "../Log";
import CommonUtils from "../utils/CommonUtils";
import FetchUtils from "../utils/FetchUtils";
import InfoUtils from "../utils/InfoUtils";
import Alert from "../utils/Alert";
import TRAVEL_STATE from "../../enum/TravelState";
/**
*
*/
export default class CompanyHelper extends WuhuBase {
className = 'CompanyHelper';
public constructor() {
super();
WuhuConfig.get('CHTrainsDetectSwitch') && this.trainsDetect().then();
}
public detectNow(): void {
this.trainsDetect(true).then();
}
/**
*
*
* TODO URL判断
*/
private async trainsDetect(test: boolean = false): Promise<null> {
// 通过用户的icon判断公司老板
if ((await InfoUtils.getInstance().getSessionData()).statusIcons.icons.company.iconID !== 'icon73') {
Log.info('火车检测跳过:非公司老板');
return;
}
// 上次检测时间戳
let lastDetect: number = WuhuConfig.get('CHTrainsDetect') || 0;
// 检测是否过了一天
if (test || CommonUtils.getInstance().isNewDay(lastDetect, -6)) {
let travelStage = CommonUtils.getInstance().getTravelStage(),
userStatus = (await InfoUtils.getInstance().getUserState()).status;
test && Log.info({ travelStage, userStatus });
if (travelStage === TRAVEL_STATE.IN_TORN && userStatus === 'ok')
FetchUtils.getInstance().fetchText('/companies.php')
.then(res => {
let tmp: HTMLElement = document.createElement('div');
let bodyTagStart = CommonUtils.getInstance().matchOne(res, /<body.+>/);
if (!bodyTagStart) {
Log.warn('火车检测: 无法获取数据');
throw new Error('火车检测: 无法获取数据');
}
tmp.innerHTML = res.split(bodyTagStart)[1].split('</body>')[0].trim()
.replaceAll('rel="stylesheet"', '')
.replaceAll('.css', '?404')
.replaceAll('type="text/javascript"', 'type="application/json"');
let trainsNode = tmp.querySelector('span.trains');
if (!trainsNode) {
Log.error('火车检测出错: 无法获取火车数');
throw new Error('火车检测出错: 无法获取火车数');
}
let trains: number = parseInt(trainsNode.innerText);
let stars: number = tmp.querySelectorAll('.company-rating .active').length / 2 || 1;
WuhuConfig.set('CHTrainsDetect', Date.now());
Log.info('火车检测: 火车/星级: ' + trains + '/' + stars);
Log.info({ tmp });
if (trains + stars > 20) {
new Alert(`公司助手<br/><br/>火车检测:火车明日将溢出!${ trains }/20火车`, {
timeout: 15,
force: true,
sysNotify: true
});
}
tmp.remove();
tmp = null;
})
.catch(error => {
Log.error('火车检测出错', error);
});
else Log.warn('[火车检测] 用户状态错误,跳过火车检测', { travelStage, userStatus });
} else {
Log.info('火车检测:今日已提醒,跳过');
}
}
}

View File

@ -1,35 +1,74 @@
import WuhuBase from "../WuhuBase";
import { MiniProfile } from "../../interface/responseType/MiniProfile";
import CommonUtils from "../utils/CommonUtils";
import globVars from "../../globVars";
import Provider from "../provider/Provider";
import ResponseInject from "../../interface/ResponseInject";
import { Injectable } from "../../container/Injectable";
import ClassName from "../../container/ClassName";
import LocalConfigWrapper from "../LocalConfigWrapper";
import Logger from "../Logger";
import MsgWrapper from "../utils/MsgWrapper";
import { fetchYata } from "../../func/module/fetchYata";
import toThousands from "../../func/utils/toThousands";
/**
* fetch
*/
export default class FetchEventCallback extends WuhuBase {
@Injectable()
@ClassName("FetchEventCallback")
export default class FetchEventCallback extends Provider implements ResponseInject {
className = "FetchEventCallback";
constructor() {
newNode = document.createElement('div')
bsEstNode = document.createElement('div')
constructor(
private readonly localConfigWrapper: LocalConfigWrapper,
private readonly commonUtils: CommonUtils,
private readonly logger: Logger,
private readonly msgWrapper: MsgWrapper,
) {
super();
globVars.responseHandlers.push((url, response) => this.handler(response, url))
}
/**
* fetch
* @param response
* @param url
* @param response
*/
public handler(response, url: string) {
public responseHandler(url: string, response) {
// mini profile 中添加上次动作
if (url.includes('profiles.php?step=getUserNameContextMenu')) {
if (url.startsWith('/page.php?sid=UserMiniProfile&userID')) {
window.setTimeout(async () => {
let cont = await CommonUtils.querySelector('[class*=profile-mini-_userProfileWrapper___]');
let cont = CommonUtils.querySelector('[class*=profile-mini-_userProfileWrapper___]');
let resp: MiniProfile = response.json as MiniProfile;
let newNode = document.createElement('div');
let formatted = CommonUtils.getInstance().secondsFormat(resp.user.lastAction.seconds);
if (this.localConfigWrapper.config.ShowMiniProfLastAct) {
this.logger.info({ resp })
let formatted = this.commonUtils.secondsFormat(resp.user.lastAction.seconds);
newNode.innerText = '上次动作: ' + formatted;
cont.append(newNode);
(await cont).append(this.newNode);
this.newNode.innerText = '上次动作: ' + formatted;
}
if (this.localConfigWrapper.config.isBSEstMiniProfOn) {
const id = resp.user.userID
const apikey = localStorage.getItem('APIKey')
this.bsEstNode.innerHTML = `[BS估算] [${ id }]获取中...`;
(await cont).append(this.bsEstNode)
if (!apikey) {
this.bsEstNode.innerHTML = '[BS估算] 未配置APIKey无法估算BS'
this.logger.error('MINI Profile bs估算失败: APIKey为空')
} else {
const bsData = fetchYata(id, apikey)
bsData.then(data => {
// 网速过慢时可能mini profile容器已更新新内容与上次请求的用户数据不同需要判断
if (this.bsEstNode.innerHTML.includes(resp.user.userID.toString())) {
this.bsEstNode.innerHTML = `[BS估算] ${ resp.user.playerName }[${ id }] ${ toThousands(data.total) }`
}
})
.catch(err => {
this.bsEstNode.innerHTML = `[BS估算] ${ err.message }`
})
}
}
}, 0);
}
}

View File

@ -1,49 +1,74 @@
import WuhuBase from "../WuhuBase";
import TornStyleBlock from "../utils/TornStyleBlock";
import WuhuConfig from "../WuhuConfig";
import Alert from "../utils/Alert";
import TornStyleSwitch from "../utils/TornStyleSwitch";
import { Injectable } from "../../container/Injectable";
import ClassName from "../../container/ClassName";
import LocalConfigWrapper from "../LocalConfigWrapper";
import IFeature from "../../man/IFeature";
import MsgWrapper from "../utils/MsgWrapper";
export default class PTHelper extends WuhuBase {
className = 'PTHelper';
private readonly observer;
private readonly usersPointSell;
@Injectable()
@ClassName('PTHelper')
export default class PTHelper implements IFeature {
private observer: MutationObserver;
private usersPointSell: HTMLDivElement;
constructor(
private readonly localConfigWrapper: LocalConfigWrapper,
private readonly msgWrapper: MsgWrapper,
) {
}
description(): string {
return "pt一键购买";
}
iStart(): void | Promise<void> {
this.start()
}
urlExcludes(): RegExp[] {
return [];
}
urlIncludes(): RegExp[] {
return [/pmarket\.php/];
}
start() {
constructor() {
super();
this.observer = new MutationObserver(e => {
for (const t of e) {
t.addedNodes.forEach(e => 'LI' === (e as HTMLElement).tagName && this.removeConfirm(e))
}
});
this.usersPointSell = document.querySelector('.users-point-sell');
this.usersPointSell = document.querySelector<HTMLDivElement>('.users-point-sell');
let block = new TornStyleBlock('PT一键购买').insert2Dom();
let switcher = new TornStyleSwitch('开启');
block.append(switcher.getBase());
let toggle = switcher.getInput();
toggle.checked = WuhuConfig.get('ptQuickBuy');
toggle.checked = this.localConfigWrapper.config.ptQuickBuy;
if (toggle.checked) {
new Alert('一键购买已开启');
this.msgWrapper.create('一键购买已开启');
for (const index in this.usersPointSell.children) {
'LI' === this.usersPointSell.children[index].tagName && this.removeConfirm(this.usersPointSell.children[index])
}
this.observer.observe(this.usersPointSell, { childList: true })
}
toggle.addEventListener('change', () => {
WuhuConfig.set('ptQuickBuy', toggle.checked, false);
this.localConfigWrapper.config.ptQuickBuy = toggle.checked;
if (toggle.checked) {
for (const index in this.usersPointSell.children) {
'LI' === this.usersPointSell.children[index].tagName && this.removeConfirm(this.usersPointSell.children[index])
}
this.observer.observe(this.usersPointSell, { childList: true });
new Alert('一键购买已开启');
this.msgWrapper.create('一键购买已开启');
} else {
for (const index in this.usersPointSell.children) {
'LI' === this.usersPointSell.children[index].tagName && this.rollbackConfirm(this.usersPointSell.children[index])
}
this.observer.disconnect();
new Alert('一键购买已关闭');
this.msgWrapper.create('一键购买已关闭');
}
});
}

View File

@ -1,47 +1,151 @@
import WuhuBase from "../WuhuBase";
import Log from "../Log";
import WuhuConfig from "../WuhuConfig";
import CommonUtils from "../utils/CommonUtils";
import TornStyleBlock from "../utils/TornStyleBlock";
import TornStyleSwitch from "../utils/TornStyleSwitch";
import FetchUtils from "../utils/FetchUtils";
import ResponseInject from "../../interface/ResponseInject";
import globVars from "../../globVars";
import IUserProfileData from "../../interface/IUserProfileData";
import ClassName from "../../container/ClassName";
import { Injectable } from "../../container/Injectable";
import LocalConfigWrapper from "../LocalConfigWrapper";
import Logger from "../Logger";
import IFeature from "../../man/IFeature";
import { fetchYata } from "../../func/module/fetchYata";
import MsgWrapper from "../utils/MsgWrapper";
import toThousands from "../../func/utils/toThousands";
import { timePastFormat } from "../../func/utils/timePastFormat";
export default class ProfileHelper extends WuhuBase {
className = 'ProfileHelper';
@ClassName('ProfileHelper')
@Injectable()
export default class ProfileHelper implements ResponseInject, IFeature {
private block: TornStyleBlock;
constructor(
private readonly localConfigWrapper: LocalConfigWrapper,
private readonly commonUtils: CommonUtils,
private readonly logger: Logger,
private readonly msgWrapper: MsgWrapper,
) {
}
description(): string {
return "个人资料页面辅助";
}
iStart(): void | Promise<void> {
this.start()
}
urlExcludes(): RegExp[] {
return [];
}
// 曾用名已检测过标记
private task = true;
urlIncludes(): RegExp[] {
return [/profiles\.php\?XID=/];
}
start() {
constructor() {
super();
CommonUtils.addStyle('body.wh-hide_profile_img .profile-image a.profile-image-wrapper .img-wrap img{display:none;}');
let id = document.querySelector('link[rel="canonical"]').getAttribute('href').split('=')[1];
// let id = document.querySelector('link[rel="canonical"]').getAttribute('href').split('=')[1];
let id = (new URL(window.location.href)).searchParams.get('XID');
// id获取格式判断
if (!CommonUtils.getInstance().isValidUid(id)) {
Log.error('[ProfileHelper] id格式错误');
if (!this.commonUtils.isValidUid(id)) {
this.logger.error('[ProfileHelper] id格式错误');
}
if (WuhuConfig.get('HideProfileImg')) {
Log.info('[ProfileHelper] 隐藏头像');
if (this.localConfigWrapper.config.HideProfileImg) {
this.logger.info('[ProfileHelper] 隐藏头像');
document.body.classList.toggle('wh-hide_profile_img');
}
let block = new TornStyleBlock('芜湖助手').insert2Dom();
this.block = new TornStyleBlock('芜湖助手').insert2Dom();
// 隐藏头像
let hideImgSwitch = new TornStyleSwitch('隐藏头像', WuhuConfig.get('HideProfileImg'));
block.append(hideImgSwitch.getBase());
hideImgSwitch.getInput().addEventListener('change', () => {
document.body.classList.toggle('wh-hide_profile_img');
WuhuConfig.set('HideProfileImg', hideImgSwitch.getInput().checked, true);
});
// 曾用名
let nameHistoryNode;
if (WuhuConfig.get('ShowNameHistory')) {
nameHistoryNode = document.createElement('p');
nameHistoryNode.innerHTML = '曾用名:';
block.append(nameHistoryNode);
FetchUtils.getInstance().getProfile(id).then((res) => {
if (res.userInformation.previousAliases.length > 0) {
res.userInformation.previousAliases.forEach(item => nameHistoryNode.innerHTML += item + ' ');
} else {
nameHistoryNode.innerHTML += '暂无';
}
try {
let hideImgSwitch = new TornStyleSwitch('隐藏头像', this.localConfigWrapper.config.HideProfileImg);
this.block.append(hideImgSwitch.getBase());
hideImgSwitch.getInput().addEventListener('change', () => {
document.body.classList.toggle('wh-hide_profile_img');
this.localConfigWrapper.config.HideProfileImg = hideImgSwitch.getInput().checked;
});
if (this.localConfigWrapper.config.ShowNameHistory) {
globVars.responseHandlers.push((...args: any[]) => this.responseHandler.apply(this, args));
}
} catch (e) {
this.logger.error('隐藏头像时出错了', e.stack)
}
// bs估算
if (this.localConfigWrapper.config.isBSEstProfOn) {
try {
const apikey = localStorage.getItem('APIKey')
if (!apikey) {
this.msgWrapper.create('BS估算失败: 尚未设定APIKey', null, 'error')
}
const promise = fetchYata(parseInt(id), apikey)
const domNode = document.createElement('div')
domNode.innerHTML = 'BS估算中...'
domNode.classList.add('mt-4')
domNode.style.border = '1px solid green'
domNode.style.padding = '2px'
this.block.append(domNode)
const buildType = { Offensive: '攻击型', Defensive: '防御型', Balanced: '平衡型' }
promise.then(data => {
domNode.innerHTML = `<b>BS估算</b><br/>
BS: ${ toThousands(data.total) }<br/>
评分: ${ toThousands(data.score) }<br/>
风格: ${ buildType[data.type] }<br/>
偏差: ${ data.skewness }%<br/>
估算时间: ${ timePastFormat(Date.now() - data.timestamp * 1000) }
`
}).catch(err => {
domNode.innerHTML = 'BS估算出错了: ' + err.message
throw new TypeError('BS估算出错了: ' + err.message)
})
} catch (e) {
this.msgWrapper.create('BS估算失败' + e.message, null, 'error')
throw new TypeError('BS估算失败' + e.message)
}
}
}
responseHandler(url: string, body: { json: unknown; text: string; isModified: boolean }) {
if (url.includes('profiles.php?step=getProfileData') && this.task) {
// 曾用名
const nameHistoryNode = document.createElement('p');
nameHistoryNode.innerHTML = '曾用名:';
this.block.append(nameHistoryNode);
let resp = body.json as IUserProfileData;
if (resp.userInformation.previousAliases.length > 0) { // 曾用名列表
resp.userInformation.previousAliases.forEach(item => nameHistoryNode.innerHTML += item + ' ');
} else {
nameHistoryNode.innerHTML += '暂无';
}
let lastAction = -1
let onlineStatusTitle = '-'
if (resp.basicInformation?.lastAction.seconds) {
lastAction = resp.basicInformation.lastAction.seconds
}
if (resp.basicInformation.icons) {
for (let i = 0; i < resp.basicInformation.icons.length; i++) {
let item = resp.basicInformation.icons[i]
if (item.id === 1) {
onlineStatusTitle = '🟢️ 在线'
break
}
if (item.id === 62) {
onlineStatusTitle = '🟡 挂机'
break
}
if (item.id === 2) {
onlineStatusTitle = '⚪ 离线'
break
}
}
}
const lastActionNode = document.createElement('p')
lastActionNode.innerHTML = `${ onlineStatusTitle } ${ this.commonUtils.secondsFormat(lastAction) }`
this.block.append(lastActionNode)
this.task = false;
}
}
}

View File

@ -0,0 +1,46 @@
import ClassName from "../../container/ClassName";
import { Injectable } from "../../container/Injectable";
import Logger from "../Logger";
import NetHighLvlWrapper, { BATTLE_STAT } from "../utils/NetHighLvlWrapper";
import { InjectionKey } from "vue";
import MsgWrapper from "../utils/MsgWrapper";
type GymResponse = {
success: boolean,
// 成功才有 You gained 689,636.71 strength
gainMessage?: string,
message: string,
text?: string,
error?: string,
};
@ClassName("QuickGymTrain")
@Injectable()
export default class QuickGymTrain {
constructor(
private readonly logger: Logger,
private readonly netHighLvlWrapper: NetHighLvlWrapper,
private readonly msgWrapper: MsgWrapper,
) {
}
doTrain(type = BATTLE_STAT.STR, count = 199) {
window.setTimeout(async () => {
let resObj: GymResponse;
try {
resObj = JSON.parse(await this.netHighLvlWrapper.doGymTrain(type, count))
} catch (e) {
resObj = { success: false, message: '解析失败' };
this.logger.error(e.stack || e.message || e);
}
let msgRs = resObj.success ? '成功' : '失败';
let msgMsg = resObj.message || resObj.text || resObj.error;
this.msgWrapper.create(
'锻炼结果: ' + msgRs + '<br/>提示: ' + (resObj.gainMessage || msgMsg),
{}, resObj.success ? 'success' : 'error'
);
}, 0);
}
}
export const QuickGymTrainKey = Symbol('QuickGymTrainKey') as InjectionKey<QuickGymTrain>;

View File

@ -1,14 +1,33 @@
import WuhuBase from "../WuhuBase";
import TornStyleBlock from "../utils/TornStyleBlock";
import TornStyleSwitch from "../utils/TornStyleSwitch";
import CommonUtils from "../utils/CommonUtils";
import WuhuConfig from "../WuhuConfig";
import ClassName from "../../container/ClassName";
import { Injectable } from "../../container/Injectable";
import LocalConfigWrapper from "../LocalConfigWrapper";
import IFeature from "../../man/IFeature";
export default class SearchHelper extends WuhuBase {
className = 'SearchHelper';
@ClassName('SearchHelper')
@Injectable()
export default class SearchHelper implements IFeature {
description(): string {
return "搜索助手";
}
constructor() {
super();
iStart(): void | Promise<void> {
this.init()
}
urlExcludes(): RegExp[] {
return [];
}
urlIncludes(): RegExp[] {
return [/page\.php\?sid=UserList/];
}
constructor(
private readonly localConfigWrapper: LocalConfigWrapper,
) {
}
public init(): void {
@ -16,7 +35,7 @@ export default class SearchHelper extends WuhuBase {
let placeholderSwitch = new TornStyleSwitch('底部空白占位区');
block.append(placeholderSwitch.getBase()).insert2Dom();
CommonUtils.addStyle('body.WHSearchPagePlaceholder .content.logged-in {margin-bottom: 340px;}');
let config = WuhuConfig.get('SearchPagePlaceholder');
let config = this.localConfigWrapper.config.SearchPagePlaceholder;
placeholderSwitch.getInput().checked = config || false;
config ? this.enable() : this.disable();
placeholderSwitch.getInput().addEventListener('change', () => {
@ -26,11 +45,11 @@ export default class SearchHelper extends WuhuBase {
private enable(): void {
document.body.classList.add('WHSearchPagePlaceholder');
WuhuConfig.set('SearchPagePlaceholder', true);
this.localConfigWrapper.config.SearchPagePlaceholder = true;
}
private disable(): void {
document.body.classList.remove('WHSearchPagePlaceholder');
WuhuConfig.set('SearchPagePlaceholder', false);
this.localConfigWrapper.config.SearchPagePlaceholder = false;
}
}

View File

@ -1,16 +1,43 @@
import WuhuBase from "../WuhuBase";
import TornStyleBlock from "../utils/TornStyleBlock";
import InfoUtils from "../utils/InfoUtils";
import Log from "../Log";
import FetchUtils from "../utils/FetchUtils";
import CommonUtils from "../utils/CommonUtils";
import MathUtils from "../utils/MathUtils";
import ClassName from "../../container/ClassName";
import { Injectable } from "../../container/Injectable";
import Logger from "../Logger";
import IFeature from "../../man/IFeature";
/**
*
*/
export default class SlotsHelper extends WuhuBase {
className = "SlotsHelper";
@ClassName("SlotsHelper")
@Injectable()
export default class SlotsHelper implements IFeature {
description(): string {
return "老虎机批量购买助手";
}
iStart(): void | Promise<void> {
this.init()
}
urlExcludes(): RegExp[] {
return [];
}
urlIncludes(): RegExp[] {
return [/loader\.php\?sid=slots/];
}
constructor(
private readonly mathUtils: MathUtils,
private readonly commonUtils: CommonUtils,
private readonly fetchUtils: FetchUtils,
private readonly infoUtils: InfoUtils,
private readonly logger: Logger,
) {
}
private block = new TornStyleBlock("芜湖助手");
@ -56,7 +83,7 @@ export default class SlotsHelper extends WuhuBase {
try {
await this.goBtnHandler(ev, ret)
} catch (e) {
Log.error(e.stack || e);
this.logger.error(e.stack || e);
}
goBtn.disabled = false;
});
@ -68,7 +95,7 @@ export default class SlotsHelper extends WuhuBase {
private async goBtnHandler(ev: MouseEvent, nodes: SlotsHelperNodes) {
let { counterInput, select, msgBox } = nodes;
// 现金
let cash = (await InfoUtils.getInstance().getSessionData())?.user?.money?.value;
let cash = (await this.infoUtils.getSessionData())?.user?.money?.value;
msgBox.innerText = '等待加载中';
// 代币
let tokens: number = parseInt((await CommonUtils.querySelector('.player-info-cont #tokens')).innerText);
@ -89,7 +116,7 @@ export default class SlotsHelper extends WuhuBase {
// 实际购买数量
let i: number;
for (i = 0; i < count; i++) {
let res: SlotsResponse = await (await FetchUtils.getInstance().ajaxFetch({
let res: SlotsResponse = await (await this.fetchUtils.ajaxFetch({
url: window.addRFC("https://www.torn.com/loader.php?sid=slotsInterface&step=play&stake=" + price),
referrer: "/loader.php?sid=slots",
method: "GET"
@ -100,7 +127,7 @@ export default class SlotsHelper extends WuhuBase {
msgBox.innerHTML += '<br/>代币不足';
break;
}
await CommonUtils.getInstance().sleep(MathUtils.getInstance().getRandomInt(1800, 2400));
await this.commonUtils.sleep(this.mathUtils.getRandomInt(1800, 2400));
}
// 利润
let profit = wonTotal - i * price;
@ -109,7 +136,7 @@ export default class SlotsHelper extends WuhuBase {
msgBox.innerHTML += '<br/>已停止, ';
msgBox.innerHTML += bang ? '血' : '小';
msgBox.innerHTML += profit > 0 ? '赚' : '亏';
Log.info("[goBtnHandler]结束", { cash, tokens, count, price, wonTotal });
this.logger.info("[goBtnHandler]结束", { cash, tokens, count, price, wonTotal });
}
}

View File

@ -1,24 +0,0 @@
import WuhuBase from "../WuhuBase";
import TornStyleBlock from "../utils/TornStyleBlock";
import WuhuConfig from "../WuhuConfig";
import TornStyleSwitch from "../utils/TornStyleSwitch";
export default class StackHelper extends WuhuBase {
className = 'StackHelper';
constructor() {
super();
let block = new TornStyleBlock('叠E保护').insert2Dom();
let switcher = new TornStyleSwitch('启用');
let input = switcher.getInput();
block.append(switcher.getBase());
input.checked = WuhuConfig.get('SEProtect');
if (input.checked) document.body.classList.add('wh-gym-stack');
// 绑定点击事件
input.onchange = e => {
let target = e.target as HTMLInputElement;
document.body.classList.toggle('wh-gym-stack');
WuhuConfig.set('SEProtect', target.checked, true);
};
}
}

View File

@ -1,19 +1,15 @@
import WuhuBase from "../WuhuBase";
import { chatDict, eventsDict, headerDict, propertyDict, sidebarDict } from "../../dictionary/translation";
import Log from "../Log";
import Timer from "../utils/Timer";
import { Button, MiniProfile } from "../../interface/responseType/MiniProfile";
import WuhuConfig from "../WuhuConfig";
import Sidebar from "../../interface/responseType/Sidebar";
export default class Translate extends WuhuBase {
className = 'Translate';
/**
* @deprecated 使TranslateNew替代
*/
export default class Translate {
public translateVer: string = '1.0';
constructor() {
super();
Log.info('翻译开始');
// Log.info('翻译开始');
let start = new Timer();
// 时分秒转换
String.prototype.replaceHMS = function replaceHMS() {
@ -42,7 +38,7 @@ export default class Translate extends WuhuBase {
this.chatTranslate();
this.playerSearchBoxTrans();
this.urlMatchPageTranslate(window.location.href);
Log.info('翻译结束, 耗时' + start.getTimeMs());
// Log.info('翻译结束, 耗时' + start.getTimeMs());
}, 0);
}
@ -293,297 +289,4 @@ export default class Translate extends WuhuBase {
observer.observe(document.body, opt);
}).observe(document.body, opt);
}
/**
* fetch xhr
* @param url
* @param body
*/
public static responseHandler(url: string, body: { json: unknown, text: string, isModified: boolean }): void {
if (!WuhuConfig.get('transNew')) return;
// TODO 字典抽取
let map = {
iconMap: {
'Online': { title: "在线" },
'Level 100': { title: "100 级" },
'Jail': {
title: "坐牢", description: {
map: {
'Being questioned for suspicious online activity.': '因可疑的网上活动而被盘问。',
'Suspect of a presidential assassination': '刺杀总统的嫌疑人',
}
}
},
'Federal jail': {
title: "联邦监狱(FJ)", description: {
map: {
'Account marked for deletion': '账号标记删除',
}
}
},
'Idle': { title: "暂离" },
'Offline': { title: "离线" },
'Enby': { title: "中性" },
'Male': { title: "男性" },
'Female': { title: "女性" },
'Donator': { title: "DP捐助者" },
'Subscriber': { title: "蓝星订阅者" },
'Traveling': { title: "旅行中" },
'Hospital': {
title: "已入院",
description: {
map: {
'Fell from a two story building while on a hitman mission': '在执行刺杀任务时从二楼摔下',
'Overdosed on Xanax': '吃 Xan 后 OD',
'Mauled by a guard dog': '被看门狗咬',
},
replace: [/(Hospitalized|Mugged|Attacked|Defeated) by (someone|<a.+\/a>)(.+Early discharge available.+)?/, '被 $2 $1$3'],
attachedMap: [
{
'Hospitalized': '强制住院',
'Mugged': '抢劫',
'Attacked': '攻击',
'Defeated': '击败',
},
{ 'someone': '某人' },
{ '<br><i>Early discharge available</i>': '<br><i>提前出院(ED)可用</i>' },
],
}
},
'Bounty': {
title: "被悬赏",
description: { replace: [/On this person's head for (\$[,0-9]+)( : .+)?/, '$1 悬赏此人$2'] }
},
'Married': {
title: "已婚", description: { replace: [/To/, '和'] }
},
'Company': {
title: "公司",
description: {
replace: [/([a-zA-Z ]+)( of )(.+) \(([aA-zZ ]+)\)$/, "【$3】$4的$1"],
attachedMap: [
{
'Director': '老板',
'Salesperson': '销售员',
},
{}, {},
{
'Private Security Firm': '安保公司 (PSF)',
'Lingerie Store': '内衣店 (LS)',
'Adult Novelties': '成人用品店 (AN)',
}
]
}
},
'Job': {
title: "系统公司",
description: {
replace: [/([a-zA-Z ]+) (in|at) (.+)$/, "$3的$1"],
attachedMap: [
{
'Manager': '经理',
},
{},
{
'a Grocery Store': '杂货店',
}
]
}
},
'Faction': {
title: "帮派",
description: { replace: [/([aA-zZ ]+)( of )(.+)/, "[$3] 帮派的 [$1]"] }
},
'Bazaar': {
title: "摊位",
description: {
map: {
'This person has items in their bazaar for sale':
'此人在摊位上有物品出售'
}
}
},
},
buttonMap: {
message: {
'Add $0 to your enemy list': '添加 $0 到敌人列表',
'Add $0 to your friend list': '添加 $0 到好友列表',
'You are currently traveling': '你在天上',
'Initiate a chat with $0': '与 $0 私聊',
'You are not in Torn': '你不在城内',
"View $0's personal statistics": '查看 $0 的个人统计数据',
"Place a bounty on $0": '对 $0 发起悬赏',
"Report $0 to staff": '向工作人员举报 $0',
"Send $0 a message": '发邮件给 $0',
"View $0's display case": '查看 $0 的展柜',
"$0 is currently in hospital": '$0 正在住院',
"$0 has not been online in the last 6 hours": '$0 超 6 小时未在线',
"Give some money to $0": '给 $0 一些钱',
"$0's bazaar is closed": '$0 的摊位已关闭',
"View $0's bazaar": '查看 $0 的摊位',
"Initiate a trade with $0": '与 $0 交易',
"$0 has no items in their bazaar": '$0 的摊位是空的',
"$0 is currently in jail": '$0 目前在坐牢',
"Pay $0's bail": '支付 $0 的保释金',
"Bust $0 out of jail": '把 $0 从监狱里踢出来',
"$0 is currently in federal jail": '$0 目前在联邦监狱(FJ)',
"NPC's cannot be bountied": 'NPC 不能被悬赏',
"$0 is hiding out abroad": '$0 正在海外躲藏',
"$0 has no items in their display cabinet": '$0 的展柜是空的',
"You do not have enough energy to attack $0": '你没有足够的能量攻击 $0',
"You have not met this person recently or they have blocked you": '最近你没有遇见此人,或已被屏蔽',
},
},
destinationMap: {
'Mexico': '墨西哥',
'Cayman Islands': '开曼群岛',
'Canada': '加拿大',
'Hawaii': '夏威夷',
'United Kingdom': '英国',
'Argentina': '阿根廷',
'Switzerland': '瑞士',
'Japan': '日本',
'China': '中国',
'UAE': '阿联酋(UAE)',
'South Africa': '南非',
},
barMap: {
'Chain': { name: '连击' },
'Energy': { name: '能量' },
'Happy': { name: '快乐' },
'Life': { name: '血量' },
'Nerve': { name: '犯罪' },
},
areaMap: {
calendar: { name: '日历', shortName: '日历' },
traveling: { name: '飞行中', shortName: '飞行' },
casino: { name: '赌场' },
city: { name: '城市' },
crimes: { name: '犯罪' },
education: { name: '教育', shortName: '教育' },
forums: { name: '论坛' },
gym: { name: '健身房' },
hall_of_fame: { name: '名人堂', shortName: '排名' },
home: { name: '主页', shortName: '主页' },
hospital: { name: '医院' },
items: { name: '物品' },
jail: { name: '监狱' },
job: { name: '工作', shortName: '工作' },
missions: { name: '任务' },
my_faction: { name: '帮派', shortName: '帮派' },
newspaper: { name: '报纸', shortName: '报纸' },
properties: { name: '住宅', shortName: '住宅' },
recruit_citizens: { name: '招募玩家', shortName: '招募' },
},
accountMap: {
awards: { name: '奖章' },
events: { name: '通知' },
messages: { name: '邮件' }
}
};
try {
// Mini Profile
if (url.includes('profiles.php?step=getUserNameContextMenu')) {
let jsonObj: MiniProfile = body.json as MiniProfile;
Log.info('翻译mini profile返回内容');
// 状态图标
jsonObj.icons.forEach(icon => {
let iconMap = map.iconMap;
let oriTitle = icon.title;
if (iconMap[oriTitle]) {
icon.title = iconMap[oriTitle].title;
let desc = iconMap[oriTitle].description;
let oriDesc = icon.description;
if (icon.description && desc) {
if (desc.map && desc.map[oriDesc]) {
icon.description = desc.map[oriDesc];
} else if (desc.replace) {
icon.description = oriDesc.replace(new RegExp(desc.replace[0]), desc.replace[1]);
if (desc.attachedMap) {
desc.attachedMap.forEach((item, index) => {
let factor = oriDesc.replace(new RegExp(desc.replace[0]), '$' + (index + 1));
Log.info({ factor });
let cn = item[factor];
cn && (icon.description = icon.description.replace(factor, cn));
});
}
}
}
}
});
// 离线转钱警告
if (jsonObj.user.sendMoneyWarning) {
let daysMatch = jsonObj.user.sendMoneyWarning.match(/[0-9]+/);
if (daysMatch.length !== 0)
jsonObj.user.sendMoneyWarning = `警告:此人已离线 ${ daysMatch[0] }`;
}
// 按钮
let buttons = jsonObj.profileButtons.buttons;
let buttonKeyList = Object.keys(buttons);
let username = jsonObj.user.playerName;
let msgMap = map.buttonMap.message;
buttonKeyList.forEach(buttonKey => {
if (buttons[buttonKey].state === 'hidden') return;
let button: Button = buttons[buttonKey];
let oriMsg = button.message.replace(username, '$0');
if (msgMap[oriMsg]) {
button.message = msgMap[oriMsg].replace('$0', username);
}
});
// TODO 称号
// 用户状态
let status = jsonObj.userStatus.status.type;
switch (status) {
case 'traveling-to': {
let origin = jsonObj.userStatus.status.to.simpleName;
let cn = map.destinationMap[origin]
cn && (jsonObj.userStatus.status.to.simpleName = cn);
break;
}
case 'traveling-from': {
let origin = jsonObj.userStatus.status.from.simpleName;
let cn = map.destinationMap[origin]
cn && (jsonObj.userStatus.status.from.simpleName = cn);
break;
}
case 'abroad': {
let origin = jsonObj.userStatus.status.in.simpleName;
let cn = map.destinationMap[origin]
cn && (jsonObj.userStatus.status.in.simpleName = cn);
break;
}
case 'jail': {
let origin = jsonObj.userStatus.status.description;
let cn = map.iconMap.Jail.description.map[origin];
cn && (jsonObj.userStatus.status.description = cn);
break;
}
}
body.isModified = true;
Log.info({ 'localized': jsonObj });
}
// TODO 边栏
else if (url.includes('sidebarAjaxAction.php?q=sync')) {
let response = body.json as Sidebar;
type target = { [k: string]: { name: string, shortName?: string } };
let nameMapReplace = (target: target, _map: target) => {
Object.keys(target).forEach(key => {
if (target[key] && _map[key]) {
target[key].name = _map[key].name;
if (target[key].shortName && _map[key].shortName) {
target[key].shortName = _map[key].shortName;
}
}
});
};
nameMapReplace(response.areas, map.areaMap);
nameMapReplace(response.account, map.accountMap);
body.isModified = true;
}
} catch (e) {
Log.error('responseHandler', e.stack || e.message);
}
}
}

View File

@ -0,0 +1,399 @@
import ResponseInject from "../../interface/ResponseInject";
import { Button, MiniProfile } from "../../interface/responseType/MiniProfile";
import Sidebar from "../../interface/responseType/Sidebar";
import InventoryItemInfo from "../../interface/responseType/InventoryItemInfo";
import CommonUtils from "../utils/CommonUtils";
import { itemNameDict, itemPageDict } from "../../dictionary/translation";
import Provider from "../provider/Provider";
import ClassName from "../../container/ClassName";
import { Injectable } from "../../container/Injectable";
import LocalConfigWrapper from "../LocalConfigWrapper";
import Logger from "../Logger";
/**
*
*/
@ClassName('TranslateNew')
@Injectable()
export default class TranslateNew extends Provider implements ResponseInject {
constructor(
private readonly localConfigWrapper: LocalConfigWrapper,
private readonly logger: Logger,
private readonly commonUtils: CommonUtils,
) {
super();
}
/**
* fetch xhr
* @param url
* @param body
* @param opt
*/
public responseHandler(
url: string,
body: { json: unknown, text: string, isModified: boolean },
opt: { method: 'GET' | 'POST', requestBody: string }
): void {
if (!this.localConfigWrapper.config.transNew) return;
// TODO 字典抽取
let map = {
iconMap: {
'Online': { title: "在线" },
'Level 100': { title: "100 级" },
'Jail': {
title: "坐牢", description: {
map: {
'Being questioned for suspicious online activity.': '因可疑的网上活动而被盘问。',
'Suspect of a presidential assassination': '刺杀总统的嫌疑人',
}
}
},
'Federal jail': {
title: "联邦监狱(FJ)", description: {
map: {
'Account marked for deletion': '账号标记删除',
}
}
},
'Idle': { title: "暂离" },
'Offline': { title: "离线" },
'Enby': { title: "中性" },
'Male': { title: "男性" },
'Female': { title: "女性" },
'Donator': { title: "DP捐助者" },
'Subscriber': { title: "蓝星订阅者" },
'Traveling': { title: "旅行中" },
'Hospital': {
title: "已入院",
description: {
map: {
'Fell from a two story building while on a hitman mission': '在执行刺杀任务时从二楼摔下',
'Overdosed on Xanax': '吃 Xan 后 OD',
'Mauled by a guard dog': '被看门狗咬',
},
replace: [/(Hospitalized|Mugged|Attacked|Defeated) by (someone|<a.+\/a>)(.+Early discharge available.+)?/, '被 $2 $1$3'],
attachedMap: [
{
'Hospitalized': '强制住院',
'Mugged': '抢劫',
'Attacked': '攻击',
'Defeated': '击败',
},
{ 'someone': '某人2' },
{ '<br><i>Early discharge available</i>': '<br><i>提前出院(ED)可用</i>' },
],
}
},
'Bounty': {
title: "被悬赏",
description: { replace: [/On this person's head for (\$[,0-9]+)( : .+)?/, '$1 悬赏此人$2'] }
},
'Married': {
title: "已婚", description: { replace: [/To/, '和'] }
},
'Company': {
title: "公司",
description: {
replace: [/([a-zA-Z ]+)( of )(.+) \(([aA-zZ ]+)\)$/, "【$3】$4的$1"],
attachedMap: [
{
'Director': '老板',
'Salesperson': '销售员',
},
{}, {},
{
'Private Security Firm': '安保公司 (PSF)',
'Lingerie Store': '内衣店 (LS)',
'Adult Novelties': '成人用品店 (AN)',
}
]
}
},
'Job': {
title: "系统公司",
description: {
replace: [/([a-zA-Z ]+) (in|at) (.+)$/, "$3的$1"],
attachedMap: [
{
'Manager': '经理',
},
{},
{
'a Grocery Store': '杂货店',
}
]
}
},
'Faction': {
title: "帮派",
description: { replace: [/([aA-zZ ]+)( of )(.+)/, "[$3] 帮派的 [$1]"] }
},
'Bazaar': {
title: "摊位",
description: {
map: {
'This person has items in their bazaar for sale':
'此人在摊位上有物品出售'
}
}
},
},
buttonMap: {
message: {
'Add $0 to your enemy list': '添加 $0 到敌人列表',
'Add $0 to your friend list': '添加 $0 到好友列表',
'You are currently traveling': '你在天上',
'Initiate a chat with $0': '与 $0 私聊',
'You are not in Torn': '你不在城内',
"View $0's personal statistics": '查看 $0 的个人统计数据',
"Place a bounty on $0": '对 $0 发起悬赏',
"Report $0 to staff": '向工作人员举报 $0',
"Send $0 a message": '发邮件给 $0',
"View $0's display case": '查看 $0 的展柜',
"$0 is currently in hospital": '$0 正在住院',
"$0 has not been online in the last 6 hours": '$0 超 6 小时未在线',
"Give some money to $0": '给 $0 一些钱',
"$0's bazaar is closed": '$0 的摊位已关闭',
"View $0's bazaar": '查看 $0 的摊位',
"Initiate a trade with $0": '与 $0 交易',
"$0 has no items in their bazaar": '$0 的摊位是空的',
"$0 is currently in jail": '$0 目前在坐牢',
"Pay $0's bail": '支付 $0 的保释金',
"Bust $0 out of jail": '把 $0 从监狱里踢出来',
"$0 is currently in federal jail": '$0 目前在联邦监狱(FJ)',
"NPC's cannot be bountied": 'NPC 不能被悬赏',
"$0 is hiding out abroad": '$0 正在海外躲藏',
"$0 has no items in their display cabinet": '$0 的展柜是空的',
"You do not have enough energy to attack $0": '你没有足够的能量攻击 $0',
"You have not met this person recently or they have blocked you": '最近你没有遇见此人,或已被屏蔽',
},
},
destinationMap: {
'Mexico': '墨西哥',
'Cayman Islands': '开曼群岛',
'Canada': '加拿大',
'Hawaii': '夏威夷',
'United Kingdom': '英国',
'Argentina': '阿根廷',
'Switzerland': '瑞士',
'Japan': '日本',
'China': '中国',
'UAE': '阿联酋(UAE)',
'South Africa': '南非',
},
barMap: {
'Chain': { name: '连击' },
'Energy': { name: '能量' },
'Happy': { name: '快乐' },
'Life': { name: '血量' },
'Nerve': { name: '犯罪' },
},
areaMap: {
calendar: { name: '日历', shortName: '日历' },
traveling: { name: '飞行中', shortName: '飞行' },
casino: { name: '赌场' },
city: { name: '城市' },
crimes: { name: '犯罪' },
education: { name: '教育', shortName: '教育' },
forums: { name: '论坛' },
gym: { name: '健身房' },
hall_of_fame: { name: '名人堂', shortName: '排名' },
home: { name: '主页', shortName: '主页' },
hospital: { name: '医院' },
items: { name: '物品' },
jail: { name: '监狱' },
job: { name: '工作', shortName: '工作' },
missions: { name: '任务' },
my_faction: { name: '帮派', shortName: '帮派' },
newspaper: { name: '报纸', shortName: '报纸' },
properties: { name: '住宅', shortName: '住宅' },
recruit_citizens: { name: '招募玩家', shortName: '招募' },
},
accountMap: {
awards: { name: '奖章' },
events: { name: '通知' },
messages: { name: '邮件' }
}
};
try {
// Mini Profile
if (url.includes('profiles.php?step=getUserNameContextMenu')) {
let jsonObj: MiniProfile = body.json as MiniProfile;
this.logger.info('翻译mini profile返回内容');
// 状态图标
jsonObj.icons.forEach(icon => {
let iconMap = map.iconMap;
let oriTitle = icon.title;
if (iconMap[oriTitle]) {
icon.title = iconMap[oriTitle].title;
let desc = iconMap[oriTitle].description;
let oriDesc = icon.description;
if (icon.description && desc) {
if (desc.map && desc.map[oriDesc]) {
icon.description = desc.map[oriDesc];
} else if (desc.replace) {
icon.description = oriDesc.replace(new RegExp(desc.replace[0]), desc.replace[1]);
if (desc.attachedMap) {
desc.attachedMap.forEach((item, index) => {
let factor = oriDesc.replace(new RegExp(desc.replace[0]), '$' + (index + 1));
this.logger.info({ factor });
let cn = item[factor];
cn && (icon.description = icon.description.replace(factor, cn));
});
}
}
}
}
});
// 离线转钱警告
if (jsonObj.user.sendMoneyWarning) {
let daysMatch = jsonObj.user.sendMoneyWarning.match(/[0-9]+/);
if (daysMatch.length !== 0)
jsonObj.user.sendMoneyWarning = `警告:此人已离线 ${ daysMatch[0] }`;
}
// 按钮
let buttons = jsonObj.profileButtons.buttons;
let buttonKeyList = Object.keys(buttons);
let username = jsonObj.user.playerName;
let msgMap = map.buttonMap.message;
buttonKeyList.forEach(buttonKey => {
if (buttons[buttonKey].state === 'hidden') return;
let button: Button = buttons[buttonKey];
let oriMsg = button.message.replace(username, '$0');
if (msgMap[oriMsg]) {
button.message = msgMap[oriMsg].replace('$0', username);
}
});
// TODO 称号
// 用户状态
let status = jsonObj.userStatus.status.type;
switch (status) {
case 'traveling-to': {
let origin = jsonObj.userStatus.status.to.simpleName;
let cn = map.destinationMap[origin]
cn && (jsonObj.userStatus.status.to.simpleName = cn);
break;
}
case 'traveling-from': {
let origin = jsonObj.userStatus.status.from.simpleName;
let cn = map.destinationMap[origin]
cn && (jsonObj.userStatus.status.from.simpleName = cn);
break;
}
case 'abroad': {
let origin = jsonObj.userStatus.status.in.simpleName;
let cn = map.destinationMap[origin]
cn && (jsonObj.userStatus.status.in.simpleName = cn);
break;
}
case 'jail': {
let origin = jsonObj.userStatus.status.description;
let cn = map.iconMap.Jail.description.map[origin];
cn && (jsonObj.userStatus.status.description = cn);
break;
}
}
body.isModified = true;
this.logger.info({ 'localized': jsonObj });
}
// TODO 边栏
else if (url.includes('sidebarAjaxAction.php?q=sync')) {
let response = body.json as Sidebar;
type target = { [k: string]: { name: string, shortName?: string } };
let nameMapReplace = (target: target, _map: target) => {
Object.keys(target).forEach(key => {
if (target[key] && _map[key]) {
target[key].name = _map[key].name;
if (target[key].shortName && _map[key].shortName) {
target[key].shortName = _map[key].shortName;
}
}
});
};
nameMapReplace(response.areas, map.areaMap);
nameMapReplace(response.account, map.accountMap);
body.isModified = true;
}
// 物品详情
else if ((url.includes('inventory.php?step=info')) || (url.includes('inventory.php') && opt?.method === 'POST' &&
typeof opt?.requestBody === 'string' && opt?.requestBody.includes('step=info'))) {
this.logger.info('responseHandler');
let resp = body.json as InventoryItemInfo;
// TODO 维护通用物品数据(对应名称、描述、类型)缓存
let map: { [k: string]: Partial<InventoryItemInfo> } = {
'Glass of Beer': {
itemName: '一杯啤酒',
itemInfo: '[译]Only savages drink beer straight out of the bottle. This glass of beer is obtained fresh from the keg, and provides the same level of drunken joy as you\'d get from a regular bottle of suds. Provides a moderate nerve increase when consumed.',
itemInfoContent: "<div class='m-bottom10'>" +
"<span class=\"bold\">一杯啤酒</span> 是酒类物品" +
"</div>" +
"Only savages drink beer straight out of the bottle. This glass of beer is obtained fresh from the keg, and provides the same level of drunken joy as you'd get from a regular bottle of suds. Provides a moderate nerve increase when consumed." +
"<div class=\"t-green bold item-effect m-top10\">效果: 犯罪 + 2增幅CD + 1h。</div>",
},
};
let idMap = { 816: 'Glass of Beer' };
let itemInfo = this.commonUtils.getItemByIdOrName(resp.itemName, idMap, map);
if (itemInfo) {
body.isModified = true;
resp.itemInfo = itemInfo.itemInfo;
resp.itemName = itemInfo.itemName;
resp.itemInfoContent = itemInfo.itemInfoContent;
}
// TODO 老字典
let itemName = itemNameDict[resp.itemName];
if (itemName) {
body.isModified = true;
resp.itemInfoContent = resp.itemInfoContent
.replace('The ', '')
.replace(resp.itemName, `${ itemName }(${ resp.itemName })`);
}
}
// 物品列表
else if ((url.includes('item.php') || url.includes('inventory.php')) && opt?.method === 'POST' &&
typeof opt?.requestBody === 'string' &&
(opt?.requestBody.includes('step=getCategoryList') ||
opt?.requestBody.includes('step=getList') ||
opt?.requestBody.includes('step=getNotAllItemsListWithoutGroups')
)) {
let resp = body.json as { html: string };
if (resp.html) {
let tmp = document.createElement('div');
tmp.innerHTML = resp.html;
this.logger.info(tmp);
tmp.childNodes.forEach(li => {
if (li.nodeType === 1) {
let elem = li as Element;
// 物品名
let name = elem.querySelector('.name-wrap .name');
let nameZh = itemNameDict[name.innerText.trim()];
if (nameZh) {
name.innerText = `${ name.innerText } ${ nameZh }`;
}
// 操作按钮
elem.querySelectorAll('.icon-h').forEach(action => {
let attrTitle = action.getAttribute('title');
// TODO
let zh = itemPageDict[attrTitle];
if (zh) {
action.setAttribute('title', zh);
}
});
}
});
resp.html = tmp.innerHTML;
body.isModified = true;
}
}
// TODO 物品列表json版
else if (url.includes('inventory.php') && opt?.method === 'POST' &&
typeof opt?.requestBody === 'string' && opt?.requestBody.includes('step=getList')) {
}
} catch (e) {
this.logger.error('responseHandler', e.stack || e.message);
}
}
}

View File

@ -1,61 +0,0 @@
import CommonUtils from "../utils/CommonUtils";
import Log from "../Log";
import WuhuBase from "../WuhuBase";
import UserScriptEngine from "../../enum/UserScriptEngine";
import Popup from "../utils/Popup";
import STOCK_IMG_HTML from "../../static/html/stock_img.html";
import * as FILTER from "../../static/json/for_stock_item_filter.json";
import WindowActiveState from "./WindowActiveState";
export default class TravelItem extends WuhuBase {
className = 'TravelItem';
private readonly apiUrl: string = 'https://yata.yt/api/v1/travel/export/';
private foreignStockInfo: any = null;
public constructor() {
super();
window.setInterval(async () => {
if (!WindowActiveState.getInstance().get()) return;
Log.info('COFetch ', this.apiUrl);
this.foreignStockInfo = JSON.parse(await CommonUtils.COFetch(this.apiUrl));
Log.info({ info: 'TravelItem 跨域返回', 'returned': this.foreignStockInfo });
}, 30 * 1000);
}
// 呈现内容
public async clickHandler(): Promise<void> {
if (CommonUtils.getScriptEngine() === UserScriptEngine.RAW) {
new Popup(STOCK_IMG_HTML.replace('{{}}', performance.now().toString()), '飞花库存');
} else {
const popup = new Popup("请稍后 " + CommonUtils.loading_gif_html(), '飞花库存');
let table = `<table><tr><th colspan="2">目的地 - 更新时间</th><th colspan="3">库存</th></tr>`;
const dest = FILTER.default;
const now = new Date();
const res = await this.get();
Log.info({ res })
if (!res || !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.getElement().innerHTML = table;
}
}
private async get() {
return this.foreignStockInfo ||= JSON.parse(await CommonUtils.COFetch(this.apiUrl));
}
}

View File

@ -1,13 +1,14 @@
import uuidv4 from "../../func/utils/uuidv4";
import WuhuBase from "../WuhuBase";
import ClassName from "../../container/ClassName";
import { Injectable } from "../../container/Injectable";
export default class WindowActiveState extends WuhuBase {
className = 'WindowActiveState';
isFocus = false;
uuid = uuidv4();
@ClassName('WindowActiveState')
@Injectable()
export default class WindowActiveState {
private isFocus = false;
private uuid = uuidv4();
constructor() {
super();
if (self !== top) return null;
localStorage.setItem('whuuid', this.uuid);
document.addEventListener('visibilitychange',
@ -17,6 +18,7 @@ export default class WindowActiveState extends WuhuBase {
addEventListener('blur', () => this.isFocus = false);
}
// 当前实例是否激活
get(): boolean {
// 当前窗口获得了焦点 优先级最高
if (this.isFocus) return true;

View File

@ -1,18 +1,49 @@
import WuhuBase from "../WuhuBase";
import XUNZHAOMUZHUANG_HTML from "../../static/html/xunzhaomuzhuang/index.html";
import * as MUZHUANG_ID_LIST_JSON from "../../static/json/muzhuang_id_list.json";
import XUNZHAOMUZHUANG_HTML from "../../../static/html/xunzhaomuzhuang/index.html";
import * as MUZHUANG_ID_LIST_JSON from "../../../static/json/muzhuang_id_list.json";
import CommonUtils from "../utils/CommonUtils";
import XUNZHAOMUZHUANG_CSS from "../../static/css/xunzhaomuzhuang.css";
import XUNZHAOMUZHUANG_CSS from "../../../static/css/xunzhaomuzhuang.module.css";
import TornStyleBlock from "../utils/TornStyleBlock";
import MathUtils from "../utils/MathUtils";
import FetchUtils from "../utils/FetchUtils";
import ClassName from "../../container/ClassName";
import { Injectable } from "../../container/Injectable";
import IFeature from "../../man/IFeature";
/**
*
* /item.php?temp=4#xunzhaomuzhuang
*/
export default class XZMZ extends WuhuBase {
className = 'XZMZ';
@ClassName('XZMZ')
@Injectable()
export default class XZMZ implements IFeature {
description(): string {
return "寻找木桩";
}
iStart(): void | Promise<void> {
this.start()
}
urlExcludes(): RegExp[] {
return [];
}
urlIncludes(): RegExp[] {
return [/item.php\?temp=4/];
}
start() {
let hasInit: boolean = false
let handle = () => {
if (!hasInit && window.location.hash === '#xunzhaomuzhuang') {
this.init()
hasInit = true
}
}
window.addEventListener('hashchange', handle)
handle()
}
private mainRoleContainer: HTMLElement;
private IDList: number[];
private btn: HTMLButtonElement;
@ -21,12 +52,16 @@ export default class XZMZ extends WuhuBase {
private tips: HTMLElement;
private counter: number;
public constructor() {
super();
public constructor(
private readonly mathUtils: MathUtils,
private readonly commonUtils: CommonUtils,
private readonly fetchUtils: FetchUtils,
) {
}
public init() {
CommonUtils.addStyle(XUNZHAOMUZHUANG_CSS);
this.commonUtils.styleInject(XUNZHAOMUZHUANG_CSS);
document.body.classList.add('wh-hide-title');
document.title = document.title.replace('Items', '寻找木桩');
this.mainRoleContainer = document.querySelector('div[role="main"]');
let block = new TornStyleBlock('寻找木桩');
@ -64,7 +99,7 @@ export default class XZMZ extends WuhuBase {
}
private async SearchDeadman(id) {
FetchUtils.getInstance().getProfile(id).then((res) => {
this.fetchUtils.getProfile(id).then((res) => {
if (res.userStatus.status.type === 'ok') {
this.addRow({
player_id: res.user.userID,
@ -73,7 +108,7 @@ export default class XZMZ extends WuhuBase {
});
}
});
await CommonUtils.getInstance().sleep(MathUtils.getInstance().getRandomInt(100, 200));
await this.commonUtils.sleep(this.mathUtils.getRandomInt(100, 200));
}
private addRow(data) {

View File

@ -0,0 +1,132 @@
const notifyKey = Symbol('ConfigNotifyKey');
class DefaultConfigType {
// 开启翻译
transEnable = false;
@Notified()
transNew = true;
// 快速犯罪
quickCrime = true;
// 任务助手
missionHint = true;
// 小镇攻略
xmasTownWT = true;
// 小镇提醒
xmasTownNotify = true;
// 起飞爆e
energyAlert = true;
// 飞行闹钟
trvAlarm = true;
// 啤酒提醒
_15Alarm = true;
@Notified()
_15_alarm_ignore: number[] = [];
// 啤酒提醒时间
_15AlarmTime = 50;
// 捡垃圾助手
cityFinder = false;
// 叠E保护
@Notified()
SEProtect = false;
// PT一键购买
ptQuickBuy = false;
// 光速拔刀 6-关闭
quickAttIndex = 2;
// 光速跑路 0-leave 1-mug 2-hos 3-关闭
quickFinishAtt = 3;
// 自动开打和结束
autoStartFinish = false;
// 攻击自刷新 0-无间隔 1-5s 6-关闭
attReload = 6;
/** @deprecated */
attRelocate = false;
// 价格监视
priceWatcher = { xan: -1, pt: -1 };
// 开发者模式
isDev = false;
// 4条转跳
barsRedirect = true;
// 浮动存钱框
floatDepo = true;
// 公司转跳存钱
companyRedirect = true;
// 收起公司冰蛙效率表
companyBWCollapse = true;
// 海外警告
abroadWarning = true;
// 落地转跳
@Notified()
landedRedirect = '';
// 任何位置一键存钱
companyDepositAnywhere = false;
// 火车提醒时间
CHTrainsDetect = 0;
// 火车提醒开关
CHTrainsDetectSwitch = true;
// 隐藏个人资料头像
@Notified()
HideProfileImg = false;
// 显示曾用名
ShowNameHistory = true;
// 盯梢模式强度 0-550 1-950 2-1450 ms
WatchTargetFreq = 1;
// 隐藏侧边栏
HideSidebar = false;
// 添加隐藏边栏按钮
HideSidebarBtn = true;
// 搜索页占位区
SearchPagePlaceholder = true;
// 解决一直转圈(加载中)的问题
@Notified()
SolveGoogleScriptPendingIssue = false;
// 图标坐标
IconPosition: Partial<{ x: number, y: number }> = {};
// 记住图标位置
SaveIconPosition = false;
// 现金变动提醒
CashChangeAlert = false;
// 收集数据以改进翻译质量
CollectPlayerData = true;
// 迷你资料卡显示上次行动时间
ShowMiniProfLastAct = true;
// 登陆邮箱
@Notified()
autoLoginEmail = '';
// 登陆密码
autoLoginPwd = '';
// 自定义css
@Notified()
CustomCss = '';
monitorOn = ['drugCDMonitor']
drugCDMonitorInterval = 60000
// mini profile显示bs估算
@Notified()
isBSEstMiniProfOn = false
// profile页面显示bs估算
@Notified()
isBSEstProfOn = true
}
export type Config = DefaultConfigType;
let config = new DefaultConfigType();
export default config as Readonly<Config>;
function Notified(f: boolean = true) {
return Reflect.metadata(notifyKey, f);
}
export function isNotified(propertyKey: string): boolean {
return Reflect.getMetadata(notifyKey, config, propertyKey);
}

Some files were not shown because too many files have changed in this diff Show More