Compare commits

...

27 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
102 changed files with 7324 additions and 4180 deletions

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,10 +1,174 @@
# TODO
- 翻译baza npc商店、imarket、imarket搜索结果
- log重构通知重构
# CHANGE # 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 ## 1.0.6
2023年06月26日 2023年06月26日

View File

@ -1,3 +1,23 @@
# 芜湖助手 Torncity 翻译插件 # Wuhu Torn Helper
[CHANGELOG](CHANGELOG.md) [![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

@ -21,7 +21,6 @@ let metaData =
// @downloadURL https://gitlab.com/JJins/wuhu-torn-helper/-/raw/dev/release.min.user.js // @downloadURL https://gitlab.com/JJins/wuhu-torn-helper/-/raw/dev/release.min.user.js
// @grant GM_xmlhttpRequest // @grant GM_xmlhttpRequest
// @grant unsafeWindow // @grant unsafeWindow
// @connect ljs-lyt.com
// @connect yata.yt // @connect yata.yt
// @connect github.io // @connect github.io
// @connect gitlab.com // @connect gitlab.com

10
global.d.ts vendored
View File

@ -122,10 +122,8 @@ declare module "*.module.css" {
declare function GM_xmlhttpRequest(init: any): void; declare function GM_xmlhttpRequest(init: any): void;
declare var unsafeWindow: Window & typeof globalThis; declare var unsafeWindow: Window & typeof globalThis;
declare interface UnknownFields {
// any property
[key: string]: any
}
declare type Constructor<T = any> = new (...args: any[]) => T; declare type Constructor<T = any> = new (...args: any[]) => T;
declare interface ClassType<T> {
new(...args: unknown[]): T
}

60
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "wuhu-torn-helper", "name": "wuhu-torn-helper",
"version": "0.8.7", "version": "1.2.2",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "wuhu-torn-helper", "name": "wuhu-torn-helper",
"version": "0.8.7", "version": "1.2.2",
"devDependencies": { "devDependencies": {
"@element-plus/icons-vue": "^2.1.0", "@element-plus/icons-vue": "^2.1.0",
"@rollup/plugin-alias": "^4.0.3", "@rollup/plugin-alias": "^4.0.3",
@ -17,11 +17,11 @@
"@rollup/plugin-terser": "^0.4.0", "@rollup/plugin-terser": "^0.4.0",
"@rollup/plugin-typescript": "^8.5.0", "@rollup/plugin-typescript": "^8.5.0",
"@types/jquery": "^3.5.14", "@types/jquery": "^3.5.14",
"@types/node": "^18.0.6", "@types/node": "^20.6.0",
"@vitejs/plugin-vue": "^4.0.0", "@vitejs/plugin-vue": "^4.0.0",
"@vue/tsconfig": "^0.1.3", "@vue/tsconfig": "^0.1.3",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"element-plus": "^2.3.3", "element-plus": "^2.3.10",
"just-clone": "^6.2.0", "just-clone": "^6.2.0",
"npm": "^8.19.2", "npm": "^8.19.2",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
@ -1150,9 +1150,9 @@
} }
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "18.0.6", "version": "20.6.0",
"resolved": "https://registry.npmmirror.com/@types/node/-/node-18.0.6.tgz", "resolved": "https://registry.npmmirror.com/@types/node/-/node-20.6.0.tgz",
"integrity": "sha512-/xUq6H2aQm261exT6iZTMifUySEt4GR5KX8eYyY+C4MSNPqSh9oNIP7tz2GLKTlFaiBbgZNxffoR3CVRG+cljw==", "integrity": "sha512-najjVq5KN2vsH2U/xyh2opaSEz6cZMR2SetLIlxlj08nOcmPOemJmUK2o4kUzfLqfrWE0PIrNeE16XhYDd3nqg==",
"dev": true "dev": true
}, },
"node_modules/@types/parse-json": { "node_modules/@types/parse-json": {
@ -1577,10 +1577,24 @@
} }
}, },
"node_modules/caniuse-lite": { "node_modules/caniuse-lite": {
"version": "1.0.30001457", "version": "1.0.30001597",
"resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001457.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001597.tgz",
"integrity": "sha512-SDIV6bgE1aVbK6XyxdURbUE89zY7+k1BBBaOwYwkNCglXlel/E7mELiHC64HQ+W0xSKlqWhV9Wh7iHxUjMs4fA==", "integrity": "sha512-7LjJvmQU6Sj7bL0j5b5WY/3n7utXUJvAe1lxhsHDbLmwX9mdL86Yjtr+5SRCyf8qME4M7pU2hswj0FpyBVCv9w==",
"dev": true "dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/browserslist"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/caniuse-lite"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
]
}, },
"node_modules/chalk": { "node_modules/chalk": {
"version": "4.1.2", "version": "4.1.2",
@ -1971,9 +1985,9 @@
"dev": true "dev": true
}, },
"node_modules/element-plus": { "node_modules/element-plus": {
"version": "2.3.3", "version": "2.3.12",
"resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.3.3.tgz", "resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.3.12.tgz",
"integrity": "sha512-Zy61OXrG6b4FF3h29A9ZOUkaEQXjCuFwNa7DlpB3Vo+42Tw5zBbHe5a4BY7i56TVJG5xTbS9UQyA726J91pDqg==", "integrity": "sha512-fAWpbKCyt+l1dsqSNPOs/F/dBN4Wp5CGAyxbiS5zqDwI4q3QPM+LxLU2h3GUHMIBtMGCvmsG98j5HPMkTKkvcA==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@ctrl/tinycolor": "^3.4.1", "@ctrl/tinycolor": "^3.4.1",
@ -8390,9 +8404,9 @@
} }
}, },
"@types/node": { "@types/node": {
"version": "18.0.6", "version": "20.6.0",
"resolved": "https://registry.npmmirror.com/@types/node/-/node-18.0.6.tgz", "resolved": "https://registry.npmmirror.com/@types/node/-/node-20.6.0.tgz",
"integrity": "sha512-/xUq6H2aQm261exT6iZTMifUySEt4GR5KX8eYyY+C4MSNPqSh9oNIP7tz2GLKTlFaiBbgZNxffoR3CVRG+cljw==", "integrity": "sha512-najjVq5KN2vsH2U/xyh2opaSEz6cZMR2SetLIlxlj08nOcmPOemJmUK2o4kUzfLqfrWE0PIrNeE16XhYDd3nqg==",
"dev": true "dev": true
}, },
"@types/parse-json": { "@types/parse-json": {
@ -8747,9 +8761,9 @@
} }
}, },
"caniuse-lite": { "caniuse-lite": {
"version": "1.0.30001457", "version": "1.0.30001597",
"resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001457.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001597.tgz",
"integrity": "sha512-SDIV6bgE1aVbK6XyxdURbUE89zY7+k1BBBaOwYwkNCglXlel/E7mELiHC64HQ+W0xSKlqWhV9Wh7iHxUjMs4fA==", "integrity": "sha512-7LjJvmQU6Sj7bL0j5b5WY/3n7utXUJvAe1lxhsHDbLmwX9mdL86Yjtr+5SRCyf8qME4M7pU2hswj0FpyBVCv9w==",
"dev": true "dev": true
}, },
"chalk": { "chalk": {
@ -9066,9 +9080,9 @@
"dev": true "dev": true
}, },
"element-plus": { "element-plus": {
"version": "2.3.3", "version": "2.3.12",
"resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.3.3.tgz", "resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.3.12.tgz",
"integrity": "sha512-Zy61OXrG6b4FF3h29A9ZOUkaEQXjCuFwNa7DlpB3Vo+42Tw5zBbHe5a4BY7i56TVJG5xTbS9UQyA726J91pDqg==", "integrity": "sha512-fAWpbKCyt+l1dsqSNPOs/F/dBN4Wp5CGAyxbiS5zqDwI4q3QPM+LxLU2h3GUHMIBtMGCvmsG98j5HPMkTKkvcA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@ctrl/tinycolor": "^3.4.1", "@ctrl/tinycolor": "^3.4.1",

View File

@ -1,6 +1,6 @@
{ {
"name": "wuhu-torn-helper", "name": "wuhu-torn-helper",
"version": "1.0.6", "version": "1.2.4",
"description": "芜湖助手", "description": "芜湖助手",
"scripts": { "scripts": {
"release": "cross-env NODE_ENV=production rollup -c && node build.mjs", "release": "cross-env NODE_ENV=production rollup -c && node build.mjs",
@ -17,11 +17,11 @@
"@rollup/plugin-terser": "^0.4.0", "@rollup/plugin-terser": "^0.4.0",
"@rollup/plugin-typescript": "^8.5.0", "@rollup/plugin-typescript": "^8.5.0",
"@types/jquery": "^3.5.14", "@types/jquery": "^3.5.14",
"@types/node": "^18.0.6", "@types/node": "^20.6.0",
"@vitejs/plugin-vue": "^4.0.0", "@vitejs/plugin-vue": "^4.0.0",
"@vue/tsconfig": "^0.1.3", "@vue/tsconfig": "^0.1.3",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"element-plus": "^2.3.3", "element-plus": "^2.3.10",
"just-clone": "^6.2.0", "just-clone": "^6.2.0",
"npm": "^8.19.2", "npm": "^8.19.2",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",

File diff suppressed because one or more lines are too long

View File

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

View File

@ -1,68 +1,71 @@
<div class="acc-title"> <div class="acc-title">
<span class="item-desc" style="display: inline-block;"> <span class="item-desc tt-buy">
<span class="item Alcohol" itemid="180" loaded="0"> <span class="item Alcohol" itemid="180" loaded="0">
<span class="item-plate"> <span class="item-plate">
<img alt="Bottle of Beer" class="torn-item large" src="/images/items/180/large.png"> <img alt="Bottle of Beer" class="torn-item large"
</span> src="/images/items/180/large.png"
<span class="item-hover"> 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">
<button aria-labelledby="Show info: 180-name 180-price 180-stock" class="view-info wai-btn" i-data="i_871_346_51_52" </span>
value="100"></button> <span class="item-hover">
<button aria-label="Buy: Bottle of Beer" class="buy-info wai-btn" i-data="i_922_346_51_52" value="100"></button> <button aria-labelledby="Show info: 180-name 180-price 180-stock" class="view-info wai-btn"
</span> value="100"></button>
</span> <button aria-label="Buy: Bottle of Beer" class="buy-info wai-btn" value="100"></button>
<span class="desc"> </span>
<span id="180-name" class="name t-overflow bold">Bottle of Beer</span> </span>
<span id="180-price" class="price t-gray-6" data-sell="$5">$10</span> <span class="desc">
<span id="180-stock" class="stock t-gray-6 t-overflow">Alcohol (<span class="instock">800</span> in stock)</span> <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> </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" i-data="i_1076_375_53_34" value="100">Buy<br></button>
</span>
</span>
</span>
<div class="torn-divider divider-right"></div> <div class="torn-divider divider-right"></div>
<div class="confirm-wrap" style="display: none;" tabindex="0"> <div class="confirm-wrap" tabindex="0">
<span class="confirm"> <span class="confirm">
<span> <span>
Are you sure you would like to buy Are you sure you would like to buy
</span> </span>
<span> <span>
<span class="count">100</span> <span class="count"></span>
x Bottle of Beer for x Bottle of Beer for
$<span class="total">1,000</span> $<span class="total"></span>
</span> </span>
<span class="confirm-act m-top5"> <span class="confirm-act m-top5">
<a class="wai-support yes m-right10 bold t-blue h" data-id="180" href="#" i-data="i_966_379_24_14"> <a class="wai-support yes m-right10 bold t-blue h" data-id="180" href="#">
Yes Yes
</a> </a>
<span class="no bold"> <span class="no bold">
<a class="wai-support t-blue h" href="#" i-data="i_1001_379_16_14"> <a class="wai-support t-blue h" href="#">
No No
</a> </a>
</span> </span>
</span> </span>
</span> </span>
</div> </div>
<div class="success-wrap" style="display: none;"> <div class="success-wrap">
<span class="success"><span class="t-green bold"></span> <span class="success">
<span class="confirm-act m-top5"></span></span> <span class="t-green bold">
<button aria-label="Close" class="close-icon p0 wai-btn" i-data="i_1101_344_10_11" value="100"></button> <span class="ajax-preloader"></span>
</span>
</span>
<button aria-label="Close" class="close-icon p0 wai-btn" value="100"></button>
</div> </div>
<div class="msg-wrap"> <div class="msg-wrap">
<span class="t-green bold"> <span class="t-green bold">
<span class="ajax-preloader"></span> <span class="ajax-preloader"></span>
</span> </span>
</div>
</div>
<div class="view-item-info">
<div class="item-cont">
<div class="item-wrap">
<span class="ajax-preloader m-top10 m-bottom10"></span>
</div>
</div> </div>
</div> </div>

View File

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

View File

@ -1,144 +1,160 @@
import depoHelper from "../func/module/depoHelper"; // import depoHelper from "../func/module/depoHelper";
import TravelHelper from "../func/module/travelHelper"; // import TravelHelper from "../feature/TravelHelper";
import priceWatcherHandle from "../func/module/priceWatcherHandle"; // import priceWatcherHandle from "../func/module/priceWatcherHandle";
import CompanyHelper from "./action/CompanyHelper"; // import CompanyHelper from "../feature/CompanyHelper";
import AttackHelper from "./action/AttackHelper"; // import AttackHelper from "./action/AttackHelper";
import SidebarHelper from "./action/SidebarHelper"; // import SidebarHelper from "../feature/SidebarHelper";
import CommonUtils from "./utils/CommonUtils"; // import CommonUtils from "./utils/CommonUtils";
import FetchUtils from "./utils/FetchUtils"; // import FetchUtils from "./utils/FetchUtils";
import FetchEventCallback from "./action/FetchEventCallback"; // import FetchEventCallback from "./action/FetchEventCallback";
import globVars from "../globVars"; // import globVars from "../globVars";
import TranslateNew from "./action/TranslateNew"; // import TranslateNew from "./action/TranslateNew";
import ClassName from "../container/ClassName"; // import ClassName from "../container/ClassName";
import { Injectable } from "../container/Injectable"; // import { Injectable } from "../container/Injectable";
import LocalConfigWrapper from "./LocalConfigWrapper"; // import LocalConfigWrapper from "./LocalConfigWrapper";
import Logger from "./Logger"; // import Logger from "./Logger";
import BuyBeerHelper from "./action/BuyBeerHelper"; // import BuyBeerHelper from "../feature/BuyBeerHelper";
import ModuleLoader from "./ModuleLoader"; // import ModuleLoader from "./ModuleLoader";
import TornPDAUtils from "./utils/TornPDAUtils"; // import TornPDAUtils from "./utils/TornPDAUtils";
import TravelItem from "./action/TravelItem"; // import TravelItem from "../feature/TravelItem";
import IconHelper from "./IconHelper"; // import IconHelper from "../feature/IconHelper";
import MsgWrapper from "./utils/MsgWrapper"; // import MsgWrapper from "./utils/MsgWrapper";
// import toThousands from "../func/utils/toThousands";
/** // import { WHIntervalLoader } from "../monitor/WHIntervalLoader";
* //
*/ // /**
@Injectable() // * 脚本不区分页面的通用功能入口
@ClassName('Common') // */
export class Common { // @Injectable()
// @ClassName('Common')
constructor( // export class Common {
private readonly localConfigWrapper: LocalConfigWrapper, // private readonly logger = Logger.factory(Common)
private readonly fetchEventCallback: FetchEventCallback, //
private readonly translateNew: TranslateNew, // constructor(
private readonly tornPDAUtils: TornPDAUtils, // private readonly localConfigWrapper: LocalConfigWrapper,
private readonly logger: Logger, // // private readonly fetchEventCallback: FetchEventCallback,
private readonly buyBeerHelper: BuyBeerHelper, // // private readonly translateNew: TranslateNew,
// private readonly icon: ZhongIcon, // // private readonly tornPDAUtils: TornPDAUtils,
private readonly fetchUtils: FetchUtils, // // private readonly buyBeerHelper: BuyBeerHelper,
private readonly moduleLoader: ModuleLoader, // // private readonly fetchUtils: FetchUtils,
private readonly msgWrapper: MsgWrapper, // // private readonly moduleLoader: ModuleLoader,
) { // private readonly msgWrapper: MsgWrapper,
} // ) {
// }
public resolve(mainMethod) { //
// window.setInterval(()=>this.msgWrapper.create('test',{sysNotify:true},'info'),2000); // public resolve(mainMethod) {
// // window.setInterval(()=>this.msgWrapper.create('test',{sysNotify:true},'info'),2000);
// fetch方法处理 //
globVars.responseHandlers.push( // // // fetch方法处理
(...args: any[]) => this.fetchEventCallback.responseHandler.apply(this.fetchEventCallback, args) // // globVars.responseHandlers.push(
); // // (...args: any[]) => this.fetchEventCallback.responseHandler.apply(this.fetchEventCallback, args)
// fetch方法处理-翻译 // // );
globVars.responseHandlers.push( // // // fetch方法处理-翻译
(...args: any[]) => this.translateNew.responseHandler.apply(this.translateNew, args) // // globVars.responseHandlers.push(
); // // (...args: any[]) => this.translateNew.responseHandler.apply(this.translateNew, args)
// // );
// 价格监控 //
priceWatcherHandle(this.tornPDAUtils.isPDA(), this.tornPDAUtils.APIKey); // // // 价格监控
// // priceWatcherHandle(this.tornPDAUtils.isPDA(), this.tornPDAUtils.APIKey);
// 啤酒提醒 //
if (this.localConfigWrapper.config._15Alarm) this.buyBeerHelper.start(); // // 啤酒提醒
// // if (this.localConfigWrapper.config._15Alarm) this.buyBeerHelper.start();
this.moduleLoader.push(SidebarHelper); //
this.moduleLoader.push(TravelItem); // // 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('script[src*="google"]')
* All('head script[nonce]') // * All('#gtm_tag')
*/ // * All('script[src*="chat/gonline"]')
if (document.readyState === 'interactive' && this.localConfigWrapper.config.SolveGoogleScriptPendingIssue) { // * All('head script[nonce]')
window.stop(); // */
document.open(); // // try {
let readyStateChangeHandler = () => { // // if (document.readyState === 'interactive' && this.localConfigWrapper.config.SolveGoogleScriptPendingIssue) {
this.logger.info('document.readyState', document.readyState); // // window.stop();
if (document.readyState === 'complete') { // // document.open();
document.removeEventListener('readystatechange', readyStateChangeHandler); // // let readyStateChangeHandler = () => {
mainMethod(); // // this.logger.info('document.readyState', document.readyState);
throw new Error('页面已重载'); // // if (document.readyState === 'complete') {
} // // document.removeEventListener('readystatechange', readyStateChangeHandler);
} // // mainMethod();
document.addEventListener('readystatechange', readyStateChangeHandler); // // throw new Error('页面已重载');
this.fetchUtils.fetchText(window.location.href).then(resp => { // // }
let removed = resp; // // }
[ // // document.addEventListener('readystatechange', readyStateChangeHandler);
/<script id="gtm_tag">.+?<\/script>/ms, // // this.fetchUtils.fetchText(window.location.href).then(resp => {
/<script async src="https:\/\/www\.google.+?<\/script>/ms, // // let removed = resp;
/<script nonce=".+?gtag.+?<\/script>/ms, // // [
/<script.+?google.+?\/script>/, // // /<script id="gtm_tag">.+?<\/script>/ms,
].forEach(remove => { // // /<script async src="https:\/\/www\.google.+?<\/script>/ms,
removed = removed.replace(remove, ''); // // /<script nonce=".+?gtag.+?<\/script>/ms,
}); // // /<script.+?google.+?\/script>/,
this.logger.info({ removed }); // // ].forEach(remove => {
document.write(removed); // // removed = removed.replace(remove, '');
document.close(); // // });
}); // // this.logger.info({ removed });
} // // document.write(removed);
// // document.close();
// 存钱相关 // // });
depoHelper(); // // }
// // } catch (e) {
// 飞行相关 // // this.logger.error('【解决一直转圈(加载中)的问题】错误',e)
this.moduleLoader.push(TravelHelper); // // }
//
// 战斗相关 // // // 存钱相关
this.moduleLoader.push(AttackHelper); // // try {
// // depoHelper();
// 公司助手 // // } catch (e) {
this.moduleLoader.push(CompanyHelper); // // this.logger.error('【存钱相关】错误',e)
// // }
// TODO 新菜单 //
this.moduleLoader.push(IconHelper); // // // 飞行相关
// // this.moduleLoader.push(TravelHelper);
this.moduleLoader.load().then(); //
// // 战斗相关
// 自定义CSS // // this.moduleLoader.push(AttackHelper);
if (this.localConfigWrapper.config.CustomCss) { //
this.logger.info('应用自定义CSS'); // // 公司助手
CommonUtils.addStyle(this.localConfigWrapper.config.CustomCss); // // this.moduleLoader.push(CompanyHelper);
} //
// // // 菜单
// 现金变动提醒 // // this.moduleLoader.push(IconHelper);
if (this.localConfigWrapper.config.CashChangeAlert) CommonUtils.elementReady("#user-money").then(userMoney => { //
new MutationObserver((mutations, observer) => { // // this.moduleLoader.load().then();
if (!this.localConfigWrapper.config.CashChangeAlert) { //
observer.disconnect(); // // // 自定义CSS
this.msgWrapper.create('现金变动提醒已关闭', { sysNotify: true }); // // if (this.localConfigWrapper.config.CustomCss) {
return; // // this.logger.info('应用自定义CSS');
} // // CommonUtils.addStyle(this.localConfigWrapper.config.CustomCss);
this.logger.info("现金变动提醒", mutations); // // }
mutations.forEach(item => { // //
if (item.attributeName === 'data-money') { // // // 现金变动提醒
// this.icon.updateCashView(userMoney.innerText); // // if (this.localConfigWrapper.config.CashChangeAlert) CommonUtils.elementReady("#user-money").then(userMoney => {
this.msgWrapper.create( // // let before = ''
'现金变动 ' + item.oldValue + ' ➡️ ' + userMoney.innerText, // // new MutationObserver((mutations, observer) => {
{ sysNotify: true } // // if (!this.localConfigWrapper.config.CashChangeAlert) {
); // // observer.disconnect();
} // // this.msgWrapper.create('现金变动提醒已关闭', { sysNotify: true });
}); // // return;
}).observe(userMoney, { attributes: true, attributeOldValue: true }) // // }
}); // // 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,62 +0,0 @@
import Logger, { LoggerKey } from "./Logger";
import ClassName from "../container/ClassName";
import { Injectable } from "../container/Injectable";
import CommonUtils, { CommonUtilsKey } from "./utils/CommonUtils";
import LocalConfigWrapper, { LocalConfigWrapperKey } from "./LocalConfigWrapper";
import NNB from "./handler/NNB";
import ItemPriceWatcherHandler from "./handler/ItemPriceWatcherHandler";
import { createApp } from "vue";
import FloatMenu from "../../vue/FloatMenu.vue";
import PopupWrapper, { PopupWrapperKey } from "./utils/PopupWrapper";
import InfoUtils from "./utils/InfoUtils";
import TravelItem, { TravelItemKey } from "./action/TravelItem";
import QuickGymTrain, { QuickGymTrainKey } from "./action/QuickGymTrain";
import QuickFlyBtnHandler, { QuickFlyBtnHandlerKey } from "./handler/QuickFlyBtnHandler";
import ItemHelper, { ItemHelperKey } from "./utils/ItemHelper";
import MathUtils, { MathUtilsKey } from "./utils/MathUtils";
@ClassName("IconHelper")
@Injectable()
export default class IconHelper {
private readonly _element: HTMLElement;
constructor(
private readonly logger: Logger,
private readonly commonUtils: CommonUtils,
private readonly localConfigWrapper: LocalConfigWrapper,
private readonly nnb: NNB,
private readonly itemPriceWatcherHandler: ItemPriceWatcherHandler,
private readonly popupWrapper: PopupWrapper,
private readonly infoUtils: InfoUtils,
private readonly travelItem: TravelItem,
private readonly quickGymTrain: QuickGymTrain,
private readonly quickFlyBtnHandler: QuickFlyBtnHandler,
private readonly itemHelper: ItemHelper,
private readonly mathUtils: MathUtils,
) {
this._element = document.createElement('div');
}
get element() {
return this._element;
}
init() {
this._element.id = 'WHMenu2023';
document.body.append(this._element);
let app = createApp(FloatMenu);
app.config.errorHandler = (err) => this.logger.error('[VUE错误]', err);
app.config.warnHandler = (err) => this.logger.warn('[VUE警告]', err);
app.provide(LoggerKey, this.logger);
app.provide(CommonUtilsKey, this.commonUtils);
app.provide(MathUtilsKey, this.mathUtils);
app.provide(TravelItemKey, this.travelItem);
app.provide(PopupWrapperKey, this.popupWrapper);
app.provide(LocalConfigWrapperKey, this.localConfigWrapper);
app.provide(QuickGymTrainKey, this.quickGymTrain);
app.provide(QuickFlyBtnHandlerKey, this.quickFlyBtnHandler);
app.provide(ItemHelperKey, this.itemHelper);
app.mount(this._element);
}
}

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;
}
}

View File

@ -2,12 +2,9 @@ import ClassName from "../container/ClassName";
import { Injectable } from "../container/Injectable"; import { Injectable } from "../container/Injectable";
@ClassName('WuhuBase') @ClassName('Interrupt')
@Injectable() @Injectable()
export default class WuhuBase { export default class Interrupt {
constructor() {
}
public conditionInterrupt() { public conditionInterrupt() {
let title: HTMLElement | { innerText: string } = (document.querySelector('#skip-to-content') || let title: HTMLElement | { innerText: string } = (document.querySelector('#skip-to-content') ||
@ -17,6 +14,6 @@ export default class WuhuBase {
title.innerText.toLowerCase().includes('please validate') || title.innerText.toLowerCase().includes('please validate') ||
document.querySelector('div.container div.cf .iAmUnderAttack') !== null document.querySelector('div.container div.cf .iAmUnderAttack') !== null
); );
if (condition) throw '芜湖'; if (condition) throw new Error('芜湖');
} }
} }

View File

@ -1,4 +1,5 @@
import ClassName from "../container/ClassName"; import "reflect-metadata";
import ClassName, { GetClassName } from "../container/ClassName";
import { Injectable } from "../container/Injectable"; import { Injectable } from "../container/Injectable";
import Log from "./Log"; import Log from "./Log";
import { InjectionKey } from "vue"; import { InjectionKey } from "vue";
@ -29,6 +30,23 @@ export default class Logger {
getTime() { getTime() {
return Log.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>; export const LoggerKey = Symbol() as InjectionKey<Logger>;

View File

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

View File

@ -7,11 +7,11 @@ import ADD_BEER_HEAD_HTML from "../../static/html/buyBeer/add_beer_head.html";
import QUICK_CRIMES_HTML from "../../static/html/quick_crimes.html"; import QUICK_CRIMES_HTML from "../../static/html/quick_crimes.html";
import RW_RIDER_HTML from "../../static/html/rw_rider.html"; import RW_RIDER_HTML from "../../static/html/rw_rider.html";
import christmasTownHelper from "../func/module/christmasTownHelper"; import christmasTownHelper from "../func/module/christmasTownHelper";
import LotteryHelper from "./action/LotteryHelper"; import LotteryHelper from "../feature/LotteryHelper";
import TornStyleBlock from "./utils/TornStyleBlock"; import TornStyleBlock from "./utils/TornStyleBlock";
import PTHelper from "./action/PTHelper"; import PTHelper from "./action/PTHelper";
import StackHelper from "./action/StackHelper"; import StackHelper from "../feature/StackHelper";
import BuyBeerHelper from "./action/BuyBeerHelper"; import BuyBeerHelper from "../feature/BuyBeerHelper";
import XZMZ from "./action/XZMZ"; import XZMZ from "./action/XZMZ";
import ProfileHelper from "./action/ProfileHelper"; import ProfileHelper from "./action/ProfileHelper";
import SearchHelper from "./action/SearchHelper"; import SearchHelper from "./action/SearchHelper";
@ -48,14 +48,14 @@ export default class UrlRouter {
// 捡垃圾助手 // 捡垃圾助手
if (href.includes('city.php') && this.localConfigWrapper.config.cityFinder) { if (href.includes('city.php') && this.localConfigWrapper.config.cityFinder) {
let _base = new TornStyleBlock('芜湖助手').insert2Dom(); let _base = new TornStyleBlock('芜湖助手').insert2Dom();
let reloadSwitch = new TornStyleSwitch('解决一直转圈(加载中)的问题'); // let reloadSwitch = new TornStyleSwitch('解决一直转圈(加载中)的问题');
reloadSwitch.getInput().checked = this.localConfigWrapper.config.SolveGoogleScriptPendingIssue; // reloadSwitch.getInput().checked = this.localConfigWrapper.config.SolveGoogleScriptPendingIssue;
_base.append(reloadSwitch.getBase()).insert2Dom(); // _base.append(reloadSwitch.getBase()).insert2Dom();
reloadSwitch.getInput().addEventListener('change', () => { // reloadSwitch.getInput().addEventListener('change', () => {
if (reloadSwitch.getInput().checked) window.location.replace(window.location.href); // if (reloadSwitch.getInput().checked) window.location.replace(window.location.href);
this.localConfigWrapper.config.SolveGoogleScriptPendingIssue = reloadSwitch.getInput().checked; // this.localConfigWrapper.config.SolveGoogleScriptPendingIssue = reloadSwitch.getInput().checked;
// WuhuConfig.set('SolveGoogleScriptPendingIssue', reloadSwitch.getInput().checked, true); // // WuhuConfig.set('SolveGoogleScriptPendingIssue', reloadSwitch.getInput().checked, true);
}); // });
_base.append(document.createElement('br')); _base.append(document.createElement('br'));

View File

@ -1,181 +0,0 @@
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";
@Injectable()
@ClassName('WuHuTornHelper')
export default class WuHuTornHelper {
constructor(
// private readonly travelItem: TravelItem,
private readonly global: Global,
private readonly logger: Logger,
private readonly infoUtils: InfoUtils,
private readonly commonUtils: CommonUtils,
) {
}
public init() {
// this.logger.info('WuHuTornHelper初始化');
// WuhuBase.glob = this.global;
let glob = this.global;
// glob.fStock = this.travelItem;
// 请求通知权限
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;
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");
return this;
}
}

View File

@ -5,7 +5,7 @@ import CommonUtils from "./utils/CommonUtils";
import * as EVENTS from "../../static/json/event.json"; import * as EVENTS from "../../static/json/event.json";
import * as FEST from "../../static/json/fest.json"; import * as FEST from "../../static/json/fest.json";
import Popup from "./utils/Popup"; import Popup from "./utils/Popup";
import TravelItem from "./action/TravelItem"; import TravelItem from "../feature/TravelItem";
import ZHONG_MENU_HTML from "../../static/html/zhong/zhong_menu.html"; import ZHONG_MENU_HTML from "../../static/html/zhong/zhong_menu.html";
import ZHONG_UPDATE_HTML from "../../static/html/zhong/zhong_update.html"; import ZHONG_UPDATE_HTML from "../../static/html/zhong/zhong_update.html";
import ZHONG_LOOT_HTML from "../../static/html/zhong/zhong_loot.html"; import ZHONG_LOOT_HTML from "../../static/html/zhong/zhong_loot.html";
@ -15,7 +15,7 @@ import QuickFlyBtnHandler from "./handler/QuickFlyBtnHandler";
import NNB from "./handler/NNB"; import NNB from "./handler/NNB";
import QuickLinksHandler from "./handler/QuickLinksHandler"; import QuickLinksHandler from "./handler/QuickLinksHandler";
import ItemPriceWatcherHandler from "./handler/ItemPriceWatcherHandler"; import ItemPriceWatcherHandler from "./handler/ItemPriceWatcherHandler";
import ChangeLogHandler from "./handler/ChangeLogHandler"; // import ChangeLogHandler from "./handler/ChangeLogHandler";
import ItemPriceHandler from "./handler/ItemPriceHandler"; import ItemPriceHandler from "./handler/ItemPriceHandler";
import SettingsHandler from "./handler/SettingsHandler"; import SettingsHandler from "./handler/SettingsHandler";
import { MENU_ITEM_TYPE } from "../interface/MenuItem"; import { MENU_ITEM_TYPE } from "../interface/MenuItem";
@ -417,7 +417,7 @@ export default class ZhongIcon {
// 物品查价 // 物品查价
list.push(ItemPriceHandler); list.push(ItemPriceHandler);
// 更新历史 // 更新历史
list.push(ChangeLogHandler); // list.push(ChangeLogHandler);
// 助手设置 // 助手设置
list.push(SettingsHandler); list.push(SettingsHandler);
// 测试 // 测试

View File

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

View File

@ -5,6 +5,10 @@ import ResponseInject from "../../interface/ResponseInject";
import { Injectable } from "../../container/Injectable"; import { Injectable } from "../../container/Injectable";
import ClassName from "../../container/ClassName"; import ClassName from "../../container/ClassName";
import LocalConfigWrapper from "../LocalConfigWrapper"; 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 * fetch
@ -14,9 +18,14 @@ import LocalConfigWrapper from "../LocalConfigWrapper";
export default class FetchEventCallback extends Provider implements ResponseInject { export default class FetchEventCallback extends Provider implements ResponseInject {
className = "FetchEventCallback"; className = "FetchEventCallback";
newNode = document.createElement('div')
bsEstNode = document.createElement('div')
constructor( constructor(
private readonly localConfigWrapper: LocalConfigWrapper, private readonly localConfigWrapper: LocalConfigWrapper,
private readonly commonUtils: CommonUtils, private readonly commonUtils: CommonUtils,
private readonly logger: Logger,
private readonly msgWrapper: MsgWrapper,
) { ) {
super(); super();
} }
@ -28,15 +37,38 @@ export default class FetchEventCallback extends Provider implements ResponseInje
*/ */
public responseHandler(url: string, response) { public responseHandler(url: string, response) {
// mini profile 中添加上次动作 // mini profile 中添加上次动作
if (url.includes('profiles.php?step=getUserNameContextMenu') && this.localConfigWrapper.config.ShowMiniProfLastAct) { if (url.startsWith('/page.php?sid=UserMiniProfile&userID')) {
window.setTimeout(async () => { window.setTimeout(async () => {
let cont = CommonUtils.querySelector('[class*=profile-mini-_userProfileWrapper___]'); let cont = CommonUtils.querySelector('[class*=profile-mini-_userProfileWrapper___]');
let resp: MiniProfile = response.json as MiniProfile; let resp: MiniProfile = response.json as MiniProfile;
let newNode = document.createElement('div'); if (this.localConfigWrapper.config.ShowMiniProfLastAct) {
this.logger.info({ resp })
let formatted = this.commonUtils.secondsFormat(resp.user.lastAction.seconds); let formatted = this.commonUtils.secondsFormat(resp.user.lastAction.seconds);
newNode.innerText = '上次动作: ' + formatted; (await cont).append(this.newNode);
(await cont).append(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); }, 0);
} }
} }

View File

@ -1,25 +1,47 @@
import TornStyleBlock from "../utils/TornStyleBlock"; import TornStyleBlock from "../utils/TornStyleBlock";
import Alert from "../utils/Alert";
import TornStyleSwitch from "../utils/TornStyleSwitch"; import TornStyleSwitch from "../utils/TornStyleSwitch";
import { Injectable } from "../../container/Injectable"; import { Injectable } from "../../container/Injectable";
import ClassName from "../../container/ClassName"; import ClassName from "../../container/ClassName";
import LocalConfigWrapper from "../LocalConfigWrapper"; import LocalConfigWrapper from "../LocalConfigWrapper";
import IFeature from "../../man/IFeature";
import MsgWrapper from "../utils/MsgWrapper";
@Injectable() @Injectable()
@ClassName('PTHelper') @ClassName('PTHelper')
export default class PTHelper { export default class PTHelper implements IFeature {
private readonly observer; private observer: MutationObserver;
private readonly usersPointSell; private usersPointSell: HTMLDivElement;
constructor( constructor(
private readonly localConfigWrapper: LocalConfigWrapper, 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() {
this.observer = new MutationObserver(e => { this.observer = new MutationObserver(e => {
for (const t of e) { for (const t of e) {
t.addedNodes.forEach(e => 'LI' === (e as HTMLElement).tagName && this.removeConfirm(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 block = new TornStyleBlock('PT一键购买').insert2Dom();
let switcher = new TornStyleSwitch('开启'); let switcher = new TornStyleSwitch('开启');
@ -27,7 +49,7 @@ export default class PTHelper {
let toggle = switcher.getInput(); let toggle = switcher.getInput();
toggle.checked = this.localConfigWrapper.config.ptQuickBuy; toggle.checked = this.localConfigWrapper.config.ptQuickBuy;
if (toggle.checked) { if (toggle.checked) {
new Alert('一键购买已开启'); this.msgWrapper.create('一键购买已开启');
for (const index in this.usersPointSell.children) { for (const index in this.usersPointSell.children) {
'LI' === this.usersPointSell.children[index].tagName && this.removeConfirm(this.usersPointSell.children[index]) 'LI' === this.usersPointSell.children[index].tagName && this.removeConfirm(this.usersPointSell.children[index])
} }
@ -40,13 +62,13 @@ export default class PTHelper {
'LI' === this.usersPointSell.children[index].tagName && this.removeConfirm(this.usersPointSell.children[index]) 'LI' === this.usersPointSell.children[index].tagName && this.removeConfirm(this.usersPointSell.children[index])
} }
this.observer.observe(this.usersPointSell, { childList: true }); this.observer.observe(this.usersPointSell, { childList: true });
new Alert('一键购买已开启'); this.msgWrapper.create('一键购买已开启');
} else { } else {
for (const index in this.usersPointSell.children) { for (const index in this.usersPointSell.children) {
'LI' === this.usersPointSell.children[index].tagName && this.rollbackConfirm(this.usersPointSell.children[index]) 'LI' === this.usersPointSell.children[index].tagName && this.rollbackConfirm(this.usersPointSell.children[index])
} }
this.observer.disconnect(); this.observer.disconnect();
new Alert('一键购买已关闭'); this.msgWrapper.create('一键购买已关闭');
} }
}); });
} }

View File

@ -8,21 +8,46 @@ import ClassName from "../../container/ClassName";
import { Injectable } from "../../container/Injectable"; import { Injectable } from "../../container/Injectable";
import LocalConfigWrapper from "../LocalConfigWrapper"; import LocalConfigWrapper from "../LocalConfigWrapper";
import Logger from "../Logger"; 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";
@ClassName('ProfileHelper') @ClassName('ProfileHelper')
@Injectable() @Injectable()
export default class ProfileHelper implements ResponseInject { export default class ProfileHelper implements ResponseInject, IFeature {
private block: TornStyleBlock;
private readonly block;
// 曾用名已检测过标记
private task = true;
constructor( constructor(
private readonly localConfigWrapper: LocalConfigWrapper, private readonly localConfigWrapper: LocalConfigWrapper,
private readonly commonUtils: CommonUtils, private readonly commonUtils: CommonUtils,
private readonly logger: Logger, 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() {
CommonUtils.addStyle('body.wh-hide_profile_img .profile-image a.profile-image-wrapper .img-wrap img{display:none;}'); 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'); let id = (new URL(window.location.href)).searchParams.get('XID');
@ -36,6 +61,7 @@ export default class ProfileHelper implements ResponseInject {
} }
this.block = new TornStyleBlock('芜湖助手').insert2Dom(); this.block = new TornStyleBlock('芜湖助手').insert2Dom();
// 隐藏头像 // 隐藏头像
try {
let hideImgSwitch = new TornStyleSwitch('隐藏头像', this.localConfigWrapper.config.HideProfileImg); let hideImgSwitch = new TornStyleSwitch('隐藏头像', this.localConfigWrapper.config.HideProfileImg);
this.block.append(hideImgSwitch.getBase()); this.block.append(hideImgSwitch.getBase());
hideImgSwitch.getInput().addEventListener('change', () => { hideImgSwitch.getInput().addEventListener('change', () => {
@ -45,21 +71,80 @@ export default class ProfileHelper implements ResponseInject {
if (this.localConfigWrapper.config.ShowNameHistory) { if (this.localConfigWrapper.config.ShowNameHistory) {
globVars.responseHandlers.push((...args: any[]) => this.responseHandler.apply(this, args)); 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 }) { responseHandler(url: string, body: { json: unknown; text: string; isModified: boolean }) {
if (url.includes('profiles.php?step=getProfileData') && this.task) { if (url.includes('profiles.php?step=getProfileData') && this.task) {
// 曾用名 // 曾用名
let nameHistoryNode; const nameHistoryNode = document.createElement('p');
nameHistoryNode = document.createElement('p');
nameHistoryNode.innerHTML = '曾用名:'; nameHistoryNode.innerHTML = '曾用名:';
this.block.append(nameHistoryNode); this.block.append(nameHistoryNode);
let resp = body.json as IUserProfileData; let resp = body.json as IUserProfileData;
if (resp.userInformation.previousAliases.length > 0) { if (resp.userInformation.previousAliases.length > 0) { // 曾用名列表
resp.userInformation.previousAliases.forEach(item => nameHistoryNode.innerHTML += item + ' '); resp.userInformation.previousAliases.forEach(item => nameHistoryNode.innerHTML += item + ' ');
} else { } else {
nameHistoryNode.innerHTML += '暂无'; 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; this.task = false;
} }
} }

View File

@ -4,10 +4,26 @@ import CommonUtils from "../utils/CommonUtils";
import ClassName from "../../container/ClassName"; import ClassName from "../../container/ClassName";
import { Injectable } from "../../container/Injectable"; import { Injectable } from "../../container/Injectable";
import LocalConfigWrapper from "../LocalConfigWrapper"; import LocalConfigWrapper from "../LocalConfigWrapper";
import IFeature from "../../man/IFeature";
@ClassName('SearchHelper') @ClassName('SearchHelper')
@Injectable() @Injectable()
export default class SearchHelper { export default class SearchHelper implements IFeature {
description(): string {
return "搜索助手";
}
iStart(): void | Promise<void> {
this.init()
}
urlExcludes(): RegExp[] {
return [];
}
urlIncludes(): RegExp[] {
return [/page\.php\?sid=UserList/];
}
constructor( constructor(
private readonly localConfigWrapper: LocalConfigWrapper, private readonly localConfigWrapper: LocalConfigWrapper,

View File

@ -6,13 +6,29 @@ import MathUtils from "../utils/MathUtils";
import ClassName from "../../container/ClassName"; import ClassName from "../../container/ClassName";
import { Injectable } from "../../container/Injectable"; import { Injectable } from "../../container/Injectable";
import Logger from "../Logger"; import Logger from "../Logger";
import IFeature from "../../man/IFeature";
/** /**
* *
*/ */
@ClassName("SlotsHelper") @ClassName("SlotsHelper")
@Injectable() @Injectable()
export default class SlotsHelper { 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( constructor(
private readonly mathUtils: MathUtils, private readonly mathUtils: MathUtils,

View File

@ -7,6 +7,7 @@ import MathUtils from "../utils/MathUtils";
import FetchUtils from "../utils/FetchUtils"; import FetchUtils from "../utils/FetchUtils";
import ClassName from "../../container/ClassName"; import ClassName from "../../container/ClassName";
import { Injectable } from "../../container/Injectable"; import { Injectable } from "../../container/Injectable";
import IFeature from "../../man/IFeature";
/** /**
* *
@ -14,8 +15,35 @@ import { Injectable } from "../../container/Injectable";
*/ */
@ClassName('XZMZ') @ClassName('XZMZ')
@Injectable() @Injectable()
export default class XZMZ { export default class XZMZ implements IFeature {
className = 'XZMZ'; 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 mainRoleContainer: HTMLElement;
private IDList: number[]; private IDList: number[];
private btn: HTMLButtonElement; private btn: HTMLButtonElement;

View File

@ -105,6 +105,16 @@ class DefaultConfigType {
// 自定义css // 自定义css
@Notified() @Notified()
CustomCss = ''; CustomCss = '';
monitorOn = ['drugCDMonitor']
drugCDMonitorInterval = 60000
// mini profile显示bs估算
@Notified()
isBSEstMiniProfOn = false
// profile页面显示bs估算
@Notified()
isBSEstProfOn = true
} }
export type Config = DefaultConfigType; export type Config = DefaultConfigType;

View File

@ -3,11 +3,8 @@ import Alert from "../utils/Alert";
import DialogMsgBox from "../utils/DialogMsgBox"; import DialogMsgBox from "../utils/DialogMsgBox";
import CommonUtils from "../utils/CommonUtils"; import CommonUtils from "../utils/CommonUtils";
import { MenuItemConfig } from "../ZhongIcon"; import { MenuItemConfig } from "../ZhongIcon";
import IFrameCrimeHandler from "./IFrameCrimeHandler";
import loadGS from "../../func/module/loadGS";
import ClassName from "../../container/ClassName"; import ClassName from "../../container/ClassName";
import { Injectable } from "../../container/Injectable"; import { Injectable } from "../../container/Injectable";
import { Container } from "../../container/Container";
@ClassName('AdditionalSettingsHandler') @ClassName('AdditionalSettingsHandler')
@Injectable() @Injectable()
@ -20,26 +17,6 @@ export default class AdditionalSettingsHandler {
public show(): void { public show(): void {
let pop = new Popup('', '更多设定'); let pop = new Popup('', '更多设定');
// let insertHtml = '<p><button class="torn-btn">清空设置</button></p><p><button class="torn-btn">通知权限</button></p><p><button class="torn-btn">外部数据权限</button></p>';
// pop.getElement().insertAdjacentHTML('beforeend', insertHtml);
// let [btn1, btn2, btn3] = Array.from(pop.getElement().querySelectorAll('button'));
// btn1.addEventListener('click', () => {
// new DialogMsgBox('将清空所有芜湖助手相关设置并刷新页面,确定?', {
// callback: () => {
// localStorage.removeItem('wh_trv_alarm');
// localStorage.removeItem('wh_trans_settings');
// localStorage.removeItem('whuuid');
// localStorage.removeItem('wh-gs-storage');
// localStorage.removeItem('WHTEST');
// new Alert('已清空,刷新页面');
// window.location.reload();
// }
// });
// });
// btn2.addEventListener('click', () => {
// });
// btn3.addEventListener('click', () => {
// });
let menuList: MenuItemConfig[] = [ let menuList: MenuItemConfig[] = [
{ {
@ -65,20 +42,6 @@ export default class AdditionalSettingsHandler {
domType: 'button', domId: '', domText: '第三方API通信权限', clickFunc() { domType: 'button', domId: '', domText: '第三方API通信权限', clickFunc() {
} }
}, },
{
domType: 'button', domId: '', domText: '小窗犯罪', clickFunc() {
Container.factory(IFrameCrimeHandler).handle()
}
},
{
domType: 'button',
domId: '',
domText: '飞贼小助手',
tip: '加载从PC端移植的伞佬的油猴版飞贼小助手',
clickFunc: () => {
loadGS(this.commonUtils.getScriptEngine())
}
},
]; ];
menuList.forEach(i => pop.element.append(this.commonUtils.elemGenerator(i, pop.element))); menuList.forEach(i => pop.element.append(this.commonUtils.elemGenerator(i, pop.element)));
} }

View File

@ -1,67 +1,70 @@
import Popup from "../utils/Popup"; // import Popup from "../utils/Popup";
import CommonUtils from "../utils/CommonUtils"; // import CommonUtils from "../utils/CommonUtils";
import MDUtils from "../utils/MDUtils"; // import MDUtils from "../utils/MDUtils";
import { MENU_ITEM_TYPE } from "../../interface/MenuItem"; // import { MENU_ITEM_TYPE } from "../../interface/MenuItem";
import Provider from "../provider/Provider"; // import Provider from "../provider/Provider";
import ClassName from "../../container/ClassName"; // import ClassName from "../../container/ClassName";
import { Injectable } from "../../container/Injectable"; // import { Injectable } from "../../container/Injectable";
import { Container } from "../../container/Container"; // import { Container } from "../../container/Container";
import Logger from "../Logger"; // import Logger from "../Logger";
//
@ClassName('ChangeLogHandler') // /**
@Injectable() // * @deprecated
export class ChangeLogHandler extends Provider { // */
constructor( // @ClassName('ChangeLogHandler')
private readonly mdUtils: MDUtils, // @Injectable()
private readonly logger: Logger, // export class ChangeLogHandler extends Provider {
) { // constructor(
super(); // private readonly mdUtils: MDUtils,
} // private readonly logger: Logger,
// ) {
public show(): void { // super();
let popup = new Popup( // }
'更新历史:<br/><a target="_blank" href="https://gitlab.com/JJins/wuhu-torn-helper/-/blob/dev/CHANGELOG.md">https://gitlab.com/JJins/wuhu-torn-helper/-/blob/dev/CHANGELOG.md</a><br/>', //
'更新历史' // public show(): void {
).element; // let popup = new Popup(
popup.classList.add('wh-changeLog'); // '更新历史:<br/><a target="_blank" href="https://gitlab.com/JJins/wuhu-torn-helper/-/blob/dev/CHANGELOG.md">https://gitlab.com/JJins/wuhu-torn-helper/-/blob/dev/CHANGELOG.md</a><br/>',
let progressBar = document.createElement('div'); // '更新历史'
progressBar.style.height = '2px'; // ).element;
progressBar.style.width = '1%'; // popup.classList.add('wh-changeLog');
progressBar.style.backgroundColor = 'red'; // let progressBar = document.createElement('div');
let progressText = document.createElement('p'); // progressBar.style.height = '2px';
progressText.innerText = '加载更新文件……'; // progressBar.style.width = '1%';
progressText.style.textAlign = 'center'; // progressBar.style.backgroundColor = 'red';
let style = document.createElement('style'); // let progressText = document.createElement('p');
style.innerHTML = `.wh-changeLog h2,.wh-changeLog h3,.wh-changeLog h4 {margin:8px 0;}.wh-changeLog li{list-style: inside;}`; // progressText.innerText = '加载更新文件……';
// progressText.style.textAlign = 'center';
popup.append(progressBar, progressText, style); // let style = document.createElement('style');
// style.innerHTML = `.wh-changeLog h2,.wh-changeLog h3,.wh-changeLog h4 {margin:8px 0;}.wh-changeLog li{list-style: inside;}`;
CommonUtils //
.COFetch('https://gitlab.com/JJins/wuhu-torn-helper/-/raw/dev/CHANGELOG.md?' + performance.now()) // popup.append(progressBar, progressText, style);
.then(update => { //
progressBar.style.width = '60%'; // CommonUtils
progressText.innerText = '解析中……'; // .COFetch('https://gitlab.com/JJins/wuhu-torn-helper/-/raw/dev/CHANGELOG.md?' + performance.now())
let md = this.mdUtils.parse(update); // .then(update => {
popup.append(md); // progressBar.style.width = '60%';
progressBar.style.width = '100%'; // progressText.innerText = '解析中……';
progressText.innerText = '加载完成'; // let md = this.mdUtils.parse(update);
// popup.append(md);
window.setTimeout(() => { // progressBar.style.width = '100%';
progressBar.remove(); // progressText.innerText = '加载完成';
progressText.remove() //
}, 3000); // window.setTimeout(() => {
}) // progressBar.remove();
.catch(e => { // progressText.remove()
this.logger.error(e); // }, 3000);
progressBar.remove(); // })
progressText.innerText = '无法加载'; // .catch(e => {
}); // this.logger.error(e);
} // progressBar.remove();
// progressText.innerText = '无法加载';
} // });
// }
export default { //
domType: MENU_ITEM_TYPE.BUTTON, // }
domText: '🐞 更新历史', //
clickFunc: () => Container.factory(ChangeLogHandler).show() // export default {
}; // domType: MENU_ITEM_TYPE.BUTTON,
// domText: '🐞 更新历史',
// clickFunc: () => Container.factory(ChangeLogHandler).show()
// };

View File

@ -1,97 +1,97 @@
import CommonUtils from "../utils/CommonUtils"; // import CommonUtils from "../utils/CommonUtils";
import Popup from "../utils/Popup"; // import Popup from "../utils/Popup";
import QUICK_CRIMES_HTML from "../../../static/html/quick_crimes.html"; // import QUICK_CRIMES_HTML from "../../../static/html/quick_crimes.html";
import ClassName from "../../container/ClassName"; // import ClassName from "../../container/ClassName";
import { Injectable } from "../../container/Injectable"; // import { Injectable } from "../../container/Injectable";
//
@ClassName('IFrameCrimeHandler') // @ClassName('IFrameCrimeHandler')
@Injectable() // @Injectable()
export default class IFrameCrimeHandler { // export default class IFrameCrimeHandler {
//
public handle(): void { // public handle(): void {
// 弹出小窗口 // // 弹出小窗口
const ifHTML = `<iframe src="/crimes.php?step=main" style="width:100%;max-width: 450px;margin: 0 auto;display: none;height: 340px;"></iframe>`; // const ifHTML = `<iframe src="/crimes.php?step=main" style="width:100%;max-width: 450px;margin: 0 auto;display: none;height: 340px;"></iframe>`;
const popup_insert = `<p>加载中请稍后${ CommonUtils.loading_gif_html() }</p><div id="wh-quick-crime-if-container"></div>`; // const popup_insert = `<p>加载中请稍后${ CommonUtils.loading_gif_html() }</p><div id="wh-quick-crime-if-container"></div>`;
const $popup = new Popup(popup_insert, '小窗快速犯罪').getElement(); // const $popup = new Popup(popup_insert, '小窗快速犯罪').getElement();
// 运行状态node // // 运行状态node
let loading_node = $popup.querySelector('p:first-of-type'); // let loading_node = $popup.querySelector('p:first-of-type');
// if容器 // // if容器
const if_cont = $popup.querySelector('#wh-quick-crime-if-container'); // const if_cont = $popup.querySelector('#wh-quick-crime-if-container');
if_cont.innerHTML = ifHTML; // if_cont.innerHTML = ifHTML;
//
// if内未加载脚本时插入的快捷crime node // // if内未加载脚本时插入的快捷crime node
const mobile_prepend_node = document.createElement('div'); // const mobile_prepend_node = document.createElement('div');
mobile_prepend_node.classList.add('wh-translate'); // mobile_prepend_node.classList.add('wh-translate');
mobile_prepend_node.innerHTML = QUICK_CRIMES_HTML; // mobile_prepend_node.innerHTML = QUICK_CRIMES_HTML;
//
// if对象加载后运行 // // if对象加载后运行
let cIframe = $popup.querySelector('iframe'); // let cIframe = $popup.querySelector('iframe');
//
// 加载状态 // // 加载状态
const if_onload_func = () => { // const if_onload_func = () => {
// if内部文档对象 // // if内部文档对象
const ifDocu = cIframe.contentWindow.document; // const ifDocu = cIframe.contentWindow.document;
// 内部插件运行flag // // 内部插件运行flag
const ifWH = cIframe.contentWindow.WHTRANS; // const ifWH = cIframe.contentWindow.WHTRANS;
// 文档加载完成后移除 // // 文档加载完成后移除
if (!!loading_node) loading_node.remove(); // if (!!loading_node) loading_node.remove();
// 文档加载完成后才显示if // // 文档加载完成后才显示if
cIframe.style.display = 'block'; // cIframe.style.display = 'block';
// 验证码flag // // 验证码flag
const isValidate = ifDocu.querySelector('h4#skip-to-content').innerText.toLowerCase().includes('validate'); // const isValidate = ifDocu.querySelector('h4#skip-to-content').innerText.toLowerCase().includes('validate');
// 如果iframe内部未运行脚本 // // 如果iframe内部未运行脚本
if (ifWH === undefined) { // if (ifWH === undefined) {
// 隐藏顶部 // // 隐藏顶部
CommonUtils.elementReady('#header-root', ifDocu).then(e => e.style.display = 'none'); // CommonUtils.elementReady('#header-root', ifDocu).then(e => e.style.display = 'none');
// 隐藏4条 // // 隐藏4条
CommonUtils.elementReady('#sidebarroot', ifDocu).then(e => e.style.display = 'none'); // CommonUtils.elementReady('#sidebarroot', ifDocu).then(e => e.style.display = 'none');
// 隐藏聊天 // // 隐藏聊天
CommonUtils.elementReady('#chatRoot', ifDocu).then(e => e.style.display = 'none'); // CommonUtils.elementReady('#chatRoot', ifDocu).then(e => e.style.display = 'none');
// 非验证码页面隐藏滚动条 // // 非验证码页面隐藏滚动条
if (!isValidate) ifDocu.body.style.overflow = 'hidden'; // if (!isValidate) ifDocu.body.style.overflow = 'hidden';
// 调整容器位置 // // 调整容器位置
CommonUtils.elementReady('.content-wrapper', ifDocu).then(elem => { // CommonUtils.elementReady('.content-wrapper', ifDocu).then(elem => {
// 加入 // // 加入
elem.prepend(mobile_prepend_node); // elem.prepend(mobile_prepend_node);
elem.style.margin = '0px'; // elem.style.margin = '0px';
elem.style.position = 'absolute'; // elem.style.position = 'absolute';
elem.style.top = '-35px'; // elem.style.top = '-35px';
new MutationObserver((m, o) => { // new MutationObserver((m, o) => {
o.disconnect(); // o.disconnect();
if (!elem.querySelector('.wh-translate')) elem.prepend(mobile_prepend_node); // if (!elem.querySelector('.wh-translate')) elem.prepend(mobile_prepend_node);
o.observe(elem, { childList: true, subtree: true }); // o.observe(elem, { childList: true, subtree: true });
}) // })
.observe(elem, { childList: true, subtree: true }); // .observe(elem, { childList: true, subtree: true });
}); // });
// 隐藏返回顶部按钮 // // 隐藏返回顶部按钮
CommonUtils.elementReady('#go-to-top-btn button', ifDocu).then(e => e.style.display = 'none'); // CommonUtils.elementReady('#go-to-top-btn button', ifDocu).then(e => e.style.display = 'none');
} // }
}; // };
cIframe.onload = if_onload_func; // cIframe.onload = if_onload_func;
//
// 超时判断 // // 超时判断
let time_counter = 0; // let time_counter = 0;
let time_out_id = window.setInterval(() => { // let time_out_id = window.setInterval(() => {
loading_node = $popup.querySelector('p:first-of-type'); // loading_node = $popup.querySelector('p:first-of-type');
if (!loading_node) { // if (!loading_node) {
clearInterval(time_out_id); // clearInterval(time_out_id);
time_out_id = undefined; // time_out_id = undefined;
return; // return;
} // }
time_counter++; // time_counter++;
if (time_counter > 0 && !loading_node.querySelector('button')) { // if (time_counter > 0 && !loading_node.querySelector('button')) {
const reload_btn = document.createElement('button'); // const reload_btn = document.createElement('button');
reload_btn.innerHTML = '重新加载'; // reload_btn.innerHTML = '重新加载';
reload_btn.onclick = () => { // reload_btn.onclick = () => {
reload_btn.remove(); // reload_btn.remove();
time_counter = 0; // time_counter = 0;
if_cont.innerHTML = null; // if_cont.innerHTML = null;
if_cont.innerHTML = ifHTML; // if_cont.innerHTML = ifHTML;
cIframe = $popup.querySelector('iframe'); // cIframe = $popup.querySelector('iframe');
cIframe.onload = if_onload_func; // cIframe.onload = if_onload_func;
}; // };
loading_node.append(reload_btn); // loading_node.append(reload_btn);
} // }
}, 1000); // }, 1000);
} // }
} // }

View File

@ -2,7 +2,7 @@ import CommonUtils from "../utils/CommonUtils";
import QUICK_FLY_CSS from "../../../static/css/quick_fly.module.css"; import QUICK_FLY_CSS from "../../../static/css/quick_fly.module.css";
import QUICK_FLY_HTML from "../../../static/html/quick_fly.html"; import QUICK_FLY_HTML from "../../../static/html/quick_fly.html";
import Alert from "../utils/Alert"; import Alert from "../utils/Alert";
import TravelItem from "../action/TravelItem"; import TravelItem from "../../feature/TravelItem";
import ClassName from "../../container/ClassName"; import ClassName from "../../container/ClassName";
import { Injectable } from "../../container/Injectable"; import { Injectable } from "../../container/Injectable";
import Logger from "../Logger"; import Logger from "../Logger";
@ -123,22 +123,31 @@ export default class QuickFlyBtnHandler {
public async directFly(destIndex: number, typeIndex: number) { public async directFly(destIndex: number, typeIndex: number) {
// 获取key // 获取key
let key; // if(false){
// let key;
// try {
// const resp = await (await fetch('/travelagency.php')).text();
// key = resp.match(/data-key="([0-9]+)"/)[1];
// } catch (e) {
// this.msgWrapper.create('起飞参数获取失败', {}, 'error');
// this.logger.error(e.stack);
// throw new Error('起飞参数获取失败');
// }
// }
let msg: string;
try { try {
const resp = await (await fetch('/travelagency.php')).text(); msg = await this.netHighLvlWrapper.doTravelFly(QuickFlyBtnHandler.getDestId(destIndex), null, ['standard', 'airstrip', 'private', 'business'][typeIndex])
key = resp.match(/data-key="([0-9]+)"/)[1]; const response = JSON.parse(msg);
} catch (e) { if (!response.success) {
this.msgWrapper.create('起飞参数获取失败', {}, 'error'); this.msgWrapper.create('起飞失败 ' + response.error, {}, 'error');
this.logger.error(e.stack); this.logger.error('起飞失败 ' + response.error, response.err);
throw new Error('起飞参数获取失败'); throw new Error('起飞失败 ' + response.error);
} }
let msg; console.log(msg);
try {
msg = this.netHighLvlWrapper.doTravelFly(QuickFlyBtnHandler.getDestId(destIndex), key, ['standard', 'airstrip', 'private', 'business'][typeIndex])
} catch (e) { } catch (e) {
this.msgWrapper.create(msg, {}, 'error'); this.msgWrapper.create('起飞时出现错误 ' + e.message, {}, 'error');
this.logger.error(e.stack); this.logger.error(e.stack);
throw new Error('起飞时出现错误'); throw new Error('起飞时出现错误 ' + e.message);
} }
this.msgWrapper.create('已起飞', {}, 'success'); this.msgWrapper.create('已起飞', {}, 'success');
} }

View File

@ -1,6 +1,6 @@
import { MenuItemConfig } from "../ZhongIcon"; import { MenuItemConfig } from "../ZhongIcon";
import Timer from "../utils/Timer"; import Timer from "../utils/Timer";
import BuyBeerHelper from "../action/BuyBeerHelper"; import BuyBeerHelper from "../../feature/BuyBeerHelper";
import UpdateTranslateDict from "./UpdateTranslateDict"; import UpdateTranslateDict from "./UpdateTranslateDict";
import landedRedirect from "../../func/module/landedRedirect"; import landedRedirect from "../../func/module/landedRedirect";
import Alert from "../utils/Alert"; import Alert from "../utils/Alert";

View File

@ -1,12 +0,0 @@
import { MENU_ITEM_TYPE } from "../../interface/MenuItem";
const BUTTON_HANDLE_METADATA_KEY = Symbol("BUTTON_HANDLE_KEY")
/**
* @ButtonHandler
*
* @param target
*/
export function ButtonHandler<T>(target: T): void {
Reflect.defineMetadata(BUTTON_HANDLE_METADATA_KEY, MENU_ITEM_TYPE.BUTTON, target);
}

View File

@ -1,26 +0,0 @@
export default class Elem {
private readonly elem: HTMLElement;
constructor(tagName) {
this.elem = document.createElement(tagName);
}
public html(htmlString): Elem {
this.elem.innerHTML = htmlString;
return this;
}
public id(id): Elem {
this.elem.id = id;
return this;
}
public class(className): Elem {
this.elem.classList.add(className);
return this;
}
public el(): HTMLElement {
return this.elem;
}
}

View File

@ -2,6 +2,7 @@ import ClassWithName from "../../interface/ClassWithName";
/** /**
* *
* @deprecated
*/ */
export default class Provider implements ClassWithName { export default class Provider implements ClassWithName {
readonly className: string = 'Provider'; readonly className: string = 'Provider';

View File

@ -7,30 +7,14 @@ import globVars from "../../globVars";
@Injectable() @Injectable()
export default class ActionButtonUtils { export default class ActionButtonUtils {
private hasAdded: boolean = false; private hasAdded: boolean = false;
private readonly logger = Logger.factory(ActionButtonUtils)
constructor(
private readonly logger: Logger,
) {
}
public add(txt: string, func: (ev: Event) => void = () => null): void { public add(txt: string, func: (ev: Event) => void = () => null): void {
if (!this.hasAdded) this.handle(txt, func);
else this.logger.warn('ActionButton已存在'); let added = { txt, func }
globVars.actionList.push({ globVars.buttons.push(added)
txt,
func this.logger.info({ globVars })
});
} }
private handle(txt, func): void {
let btn = document.createElement('button');
btn.style.padding = '8px 13px 8px 0';
btn.style.verticalAlign = 'bottom';
btn.style.color = '#4CAF50';
btn.innerHTML = txt;
btn.addEventListener('click', func);
// ZhongIcon.ZhongNode.querySelector('button').after(btn);
this.hasAdded = true;
this.logger.info('ActionButton已添加', { txt, func, btn });
}
} }

View File

@ -43,7 +43,7 @@ export default class CommonUtils {
let logger = Container.factory(Logger); let logger = Container.factory(Logger);
let start = new Timer(); let start = new Timer();
const engine = this.getScriptEngine(); const engine = this.getScriptEngine();
logger.info('跨域获取数据开始, 脚本引擎: ' + engine); logger.info(`跨域请求 -> ${url}, 脚本引擎: ${engine}`);
return new Promise<string>((resolve, reject) => { return new Promise<string>((resolve, reject) => {
switch (engine) { switch (engine) {
case UserScriptEngine.RAW: { case UserScriptEngine.RAW: {

View File

@ -48,7 +48,7 @@ export default class NetHighLvlWrapper {
}, },
"referrer": "https://www.torn.com/travelagency.php", "referrer": "https://www.torn.com/travelagency.php",
"referrerPolicy": "strict-origin-when-cross-origin", "referrerPolicy": "strict-origin-when-cross-origin",
"body": `step=travel&id=${ destId }&key=${ key }&type=${ type }`, "body": `step=travel&id=${ destId }&type=${ type }`,
"method": "POST", "method": "POST",
"mode": "cors", "mode": "cors",
"credentials": "include" "credentials": "include"

View File

@ -3,13 +3,13 @@ const CLASSNAME_METADATA_KEY = Symbol("CLASSNAME_KEY");
/** /**
* *
*/ */
export default function ClassName(className?: string): ClassDecorator { export default function ClassName(className: string): ClassDecorator {
return function <TFunction extends Function>(target: TFunction): TFunction { return function <TFunction extends Function>(target: TFunction): TFunction {
Reflect.defineMetadata(CLASSNAME_METADATA_KEY, className || target.name, target); Reflect.defineMetadata(CLASSNAME_METADATA_KEY, className, target);
return target; return target;
}; };
} }
export function GetClassName(target: any) { export function GetClassName(target: ClassType<{}>): string {
return Reflect.getMetadata(CLASSNAME_METADATA_KEY, target); return Reflect.getMetadata(CLASSNAME_METADATA_KEY, target);
} }

View File

@ -1,40 +1,38 @@
import "reflect-metadata";
import { assertInjectable } from "./Injectable"; import { assertInjectable } from "./Injectable";
import { GetClassName } from "./ClassName"; import ClassName, { GetClassName } from "./ClassName";
import Logger from "../class/Logger";
/** /**
* *
*/ */
@ClassName('Container')
export class Container { export class Container {
static _container = new Map(); static _container = new Map();
private static logger; private static logger;
static set(k: any, v: any): void { static set<T>(k: Constructor<T>, v: T): void {
if (!this._container.has(k)) { if (!this._container.has(k)) {
this._container.set(k, v); this._container.set(k, v);
} }
} }
static get(k: any): any { static get<T>(k: Constructor<T>): T {
return this._container.get(k); return this._container.get(k);
} }
static factory<T>(target: Constructor<T>): T { static factory<T>(target: ClassType<T>): T {
assertInjectable(target); assertInjectable(target);
// if (Container.get(target))
// return Container.get(target);
return this.get(target) || this.initParam(target); return this.get(target) || this.initParam(target);
} }
static setLogger(logger: { info(...o: any[]), error(...o: any[]), warn(...o: any[]) }): void { static setLogger(logger: Logger): void {
this.logger = logger; this.logger = logger;
} }
private static initParam<T>(target: Constructor<T>): T { private static initParam<T>(target: Constructor<T>): T {
// 获取所有注入的服务 // 获取所有注入的服务
const providers = Reflect.getMetadata('design:paramtypes', target); const providers = Reflect.getMetadata('design:paramtypes', target);
// this.logger.info({providers})
// this.logger.info('原型名'+Object.getPrototypeOf(target).constructor.name)
// this.logger.info('直接名'+target.name)
const args = providers ? providers.map((provider: Constructor) => { const args = providers ? providers.map((provider: Constructor) => {
return this.factory(provider); return this.factory(provider);
}) : []; }) : [];

View File

@ -5,7 +5,6 @@ const INJECTABLE_METADATA_KEY = Symbol("INJECTABLE_KEY");
// TODO 实现非单例注入 // TODO 实现非单例注入
export enum INJECT_MODE { export enum INJECT_MODE {
Singleton = 1, Singleton = 1,
Multi = 2,
} }
export function Injectable(injectMode: INJECT_MODE = INJECT_MODE.Singleton): ClassDecorator { export function Injectable(injectMode: INJECT_MODE = INJECT_MODE.Singleton): ClassDecorator {

File diff suppressed because it is too large Load Diff

278
src/ts/feature/Atk.ts Normal file
View File

@ -0,0 +1,278 @@
import ClassName from "../container/ClassName"
import { Injectable } from "../container/Injectable"
import IFeature from "../man/IFeature"
import LocalConfigWrapper from "../class/LocalConfigWrapper"
import CommonUtils from "../class/utils/CommonUtils"
import Global from "../class/Global"
import ActionButtonUtils from "../class/utils/ActionButtonUtils"
import Logger from "../class/Logger"
import Alert from "../class/utils/Alert"
import Device from "../enum/Device"
import ATTACK_HELPER_CSS from "../../static/css/attack_helper.module.css"
import ATK_PAGE_REG from "./url/ATK_PAGE_REG";
enum FIGHT_STAGE {
READY = 'ready',
IN_PROGRESS_OR_ERROR = 'in_progress_or_error',
FINISHED = 'finished',
END = 'end',
OTHER = 'other'
}
@ClassName('Atk')
@Injectable()
export default class Atk implements IFeature {
private currentStage: FIGHT_STAGE = FIGHT_STAGE.OTHER;
constructor(
private readonly actionButtonUtils: ActionButtonUtils,
private readonly localConfigWrapper: LocalConfigWrapper,
private readonly commonUtils: CommonUtils,
private readonly global: Global,
// private readonly mathUtils: MathUtils,
// private readonly fetchUtils: FetchUtils,
private readonly logger: Logger,
) {
}
urlIncludes(): RegExp[] {
return [ATK_PAGE_REG]
}
urlExcludes(): RegExp[] {
return []
}
description(): string {
return '攻击助手'
}
iStart(): void | Promise<void> {
// 光速刷新按钮
this.actionButtonUtils.add('光速刷新', () => this.doAttackReload());
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';
});
}
}

View File

@ -0,0 +1,68 @@
import ClassName from "../container/ClassName"
import { Injectable } from "../container/Injectable"
import IFeature from "../man/IFeature"
import TornStyleBlock from "../class/utils/TornStyleBlock"
import ADD_BEER_HEAD_HTML from "../../static/html/buyBeer/add_beer_head.html"
import SHOP_BEER_STATIC_ITEM_HTML from "../../static/html/buyBeer/shop_beer_static_item.html"
import globVars from "../globVars"
import Logger from "../class/Logger";
import BuyBeerHelper from "./BuyBeerHelper";
@ClassName('BeerShopModifier')
@Injectable()
export default class BeerShopModifier implements IFeature {
private readonly logger = Logger.factory(BeerShopModifier)
constructor(
private readonly buyBeerHelper: BuyBeerHelper,
) {
}
description(): string {
return "啤酒店页面修改";
}
iStart(): void | Promise<void> {
this.start()
}
urlExcludes(): RegExp[] {
return [];
}
urlIncludes(): RegExp[] {
return [/shops.php\?step=bitsnbobs/];
}
start() {
let block = new TornStyleBlock('啤酒助手').insert2Dom();
block.setContent(ADD_BEER_HEAD_HTML);
const msg_node = block.querySelector('#wh-msg');
// 加入啤酒
block.querySelector('button').addEventListener('click', e => {
let node = document.querySelector('ul.items-list');
if (!node) {
msg_node.innerHTML = '❌ 商品未加载完';
this.logger.error('商品未加载完');
return;
}
if (node.querySelector('span[id="180-name"]')) {
msg_node.innerHTML = '❌ 页面已经有啤酒了';
this.logger.warn('商店页面已有啤酒');
return;
}
const clear_node = node.querySelector('li.clear');
const beer = document.createElement('li');
beer.classList.add('torn-divider', 'divider-vertical');
beer.style.backgroundColor = '#c8c8c8';
beer.innerHTML = SHOP_BEER_STATIC_ITEM_HTML;
if (clear_node) clear_node.before(beer);
else node.append(beer);
(<HTMLInputElement>e.target).disabled = true;
msg_node.innerHTML = '添加成功';
});
// 监听啤酒购买
globVars.responseHandlers.push((...args: any[]) => this.buyBeerHelper.responseHandler.apply(this.buyBeerHelper, args));
}
}

View File

@ -1,18 +1,37 @@
import InfoUtils from "../utils/InfoUtils"; import InfoUtils from "../class/utils/InfoUtils";
import MathUtils from "../utils/MathUtils"; import MathUtils from "../class/utils/MathUtils";
import NOTIFY_HTML from "../../../static/html/buyBeer/notify.html"; import NOTIFY_HTML from "../../static/html/buyBeer/notify.html";
import CommonUtils from "../utils/CommonUtils"; import CommonUtils from "../class/utils/CommonUtils";
import Popup from "../utils/Popup"; import Popup from "../class/utils/Popup";
import ResponseInject from "../../interface/ResponseInject"; import ResponseInject from "../interface/ResponseInject";
import LocalConfigWrapper from "../LocalConfigWrapper"; import LocalConfigWrapper from "../class/LocalConfigWrapper";
import ClassName from "../../container/ClassName"; import ClassName from "../container/ClassName";
import { Injectable } from "../../container/Injectable"; import { Injectable } from "../container/Injectable";
import Logger from "../Logger"; import Logger from "../class/Logger";
import MsgWrapper from "../utils/MsgWrapper"; import MsgWrapper from "../class/utils/MsgWrapper";
import IFeature from "../man/IFeature";
import ATK_PAGE_REG from "./url/ATK_PAGE_REG";
import ALL_PAGE_REG from "./url/ALL_PAGE_REG";
@ClassName('BuyBeerHelper') @ClassName('BuyBeerHelper')
@Injectable() @Injectable()
export default class BuyBeerHelper implements BeerMonitorLoop, ResponseInject { export default class BuyBeerHelper implements BeerMonitorLoop, ResponseInject, IFeature {
urlIncludes(): RegExp[] {
return [ALL_PAGE_REG]
}
urlExcludes(): RegExp[] {
return [ATK_PAGE_REG]
}
description(): string {
return '啤酒助手'
}
iStart(): void | Promise<void> {
(this.localConfigWrapper.config._15Alarm) && this.start()
}
private isNotifying = false; private isNotifying = false;
private loopId: number = null; private loopId: number = null;

View File

@ -0,0 +1,25 @@
import ClassName from "../container/ClassName";
import { Injectable } from "../container/Injectable";
import IFeature from "../man/IFeature";
import christmasTownHelper from "../func/module/christmasTownHelper";
@ClassName('ChristmasTown')
@Injectable()
export default class ChristmasTown implements IFeature {
urlIncludes(): RegExp[] {
return [/christmas_town\.php/]
}
urlExcludes(): RegExp[] {
return []
}
description(): string {
return '圣诞小镇助手'
}
iStart(): void | Promise<void> {
christmasTownHelper()
}
}

View File

@ -1,26 +1,44 @@
import CommonUtils from "../utils/CommonUtils"; import CommonUtils from "../class/utils/CommonUtils";
import FetchUtils from "../utils/FetchUtils"; import FetchUtils from "../class/utils/FetchUtils";
import InfoUtils from "../utils/InfoUtils"; import InfoUtils from "../class/utils/InfoUtils";
import TRAVEL_STATE from "../../enum/TravelState"; import TRAVEL_STATE from "../enum/TravelState";
import LocalConfigWrapper from "../LocalConfigWrapper"; import LocalConfigWrapper from "../class/LocalConfigWrapper";
import { Injectable } from "../../container/Injectable"; import { Injectable } from "../container/Injectable";
import ClassName from "../../container/ClassName"; import ClassName from "../container/ClassName";
import Logger from "../Logger"; import Logger from "../class/Logger";
import MsgWrapper from "../utils/MsgWrapper"; import MsgWrapper from "../class/utils/MsgWrapper";
import IFeature from "../man/IFeature";
import ATK_PAGE_REG from "./url/ATK_PAGE_REG";
import ALL_PAGE_REG from "./url/ALL_PAGE_REG";
/** /**
* *
*/ */
@Injectable() @Injectable()
@ClassName('CompanyHelper') @ClassName('CompanyHelper')
export default class CompanyHelper { export default class CompanyHelper implements IFeature {
className = 'CompanyHelper'; private readonly logger: Logger = Logger.factory(CompanyHelper)
description(): string {
return "公司助手、火车爆仓检测";
}
iStart(): void | Promise<void> {
this.init()
}
urlExcludes(): RegExp[] {
return [ATK_PAGE_REG];
}
urlIncludes(): RegExp[] {
return [ALL_PAGE_REG];
}
public constructor( public constructor(
private readonly localConfigWrapper: LocalConfigWrapper, private readonly localConfigWrapper: LocalConfigWrapper,
private readonly commonUtils: CommonUtils, private readonly commonUtils: CommonUtils,
private readonly fetchUtils: FetchUtils, private readonly fetchUtils: FetchUtils,
private readonly logger: Logger,
private readonly infoUtils: InfoUtils, private readonly infoUtils: InfoUtils,
private readonly msgWrapper: MsgWrapper, private readonly msgWrapper: MsgWrapper,
) { ) {
@ -30,10 +48,6 @@ export default class CompanyHelper {
this.localConfigWrapper.config.CHTrainsDetectSwitch && this.trainsDetect().then(); this.localConfigWrapper.config.CHTrainsDetectSwitch && this.trainsDetect().then();
} }
// public detectNow(): void {
// this.trainsDetect(true).then();
// }
/** /**
* *
* *
@ -41,7 +55,7 @@ export default class CompanyHelper {
*/ */
private async trainsDetect(test: boolean = false): Promise<null> { private async trainsDetect(test: boolean = false): Promise<null> {
// 通过用户的icon判断公司老板 // 通过用户的icon判断公司老板
if ((await this.infoUtils.getSessionData()).statusIcons.icons.company.iconID !== 'icon73') { if ((await this.infoUtils.getSessionData()).statusIcons.icons.company?.iconID !== 'icon73') {
this.logger.info('火车检测跳过:非公司老板'); this.logger.info('火车检测跳过:非公司老板');
return; return;
} }
@ -56,7 +70,7 @@ export default class CompanyHelper {
this.fetchUtils.fetchText('/companies.php') this.fetchUtils.fetchText('/companies.php')
.then(res => { .then(res => {
let tmp: HTMLElement = document.createElement('div'); let tmp: HTMLElement = document.createElement('div');
let bodyTagStart = this.commonUtils.matchOne(res, /<body.+>/); let bodyTagStart = this.commonUtils.matchOne(res, /<body.+/);
if (!bodyTagStart) { if (!bodyTagStart) {
this.logger.warn('火车检测: 无法获取数据'); this.logger.warn('火车检测: 无法获取数据');
throw new Error('火车检测: 无法获取数据'); throw new Error('火车检测: 无法获取数据');

View File

@ -0,0 +1,78 @@
import ClassName from "../container/ClassName";
import { Injectable } from "../container/Injectable";
import IFeature from "../man/IFeature";
import LocalConfigWrapper from "../class/LocalConfigWrapper";
import CommonUtils from "../class/utils/CommonUtils";
import QUICK_CRIMES_HTML from "../../static/html/quick_crimes.html";
@ClassName('CrimePageModifier')
@Injectable()
export default class CrimePageModifier implements IFeature {
constructor(
private readonly localConfigWrapper: LocalConfigWrapper
) {
}
description(): string {
return "页面快速crime";
}
iStart(): void | Promise<void> {
this.start()
}
urlExcludes(): RegExp[] {
return [];
}
urlIncludes(): RegExp[] {
return [/crimes\.php/];
}
start() { // TODO 重构、与翻译解藕
if (this.localConfigWrapper.config.quickCrime) {
// iframe
if (self !== top) {
const isValidate = document.querySelector('h4#skip-to-content').innerText.toLowerCase().includes('validate');
CommonUtils.elementReady('#header-root').then(e => e.style.display = 'none');
CommonUtils.elementReady('#sidebarroot').then(e => e.style.display = 'none');
CommonUtils.elementReady('#chatRoot').then(e => e.style.display = 'none');
if (!isValidate) document.body.style.overflow = 'hidden';
CommonUtils.elementReady('.content-wrapper').then(e => {
e.style.margin = '0px';
e.style.position = 'absolute';
e.style.top = '-35px';
});
CommonUtils.elementReady('#go-to-top-btn button').then(e => e.style.display = 'none');
}
const element = document.querySelector('.content-wrapper');
const OB = new MutationObserver(() => {
OB.disconnect();
trans();
OB.observe(element, {
characterData: true,
attributes: true,
subtree: true,
childList: true
});
});
const trans = () => {
const dom = QUICK_CRIMES_HTML;
const hasInserted = element.querySelector('.wh-translate') !== null;
const $title = document.querySelector('div.content-title');
const $info = document.querySelector('.info-msg-cont');
if (!hasInserted) {
if ($title) $title.insertAdjacentHTML('beforebegin', dom);
else if ($info) $info.insertAdjacentHTML('beforebegin', dom);
}
};
trans();
OB.observe(element, {
characterData: true,
attributes: true,
subtree: true,
childList: true
});
}
}
}

View File

@ -0,0 +1,76 @@
import Logger, { LoggerKey } from "../class/Logger"
import ClassName from "../container/ClassName"
import { Injectable } from "../container/Injectable"
import CommonUtils, { CommonUtilsKey } from "../class/utils/CommonUtils"
import LocalConfigWrapper, { LocalConfigWrapperKey } from "../class/LocalConfigWrapper"
import { createApp } from "vue"
import FloatMenu from "../../vue/FloatMenu.vue"
import PopupWrapper, { PopupWrapperKey } from "../class/utils/PopupWrapper"
import TravelItem from "./TravelItem"
import QuickGymTrain, { QuickGymTrainKey } from "../class/action/QuickGymTrain"
import QuickFlyBtnHandler, { QuickFlyBtnHandlerKey } from "../class/handler/QuickFlyBtnHandler"
import ItemHelper, { ItemHelperKey } from "../class/utils/ItemHelper"
import MathUtils, { MathUtilsKey } from "../class/utils/MathUtils"
import IFeature from "../man/IFeature"
import ALL_PAGE_REG from "./url/ALL_PAGE_REG"
@ClassName("IconHelper")
@Injectable()
export default class IconHelper implements IFeature {
private readonly _element: HTMLElement = document.createElement('div');
private readonly logger = Logger.factory(IconHelper)
description(): string {
return "浮动图标、侧边菜单、标签式便携功能";
}
iStart(): void | Promise<void> {
this.init()
}
urlExcludes(): RegExp[] {
return [];
}
urlIncludes(): RegExp[] {
return [ALL_PAGE_REG];
}
constructor(
private readonly commonUtils: CommonUtils,
private readonly localConfigWrapper: LocalConfigWrapper,
private readonly popupWrapper: PopupWrapper,
private readonly travelItem: TravelItem,
private readonly quickGymTrain: QuickGymTrain,
private readonly quickFlyBtnHandler: QuickFlyBtnHandler,
private readonly itemHelper: ItemHelper,
private readonly mathUtils: MathUtils,
) {
}
get element() {
return this._element;
}
init() {
this._element.id = 'WHMenu2023';
document.body.append(this._element);
let app = createApp(FloatMenu);
this.logger.info('Vue实例已创建', { app })
app.config.errorHandler = (err) => this.logger.error('[VUE错误]', err);
app.config.warnHandler = (err) => this.logger.warn('[VUE警告]', err);
app.provide(LoggerKey, this.logger);
app.provide(CommonUtilsKey, this.commonUtils);
app.provide(MathUtilsKey, this.mathUtils);
// app.provide(TravelItemKey, this.travelItem);
app.provide(PopupWrapperKey, this.popupWrapper);
app.provide(LocalConfigWrapperKey, this.localConfigWrapper);
app.provide(QuickGymTrainKey, this.quickGymTrain);
app.provide(QuickFlyBtnHandlerKey, this.quickFlyBtnHandler);
app.provide(ItemHelperKey, this.itemHelper);
app.mount(this._element);
this.logger.info('Vue实例已挂载')
}
}

View File

@ -1,19 +1,34 @@
import InfoUtils from "../utils/InfoUtils"; import InfoUtils from "../class/utils/InfoUtils";
import MathUtils from "../utils/MathUtils"; import MathUtils from "../class/utils/MathUtils";
import CommonUtils from "../utils/CommonUtils"; import CommonUtils from "../class/utils/CommonUtils";
import TornStyleBlock from "../utils/TornStyleBlock"; import TornStyleBlock from "../class/utils/TornStyleBlock";
import Timer from "../utils/Timer"; import Timer from "../class/utils/Timer";
import FetchUtils from "../utils/FetchUtils"; import FetchUtils from "../class/utils/FetchUtils";
import { Injectable } from "../../container/Injectable"; import { Injectable } from "../container/Injectable";
import ClassName from "../../container/ClassName"; import ClassName from "../container/ClassName";
import Logger from "../Logger"; import Logger from "../class/Logger";
import IFeature from "../man/IFeature";
/** /**
* *
*/ */
@Injectable() @Injectable()
@ClassName('LotteryHelper') @ClassName('LotteryHelper')
export default class LotteryHelper { export default class LotteryHelper implements IFeature {
private readonly logger: Logger = Logger.factory(LotteryHelper)
description(): string {
return "彩票助手";
}
iStart(): void | Promise<void> {
this.init()
}
urlExcludes(): RegExp[] {
return [];
}
private loopFlag = true; private loopFlag = true;
private radioDaily: HTMLInputElement = null; private radioDaily: HTMLInputElement = null;
@ -26,12 +41,15 @@ export default class LotteryHelper {
private status: HTMLElement = null; private status: HTMLElement = null;
private desc: HTMLElement = null; private desc: HTMLElement = null;
urlIncludes(): RegExp[] {
return [/loader\.php\?sid=lottery/];
}
constructor( constructor(
private readonly mathUtils: MathUtils, private readonly mathUtils: MathUtils,
private readonly commonUtils: CommonUtils, private readonly commonUtils: CommonUtils,
private readonly fetchUtils: FetchUtils, private readonly fetchUtils: FetchUtils,
private readonly infoUtils: InfoUtils, private readonly infoUtils: InfoUtils,
private readonly logger: Logger,
) { ) {
} }

50
src/ts/feature/MapItem.ts Normal file
View File

@ -0,0 +1,50 @@
import IFeature from "../man/IFeature";
import ClassName from "../container/ClassName";
import { Injectable } from "../container/Injectable";
import TornStyleBlock from "../class/utils/TornStyleBlock";
import TornStyleSwitch from "../class/utils/TornStyleSwitch";
import cityFinder from "../func/module/cityFinder";
import LocalConfigWrapper from "../class/LocalConfigWrapper";
@ClassName('MapItem')
@Injectable()
export default class MapItem implements IFeature {
constructor(
private readonly localConfigWrapper: LocalConfigWrapper
) {
}
description(): string {
return "捡垃圾助手";
}
iStart(): void | Promise<void> {
this.start()
}
urlExcludes(): RegExp[] {
return [];
}
urlIncludes(): RegExp[] {
return [/city\.php/];
}
start() {
if (this.localConfigWrapper.config.cityFinder) {
let _base = new TornStyleBlock('芜湖助手').insert2Dom();
// 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;
// });
_base.append(document.createElement('br'));
cityFinder(_base);
}
}
}

View File

@ -0,0 +1,77 @@
import ClassName from "../container/ClassName";
import { Injectable } from "../container/Injectable";
import IFeature from "../man/IFeature";
import LocalConfigWrapper from "../class/LocalConfigWrapper";
import getTaskHint from "../func/translate/getTaskHint";
import { missionDict } from "../dictionary/translation";
@ClassName('MissionPageModifier')
@Injectable()
export default class MissionPageModifier implements IFeature {
constructor(
private readonly localConfigWrapper: LocalConfigWrapper
) {
}
description(): string {
return "页面快速crime";
}
iStart(): void | Promise<void> {
this.start()
}
urlExcludes(): RegExp[] {
return [];
}
urlIncludes(): RegExp[] {
return [/crimes\.php/];
}
start() {
const anchor = document.querySelector('.content-wrapper');
const OB = new MutationObserver(() => {
OB.disconnect();
trans();
OB.observe(anchor, {
characterData: true,
attributes: true,
subtree: true,
childList: true
});
});
const taskList = {};
const trans = () => {
$('ul#giver-tabs a.ui-tabs-anchor').each((i, e) => {
let $e = $(e);
if ($e.children().hasClass('mission-complete-icon')) {
taskList[i] = e.innerText.trim();
} else {
taskList[i] = $e.clone().children().remove().end().text().trim();
}
});
// 助手注入
$('div.max-height-fix.info').each((i, e) => {
let $e = $(e);
if ($e.find('.wh-translated').length !== 0) return;
$e.append(`<div class="wh-translated"><h6 style="color:green"><b>任务助手</b></h6><p>${ getTaskHint(taskList[i]) }</p></div>`);
});
// 任务目标
$('ul.tasks-list span.title-wrap').contents().each((i, e) => {
if (e.nodeType === 3) {
if (missionDict[e.nodeValue.trim()]) {
e.nodeValue = missionDict[e.nodeValue.trim()];
}
}
});
};
trans();
OB.observe(anchor, {
characterData: true,
attributes: true,
subtree: true,
childList: true
});
}
}

View File

@ -1,10 +1,13 @@
import CommonUtils from "../utils/CommonUtils"; import CommonUtils from "../class/utils/CommonUtils";
import Global from "../Global"; import Global from "../class/Global";
import Device from "../../enum/Device"; import Device from "../enum/Device";
import ClassName from "../../container/ClassName"; import ClassName from "../container/ClassName";
import { Injectable } from "../../container/Injectable"; import { Injectable } from "../container/Injectable";
import LocalConfigWrapper from "../LocalConfigWrapper"; import LocalConfigWrapper from "../class/LocalConfigWrapper";
import Logger from "../Logger"; import Logger from "../class/Logger";
import IFeature from "../man/IFeature";
import ATK_PAGE_REG from "./url/ATK_PAGE_REG";
import ALL_PAGE_REG from "./url/ALL_PAGE_REG";
/** /**
* ### * ###
@ -13,16 +16,32 @@ import Logger from "../Logger";
*/ */
@ClassName('SidebarHelper') @ClassName('SidebarHelper')
@Injectable() @Injectable()
export default class SidebarHelper { export default class SidebarHelper implements IFeature {
private readonly logger: Logger = Logger.factory(SidebarHelper)
description(): string {
return "边栏助手";
}
iStart(): void | Promise<void> {
this.init()
}
urlExcludes(): RegExp[] {
return [ATK_PAGE_REG];
}
private readonly sidebarRootNode: HTMLElement; private readonly sidebarRootNode: HTMLElement;
private readonly toggleBtn: HTMLButtonElement; private readonly toggleBtn: HTMLButtonElement;
private isHide: boolean; private isHide: boolean;
urlIncludes(): RegExp[] {
return [ALL_PAGE_REG];
}
constructor( constructor(
private readonly localConfigWrapper: LocalConfigWrapper, private readonly localConfigWrapper: LocalConfigWrapper,
private readonly global: Global, private readonly global: Global,
private readonly logger: Logger,
) { ) {
this.sidebarRootNode = document.querySelector('#sidebarroot'); this.sidebarRootNode = document.querySelector('#sidebarroot');
this.toggleBtn = document.createElement('button'); this.toggleBtn = document.createElement('button');
@ -35,7 +54,7 @@ export default class SidebarHelper {
this.initToggleBtn(); this.initToggleBtn();
this.barRedirect(); this.barRedirect();
} else { } else {
this.logger.warn('[SidebarHelper] 页面未开启边栏,边栏助手退出'); this.logger.warn('页面未开启边栏,边栏助手退出');
} }
} }
@ -73,10 +92,10 @@ export default class SidebarHelper {
// 4条转跳 // 4条转跳
private barRedirect(): void { private barRedirect(): void {
if (this.localConfigWrapper.config.barsRedirect) { if (this.localConfigWrapper.config.barsRedirect) {
const eb = document.getElementById('barEnergy') as HTMLAnchorElement; const eb = document.querySelector<HTMLAnchorElement>('#barEnergy');
const nb = document.getElementById('barNerve') as HTMLAnchorElement; const nb = document.querySelector<HTMLAnchorElement>('#barNerve');
const hb = document.getElementById('barHappy') as HTMLAnchorElement; const hb = document.querySelector<HTMLAnchorElement>('#barHappy');
const lb = document.getElementById('barLife') as HTMLAnchorElement; const lb = document.querySelector<HTMLAnchorElement>('#barLife');
if (eb) { if (eb) {
eb.addEventListener('click', () => location.href = '/gym.php'); eb.addEventListener('click', () => location.href = '/gym.php');
eb.href = '/gym.php'; eb.href = '/gym.php';

View File

@ -1,15 +1,36 @@
import TornStyleBlock from "../utils/TornStyleBlock"; import TornStyleBlock from "../class/utils/TornStyleBlock";
import TornStyleSwitch from "../utils/TornStyleSwitch"; import TornStyleSwitch from "../class/utils/TornStyleSwitch";
import { Injectable } from "../../container/Injectable"; import { Injectable } from "../container/Injectable";
import ClassName from "../../container/ClassName"; import ClassName from "../container/ClassName";
import LocalConfigWrapper from "../LocalConfigWrapper"; import LocalConfigWrapper from "../class/LocalConfigWrapper";
import IFeature from "../man/IFeature";
@Injectable() @Injectable()
@ClassName('StackHelper') @ClassName('StackHelper')
export default class StackHelper { export default class StackHelper implements IFeature {
constructor( constructor(
private readonly localConfigWrapper: LocalConfigWrapper, private readonly localConfigWrapper: LocalConfigWrapper,
) { ) {
}
description(): string {
return "叠e助手";
}
iStart(): void | Promise<void> {
this.start()
}
urlExcludes(): RegExp[] {
return [];
}
urlIncludes(): RegExp[] {
return [/gym\.php/];
}
start() {
let block = new TornStyleBlock('叠E保护').insert2Dom(); let block = new TornStyleBlock('叠E保护').insert2Dom();
let switcher = new TornStyleSwitch('启用'); let switcher = new TornStyleSwitch('启用');
let input = switcher.getInput(); let input = switcher.getInput();
@ -23,5 +44,4 @@ export default class StackHelper {
this.localConfigWrapper.config.SEProtect = target.checked; this.localConfigWrapper.config.SEProtect = target.checked;
}; };
} }
} }

View File

@ -1,18 +1,20 @@
import titleTrans from "../translate/titleTrans"; import titleTrans from "../func/translate/titleTrans";
import contentTitleLinksTrans from "../translate/contentTitleLinksTrans"; import contentTitleLinksTrans from "../func/translate/contentTitleLinksTrans";
import Device from "../../enum/Device"; import Device from "../enum/Device";
import ActionButtonUtils from "../../class/utils/ActionButtonUtils"; import ActionButtonUtils from "../class/utils/ActionButtonUtils";
import CommonUtils from "../../class/utils/CommonUtils"; import CommonUtils from "../class/utils/CommonUtils";
import TRAVEL_ALARM_CSS from "../../../static/css/travel_alarm.module.css"; import TRAVEL_ALARM_CSS from "../../static/css/travel_alarm.module.css";
import TRAVEL_ALARM_HTML from "../../../static/html/travel_alarm.html"; import TRAVEL_ALARM_HTML from "../../static/html/travel_alarm.html";
import TornStyleBlock from "../../class/utils/TornStyleBlock"; import TornStyleBlock from "../class/utils/TornStyleBlock";
import QuickFlyBtnHandler from "../../class/handler/QuickFlyBtnHandler"; import QuickFlyBtnHandler from "../class/handler/QuickFlyBtnHandler";
import TRAVEL_STATE from "../../enum/TravelState"; import TRAVEL_STATE from "../enum/TravelState";
import Global from "../../class/Global"; import Global from "../class/Global";
import ClassName from "../../container/ClassName"; import ClassName from "../container/ClassName";
import { Injectable } from "../../container/Injectable"; import { Injectable } from "../container/Injectable";
import LocalConfigWrapper from "../../class/LocalConfigWrapper"; import LocalConfigWrapper from "../class/LocalConfigWrapper";
import MsgWrapper from "../../class/utils/MsgWrapper"; import MsgWrapper from "../class/utils/MsgWrapper";
import IFeature from "../man/IFeature";
import INDEX_PAGE_REG from "./url/INDEX_PAGE_REG";
/** /**
* *
@ -24,7 +26,23 @@ import MsgWrapper from "../../class/utils/MsgWrapper";
*/ */
@ClassName('TravelHelper') @ClassName('TravelHelper')
@Injectable() @Injectable()
export default class TravelHelper { export default class TravelHelper implements IFeature {
description(): string {
return "飞行助手";
}
iStart(): void | Promise<void> {
this.init()
}
urlExcludes(): RegExp[] {
return [];
}
urlIncludes(): RegExp[] {
return [INDEX_PAGE_REG, /travelagency\.php/];
}
constructor( constructor(
private readonly global: Global, private readonly global: Global,
private readonly commonUtils: CommonUtils, private readonly commonUtils: CommonUtils,
@ -153,7 +171,7 @@ export default class TravelHelper {
// 响铃的方法 // 响铃的方法
let audio_play_handle = () => { let audio_play_handle = () => {
if (user_stop_alert) { if (user_stop_alert) {
clearInterval(audio_play_id); window.clearInterval(audio_play_id);
audio_play_id = null; audio_play_id = null;
return; return;
} }
@ -194,7 +212,7 @@ export default class TravelHelper {
} else { } else {
// flying_status.innerHTML = `飞行中...`; // flying_status.innerHTML = `飞行中...`;
if (wh_trv_alarm.enable) { if (wh_trv_alarm.enable) {
clearInterval(audio_play_id); window.clearInterval(audio_play_id);
audio_play_id = null; audio_play_id = null;
stop_node.parentElement.classList.add('wh-trv-alarm-stop-hide'); stop_node.parentElement.classList.add('wh-trv-alarm-stop-hide');
} }
@ -266,6 +284,7 @@ export default class TravelHelper {
}); });
const trans = () => { const trans = () => {
// 当前能量e // 当前能量e
// @ts-ignore
const energyBarStr = $('#barEnergy p[class^="bar-value__"]').text().trim(); const energyBarStr = $('#barEnergy p[class^="bar-value__"]').text().trim();
const [curE, maxE] = energyBarStr.split('/').length === 2 const [curE, maxE] = energyBarStr.split('/').length === 2
? [parseInt(energyBarStr.split('/')[0]), parseInt(energyBarStr.split('/')[1])] ? [parseInt(energyBarStr.split('/')[0]), parseInt(energyBarStr.split('/')[1])]
@ -274,6 +293,7 @@ export default class TravelHelper {
const fullEnergyTime = !(isNaN(curE) || isNaN(maxE)) ? (maxE - 5 - curE) / 5 * incTime const fullEnergyTime = !(isNaN(curE) || isNaN(maxE)) ? (maxE - 5 - curE) / 5 * incTime
+ (incTime - new Date().getMinutes() % incTime) : NaN; + (incTime - new Date().getMinutes() % incTime) : NaN;
// 起飞前提示 // 起飞前提示
// @ts-ignore
$('.travel-confirm .travel-question .q-wrap span:nth-of-type(2)').each((i, e) => { $('.travel-confirm .travel-question .q-wrap span:nth-of-type(2)').each((i, e) => {
if (isNaN(fullEnergyTime)) return; if (isNaN(fullEnergyTime)) return;
const spl = e.innerText.trim().split(' '); const spl = e.innerText.trim().split(' ');
@ -304,10 +324,6 @@ export default class TravelHelper {
} }
} }
/** TODO */
inTravelPage() {
}
async doTravelBack(): Promise<void> { async doTravelBack(): Promise<void> {
if (typeof window['getAction'] !== 'function') return; if (typeof window['getAction'] !== 'function') return;
let backHomeAction = function (): Promise<string> { let backHomeAction = function (): Promise<string> {

View File

@ -1,22 +1,41 @@
import CommonUtils from "../utils/CommonUtils"; import CommonUtils from "../class/utils/CommonUtils";
import UserScriptEngine from "../../enum/UserScriptEngine"; import UserScriptEngine from "../enum/UserScriptEngine";
import Popup from "../utils/Popup"; import Popup from "../class/utils/Popup";
import STOCK_IMG_HTML from "../../../static/html/stock_img.html"; import STOCK_IMG_HTML from "../../static/html/stock_img.html";
import WindowActiveState from "./WindowActiveState"; import WindowActiveState from "../class/action/WindowActiveState";
import { Injectable } from "../../container/Injectable"; import { Injectable } from "../container/Injectable";
import ClassName from "../../container/ClassName"; import ClassName from "../container/ClassName";
import Logger from "../Logger"; import Logger from "../class/Logger";
import { InjectionKey } from "vue"; import IFeature from "../man/IFeature";
import ATK_PAGE_REG from "./url/ATK_PAGE_REG";
import ALL_PAGE_REG from "./url/ALL_PAGE_REG";
@Injectable() @Injectable()
@ClassName('TravelItem') @ClassName('TravelItem')
export default class TravelItem { export default class TravelItem implements IFeature {
private readonly logger: Logger = Logger.factory(TravelItem)
description(): string {
return "海外货物存量静默获取";
}
iStart(): void | Promise<void> {
this.init()
}
urlExcludes(): RegExp[] {
return [ATK_PAGE_REG];
}
private readonly apiUrl: string = 'https://yata.yt/api/v1/travel/export/'; private readonly apiUrl: string = 'https://yata.yt/api/v1/travel/export/';
private foreignStockInfo: any = null; private foreignStockInfo: any = null;
urlIncludes(): RegExp[] {
return [ALL_PAGE_REG];
}
public constructor( public constructor(
private readonly windowActiveState: WindowActiveState, private readonly windowActiveState: WindowActiveState,
private readonly logger: Logger,
private readonly commonUtils: CommonUtils, private readonly commonUtils: CommonUtils,
) { ) {
} }
@ -174,4 +193,4 @@ export default class TravelItem {
} }
} }
export const TravelItemKey = Symbol('TravelItemKey') as InjectionKey<TravelItem> // export const TravelItemKey = Symbol('TravelItemKey') as InjectionKey<TravelItem>

View File

@ -0,0 +1,2 @@
const ALL_PAGE_REG = /^/
export default ALL_PAGE_REG

View File

@ -0,0 +1,2 @@
const ATK_PAGE_REG = /\/loader.php\?sid=attack&user2ID=/
export default ATK_PAGE_REG

View File

@ -0,0 +1,2 @@
const INDEX_PAGE_REG = /index\.php/
export default INDEX_PAGE_REG

View File

@ -280,7 +280,7 @@ export default function christmasTownHelper() {
const history = Object.keys(dropHist).map(key => dropHist[key]).sort((a, b) => a.id - b.id); const history = Object.keys(dropHist).map(key => dropHist[key]).sort((a, b) => a.id - b.id);
let table_html = ''; let table_html = '';
history.forEach(e => { history.forEach(e => {
table_html += `<tr><td>${ e.pos }</td><td>${ e.map }</td><td>${ e.name }</td><td>${ e.last }</td><td>${ e.isPassed ? '已取得' : '不确定' }</td></tr>`; table_html += `<tr><td>${ e.pos }</td><td>${ e.map ?? e.data }</td><td>${ e.name }</td><td>${ e.last }</td><td>${ e.isPassed ? '已取得' : '不确定' }</td></tr>`;
}); });
$tbody.innerHTML = table_html; $tbody.innerHTML = table_html;
localStorage.setItem('wh-loot-store', JSON.stringify(dropHist)); localStorage.setItem('wh-loot-store', JSON.stringify(dropHist));

View File

@ -0,0 +1,98 @@
import CommonUtils from "../../class/utils/CommonUtils"
type YataBSEstData = {
"total": number,
"score": number,
"type": "Offensive" | "Defensive" | "Balanced",
"skewness": number,
"timestamp": number,
"version": 1,
}
type YataApiResponse = {
// key为请求id
[key: number]: YataBSEstData
error?: { 'error': string, 'code': 1 | 2 | 3 | 4 }
}
type YataApiDataWrap = YataBSEstData & {
id: string,
isCache: boolean,
}
const cacheExpireMs = 86400000 // 一天
const KEY = 'WHBSEstCache'
const getCacheObj = () => {
let obj: { [key: string]: YataApiDataWrap }
try {
obj = JSON.parse(localStorage.getItem(KEY)) ?? {}
} catch (e) {
obj = {}
}
return obj
}
const getCache = (id: number): YataApiDataWrap => {
let cache: YataApiDataWrap = getCacheObj()[id]
if (cache && ((Date.now() - (cache.timestamp * 1000)) < cacheExpireMs)) {
cache.isCache = true
} else {
cache = null
}
return cache
}
const setCache = (data: YataApiDataWrap): void => {
const cache = getCacheObj()
cache[data.id] = data
localStorage.setItem(KEY, JSON.stringify(cache))
}
/**
*
*/
const purge = () => {
localStorage.removeItem(KEY)
}
const fetchYata = async (id: number, apikey: string): Promise<YataApiDataWrap> => {
if (!id || !apikey) {
throw new TypeError('请求yata接口时出错: id和apikey不能为空')
}
const cache = getCache(id)
if (cache) {
return cache
} else {
let responseString: string, response: YataApiResponse
try {
responseString = await CommonUtils.COFetch(`https://yata.yt/api/v1/bs/${ id }?key=${ apikey }`)
} catch (e) {
throw new TypeError('请求yata接口时出错 ' + e.message)
}
try {
response = JSON.parse(responseString)
} catch (e) {
throw new TypeError('解析yata接口响应时出错 ' + e.message)
}
if (response.error) {
switch (response.error.code) {
case 1:
throw new TypeError('请求yata接口时出错: yata服务端错误-' + response.error.error)
case 2:
throw new TypeError('请求yata接口时出错: 脚本逻辑错误-' + response.error.error)
case 3:
throw new TypeError('请求yata接口时出错: 已达到次数限制-' + response.error.error)
case 4:
throw new TypeError('请求yata接口时出错: apikey错误-' + response.error.error)
}
}
const wrapper = <YataApiDataWrap>response[id]
wrapper.id = String(id)
wrapper.isCache = false
setCache(wrapper)
return wrapper
}
}
export { fetchYata, YataApiDataWrap, purge }

View File

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

View File

@ -67,57 +67,57 @@ export default function translateMain(href: string): void {
}; };
// 边栏 // 边栏
let sidebarTimeOut = 60; // let sidebarTimeOut = 60;
const sidebarInterval = setInterval(() => { // const sidebarInterval = setInterval(() => {
// 60秒后取消定时 // // 60秒后取消定时
if ($('div[class^="sidebar"]').length === 0) { // if ($('div[class^="sidebar"]').length === 0) {
sidebarTimeOut--; // sidebarTimeOut--;
if (sidebarTimeOut < 0) { // if (sidebarTimeOut < 0) {
clearInterval(sidebarInterval); // clearInterval(sidebarInterval);
} // }
return; // return;
} // }
// 边栏块标题 // // 边栏块标题
$('h2[class^="header"]').each((i, e) => { // $('h2[class^="header"]').each((i, e) => {
if (!sidebarDict[e.firstChild.nodeValue]) return; // if (!sidebarDict[e.firstChild.nodeValue]) return;
e.firstChild.nodeValue = sidebarDict[e.firstChild.nodeValue]; // e.firstChild.nodeValue = sidebarDict[e.firstChild.nodeValue];
}); // });
// 边栏人物名字 // // 边栏人物名字
$('span[class^="menu-name"]').each((i, e) => { // $('span[class^="menu-name"]').each((i, e) => {
e.firstChild.nodeValue = '名字:'; // e.firstChild.nodeValue = '名字:';
}); // });
// 钱 等级 pt 天赋点 // // 钱 等级 pt 天赋点
$('p[class^="point-block"]').each((i, e) => { // $('p[class^="point-block"]').each((i, e) => {
if (sidebarDict[e.firstChild.firstChild.nodeValue]) // if (sidebarDict[e.firstChild.firstChild.nodeValue])
e.firstChild.firstChild.nodeValue = sidebarDict[e.firstChild.firstChild.nodeValue]; // e.firstChild.firstChild.nodeValue = sidebarDict[e.firstChild.firstChild.nodeValue];
}); // });
// 4条 状态条 // // 4条 状态条
$('p[class^="bar-name"]').each((i, e) => { // $('p[class^="bar-name"]').each((i, e) => {
if (sidebarDict[e.firstChild.nodeValue]) // if (sidebarDict[e.firstChild.nodeValue])
e.firstChild.nodeValue = sidebarDict[e.firstChild.nodeValue]; // e.firstChild.nodeValue = sidebarDict[e.firstChild.nodeValue];
}); // });
// 边栏菜单 // // 边栏菜单
$('span[class^="linkName"]').each((i, e) => { // $('span[class^="linkName"]').each((i, e) => {
if (sidebarDict[e.firstChild.nodeValue]) // if (sidebarDict[e.firstChild.nodeValue])
e.firstChild.nodeValue = sidebarDict[e.firstChild.nodeValue]; // e.firstChild.nodeValue = sidebarDict[e.firstChild.nodeValue];
}); // });
// [use]按钮 // // [use]按钮
if (document.querySelector('#pointsMerits')) // if (document.querySelector('#pointsMerits'))
$('#pointsMerits')[0].firstChild.nodeValue = '[使用]'; // $('#pointsMerits')[0].firstChild.nodeValue = '[使用]';
if (document.querySelector('#pointsPoints')) // if (document.querySelector('#pointsPoints'))
$('#pointsPoints')[0].firstChild.nodeValue = '[使用]'; // $('#pointsPoints')[0].firstChild.nodeValue = '[使用]';
if (document.querySelector('#pointsLevel')) // if (document.querySelector('#pointsLevel'))
$('#pointsLevel')[0].firstChild.nodeValue = '[升级]'; // $('#pointsLevel')[0].firstChild.nodeValue = '[升级]';
//
// 手机 区域菜单 // // 手机 区域菜单
$('div[class*="areas-mobile"] span:nth-child(2)').contents().each((i, e) => { // $('div[class*="areas-mobile"] span:nth-child(2)').contents().each((i, e) => {
//log(e); // //log(e);
if (sidebarDict[e.nodeValue]) // if (sidebarDict[e.nodeValue])
e.nodeValue = sidebarDict[e.nodeValue]; // e.nodeValue = sidebarDict[e.nodeValue];
}); // });
//
clearInterval(sidebarInterval); // clearInterval(sidebarInterval);
}, 1000); // }, 1000);
// header // header
if (document.querySelector('div#header-root')) { if (document.querySelector('div#header-root')) {

View File

@ -0,0 +1,14 @@
const convertToCSV = (data: any[]) => {
let csv = ''
for (let i = 0; i < data.length; i++) {
let row = ''
for (const key in data[i]) {
row += `"${ data[i][key] }",`
}
row = row.slice(0, -1) // 删除最后一个逗号
csv += row + '\r\n' // 添加换行符
}
return csv
}
export { convertToCSV }

View File

@ -22,3 +22,7 @@ export const fetchCurrentMoney = async (action?: string): Promise<number> => {
export const fetchCurrentCompanyAvailableMoney = () => { export const fetchCurrentCompanyAvailableMoney = () => {
return fetchCurrentMoney("companyAction"); return fetchCurrentMoney("companyAction");
}; };
export const fetchCurrentPropVaultAvailableMoney = () => {
return fetchCurrentMoney("propertyDepositAction");
};

View File

@ -0,0 +1,19 @@
import { SessionStoreDataType } from "../../interface/SessionStoreDataType";
/**
* session storage sidebarData
*/
export default function (): SessionStoreDataType {
let json: string = null
for (let i = 0; i < sessionStorage.length; i++) {
let key = sessionStorage.key(i)
if (key.startsWith('sidebarData')) {
json = sessionStorage.getItem(key)
break
}
}
if (!json) {
throw new TypeError('未找到 sidebarData')
}
return JSON.parse(json)
}

View File

@ -0,0 +1,20 @@
const timePastFormat = (ts: number): string => {
// 毫秒
if (ts < 1000) {
return ts + 'ms'
}
// 秒
else if (ts < 60000) {
return (ts / 1000 | 0) + 's'
}
// 分
else if (ts < 3600000) {
return (ts / 60000 | 0) + 'm'
}
// 时
else {
return (ts / 3600000 | 0) + 'h'
}
}
export { timePastFormat }

View File

@ -1,6 +1,17 @@
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus"
const useItem = (itemId: string) => { type CrimeResVo = { text: string, success: boolean }
const defaultCb = (res: CrimeResVo) => {
ElMessage({
message: res.text,
type: res.success ? 'success' : 'error',
dangerouslyUseHTMLString: true,
grouping: true
})
}
const useItem = (itemId: string, cb = defaultCb) => {
fetch(window.addRFC("https://www.torn.com/item.php"), { fetch(window.addRFC("https://www.torn.com/item.php"), {
"headers": { "headers": {
"accept": "*/*", "accept": "*/*",
@ -19,15 +30,22 @@ const useItem = (itemId: string) => {
"credentials": "include" "credentials": "include"
}) })
.then(res => res.json()) .then(res => res.json())
.then(res => ElMessage({ .then(res => cb(res))
message: res.text,
type: res.success ? 'success' : 'error',
dangerouslyUseHTMLString: true
}))
.catch(e => ElMessage({ .catch(e => ElMessage({
message: e.toString, message: e.toString,
type: 'error' type: 'error'
})); }));
}; }
export default useItem; const useItemSync = (itemId: string, showMsg = true, cb = (response: CrimeResVo) => null) => {
return new Promise((res, rej) => {
useItem(itemId, (_res) => {
if (showMsg) defaultCb(_res)
cb(_res)
res(null)
})
})
}
export default useItem
export { useItemSync }

View File

@ -1,5 +1,6 @@
import Popup from "./class/utils/Popup"; import Popup from "./class/utils/Popup";
import { reactive } from 'vue'
import IFeatureResult from "./man/IFeatureResult";
type ResponseHandlers = ((url: string, responseBody: { type ResponseHandlers = ((url: string, responseBody: {
json: unknown, json: unknown,
@ -20,11 +21,14 @@ class GlobVars {
responseHandlers: ResponseHandlers = []; responseHandlers: ResponseHandlers = [];
version = '$$WUHU_DEV_VERSION$$'; version = '$$WUHU_DEV_VERSION$$';
popup_node: MyHTMLElement | Popup = null; popup_node: MyHTMLElement | Popup = null;
actionList: {
txt: string,
func: (ev: Event) => void
}[] = [];
loadTime: number = 0; loadTime: number = 0;
buttons = reactive<FloatButtonData[]>([])
featureStatus = reactive<IFeatureResult[]>([])
} }
export default new GlobVars(); export default new GlobVars();
interface FloatButtonData {
txt: string,
func: (ev: Event) => void
}

View File

@ -1,13 +1,15 @@
import "reflect-metadata"; import "reflect-metadata";
import { Container } from "./container/Container";
import App from "./App"; import App from "./App";
import Logger from "./class/Logger"; import EntryPoint from "./starter/EntryPoint";
import EntryPoint from "./class/provider/EntryPoint"; import { Injectable } from "./container/Injectable";
import ClassName from "./container/ClassName";
import { Container } from "./container/Container";
@EntryPoint @EntryPoint
class _ { @ClassName('Index')
static main() { @Injectable()
Container.setLogger(new Logger()); class Index {
Container.factory(App).run(); public static main() {
Container.factory(App).run()
} }
} }

View File

@ -1,7 +1,7 @@
import Device from "../enum/Device"; import Device from "../enum/Device";
import { BeerMonitorLoop } from "../class/action/BuyBeerHelper"; import { BeerMonitorLoop } from "../feature/BuyBeerHelper";
import Popup from "../class/utils/Popup"; import Popup from "../class/utils/Popup";
import TravelItem from "../class/action/TravelItem"; import TravelItem from "../feature/TravelItem";
export default interface IGlobal { export default interface IGlobal {
GM_xmlhttpRequest?: Function; GM_xmlhttpRequest?: Function;

View File

@ -38,7 +38,10 @@ export default interface IUserProfileData {
} }
}; };
medalInformation: unknown; medalInformation: unknown;
basicInformation: unknown; basicInformation: {
icons: { id: number, title: string }[],
lastAction: { seconds: number }
};
personalInformation: unknown; personalInformation: unknown;
competitionStatus: unknown; competitionStatus: unknown;
staffTools: null; staffTools: null;

View File

@ -0,0 +1,566 @@
/**
* session storage sidebarData+uid
*/
export interface SessionStoreDataType {
sliderHintDisabled: boolean
selectedStatusIcon: any
progressBarFetchActive: boolean
linkWithContextMenu: number
statusIcons: StatusIcons
user: User
listsStatuses: ListsStatuses
listsLinksStatuses: ListsLinksStatuses
bars: Bars
account: Account
areas: Areas
lists: Lists
footer: Footer
dataSync: boolean
sidebarFetched: boolean
currentUrl: string
}
export interface StatusIcons {
size: string
icons: Icons
visible: boolean
onTop: boolean
}
export interface Icons {
enbyGender: EnbyGender
donator: Donator
company: Company
faction: Faction
education: Education
bazaar: Bazaar
stock_market: StockMarket
booster_cooldown?: BoosterCooldown
drug_cooldown?: DrugCooldown
}
export interface EnbyGender {
iconID: string
title: string
}
export interface Donator {
iconID: string
title: string
subtitle: string
link: string
}
export interface Company {
iconID: string
title: string
subtitle: string
link: string
}
export interface Faction {
iconID: string
title: string
subtitle: string
link: string
}
export interface Education {
iconID: string
title: string
subtitle: string
link: string
serverTimestamp: number
timerExpiresAt: number
isShortFormatTimer: boolean
}
export interface Bazaar {
iconID: string
title: string
subtitle: string
link: string
}
export interface StockMarket {
iconID: string
title: string
subtitle: string
link: string
}
export interface BoosterCooldown {
iconID: string
title: string
factionUpgrade: string
serverTimestamp: number
timerExpiresAt: number
isShortFormatTimer: boolean
}
export interface DrugCooldown {
iconID: string
title: string
subtitle: string
serverTimestamp: number
timerExpiresAt: number
isShortFormatTimer: boolean
}
export interface User {
userID: number
name: string
link: string
money: Money
donator: boolean
level: Level
points: Points
merits: Merits
refills: Refills
lifeModifier: number
status: any
}
export interface Money {
value: number
}
export interface Level {
value: number
upgradePossibility: boolean
link: string
}
export interface Points {
value: number
link: string
}
export interface Merits {
value: number
link: string
}
export interface Refills {
value: string
link: string
}
export interface ListsStatuses {
areas: boolean
lists: boolean
}
export interface ListsLinksStatuses {
staff: boolean
friends: boolean
enemies: boolean
}
export interface Bars {
energy: Energy
nerve: Nerve
happy: Happy
life: Life
chain: Chain
}
export interface Energy {
name: string
timestampToUpdate: number
timeToUpdate: number
statstamp: number
amount: number
max: number
step: number
}
export interface Nerve {
name: string
timestampToUpdate: number
timeToUpdate: number
statstamp: number
amount: number
max: number
step: number
}
export interface Happy {
name: string
timestampToUpdate: number
timeToUpdate: number
statstamp: number
amount: number
max: number
step: number
}
export interface Life {
name: string
timestampToUpdate: number
timeToUpdate: any
statstamp: number
amount: number
max: number
step: number
}
export interface Chain {
name: string
timestampToUpdate: number
timeToUpdate: number
amount: number
max: number
step: number
coolDown: number
endCoolDownTimestamp: number
bonuses: number
link: string
}
export interface Account {
messages: Messages
events: Events
awards: Awards
}
export interface Messages {
linkOrder: number
name: string
amount: number
link: string
icon: string
highlightStatus: boolean
status: any
isMobileOnly: boolean
}
export interface Events {
linkOrder: number
name: string
amount: number
link: string
icon: string
highlightStatus: boolean
status: string
isMobileOnly: boolean
}
export interface Awards {
linkOrder: number
name: string
amount: number
link: string
icon: string
highlightStatus: boolean
status: any
isMobileOnly: boolean
}
export interface Areas {
messages: Messages2
events: Events2
awards: Awards2
gym: Gym
crimes: Crimes
items: Items
city: City
missions: Missions
casino: Casino
calendar: Calendar
education: Education2
properties: Properties
friends_list: FriendsList
enemies_list: EnemiesList
elimination: Elimination
community_events: CommunityEvents
}
export interface Messages2 {
linkOrder: number
name: string
amount: number
link: string
icon: string
highlightStatus: boolean
status: any
isMobileOnly: boolean
}
export interface Events2 {
linkOrder: number
name: string
amount: number
link: string
icon: string
highlightStatus: boolean
status: string
isMobileOnly: boolean
}
export interface Awards2 {
linkOrder: number
name: string
amount: number
link: string
icon: string
highlightStatus: boolean
status: any
isMobileOnly: boolean
}
export interface Gym {
linkOrder: number
name: string
shortName: any
link: string
icon: string
styleStatus: any
highlightStatus: boolean
status: any
activeStatusUrlRegex: any
amount: any
isMobileOnly: boolean
}
export interface Crimes {
linkOrder: number
name: string
shortName: any
link: string
icon: string
styleStatus: any
highlightStatus: boolean
status: any
activeStatusUrlRegex: any
amount: any
isMobileOnly: boolean
}
export interface Items {
linkOrder: number
name: string
shortName: any
link: string
icon: string
styleStatus: any
highlightStatus: boolean
status: any
activeStatusUrlRegex: any
amount: any
isMobileOnly: boolean
}
export interface City {
linkOrder: number
name: string
shortName: any
link: string
icon: string
styleStatus: any
highlightStatus: boolean
status: any
activeStatusUrlRegex: any
amount: any
isMobileOnly: boolean
}
export interface Missions {
linkOrder: number
name: string
shortName: any
link: string
icon: string
styleStatus: any
highlightStatus: boolean
status: any
activeStatusUrlRegex: any
amount: any
isMobileOnly: boolean
}
export interface Casino {
linkOrder: number
name: string
shortName: any
link: string
icon: string
styleStatus: any
highlightStatus: boolean
status: any
activeStatusUrlRegex: any
amount: any
isMobileOnly: boolean
}
export interface Calendar {
linkOrder: number
name: string
shortName: string
link: string
icon: string
styleStatus: any
highlightStatus: boolean
status: any
activeStatusUrlRegex: any
amount: any
isMobileOnly: boolean
}
export interface Education2 {
linkOrder: number
name: string
shortName: string
link: string
icon: string
styleStatus: any
highlightStatus: boolean
status: any
activeStatusUrlRegex: any
amount: any
isMobileOnly: boolean
}
export interface Properties {
linkOrder: number
name: string
shortName: string
link: string
icon: string
styleStatus: any
highlightStatus: boolean
status: any
activeStatusUrlRegex: any
amount: any
isMobileOnly: boolean
}
export interface FriendsList {
linkOrder: number
name: string
icon: string
link: string
elements: any
status: any
isMobileOnly: boolean
}
export interface EnemiesList {
linkOrder: number
name: string
icon: string
link: string
elements: Element[]
status: any
isMobileOnly: boolean
}
export interface Element {
name: string
link: string
status: string
lastAction: number
}
export interface Elimination {
linkOrder: number
name: string
shortName: string
link: string
icon: string
styleStatus: string
highlightStatus: boolean
status: any
activeStatusUrlRegex: any
amount: any
isMobileOnly: boolean
}
export interface CommunityEvents {
linkOrder: number
name: string
shortName: string
link: string
icon: string
styleStatus: string
highlightStatus: boolean
status: any
activeStatusUrlRegex: any
amount: any
isMobileOnly: boolean
}
export interface Lists {
company_list: CompanyList
faction_list: FactionList
friends_list: FriendsList2
enemies_list: EnemiesList2
}
export interface CompanyList {
linkOrder: number
name: string
icon: string
link: string
elements: Element2[]
status: any
isMobileOnly: boolean
}
export interface Element2 {
name: string
link: string
status: string
lastAction: number
}
export interface FactionList {
linkOrder: number
name: string
icon: string
link: string
elements: Element3[]
status: any
isMobileOnly: boolean
}
export interface Element3 {
name: string
link: string
status: string
lastAction: number
}
export interface FriendsList2 {
linkOrder: number
name: string
icon: string
link: string
elements: any
status: any
isMobileOnly: boolean
}
export interface EnemiesList2 {
linkOrder: number
name: string
icon: string
link: string
elements: Element4[]
status: any
isMobileOnly: boolean
}
export interface Element4 {
name: string
link: string
status: string
lastAction: number
}
export interface Footer {
serverName: string
dayTextualFormat: string
dateFormat: string
dayTimeFormat: string
}

View File

@ -32,6 +32,7 @@ export interface MiniProfile {
}, },
sendMoneyWarning: string, sendMoneyWarning: string,
playerName: string, playerName: string,
userID: number,
}; };
userStatus: { userStatus: {
status: { status: {

116
src/ts/man/FeatureMan.ts Normal file
View File

@ -0,0 +1,116 @@
import IFeature from "./IFeature"
import { Container } from "../container/Container"
import ClassName, { GetClassName } from "../container/ClassName"
import Atk from "../feature/Atk"
import { Injectable } from "../container/Injectable"
import Logger from "../class/Logger"
import BuyBeerHelper from "../feature/BuyBeerHelper"
import SidebarHelper from "../feature/SidebarHelper"
import TravelItem from "../feature/TravelItem"
import TravelHelper from "../feature/TravelHelper"
import CompanyHelper from "../feature/CompanyHelper"
import IconHelper from "../feature/IconHelper"
import MapItem from "../feature/MapItem"
import PTHelper from "../class/action/PTHelper"
import StackHelper from "../feature/StackHelper"
import XZMZ from "../class/action/XZMZ"
import BeerShopModifier from "../feature/BeerShopModifier"
import CrimePageModifier from "../feature/CrimePageModifier"
import ProfileHelper from "../class/action/ProfileHelper"
import ChristmasTown from "../feature/ChristmasTown"
import LotteryHelper from "../feature/LotteryHelper"
import SlotsHelper from "../class/action/SlotsHelper"
import SearchHelper from "../class/action/SearchHelper"
import { WHIntervalLoader } from "../monitor/WHIntervalLoader"
import IFeatureResult from "./IFeatureResult"
import FeatureStatus from "./const/FeatureStatus"
import globVars from "../globVars";
@ClassName('FeatureMan')
@Injectable()
export default class FeatureMan {
private readonly features: ClassType<IFeature>[] = [
/* 通配 */
BuyBeerHelper,
SidebarHelper,
TravelItem,
CompanyHelper,
IconHelper,
WHIntervalLoader,
/* 页面匹配 */
Atk,
TravelHelper,
MapItem,
PTHelper,
StackHelper,
XZMZ,
BeerShopModifier,
CrimePageModifier,
ProfileHelper,
ChristmasTown,
LotteryHelper,
SlotsHelper,
SearchHelper,
]
private result: IFeatureResult[] = null
private readonly logger = Logger.factory(FeatureMan)
async fStart() {
const manResult: IFeatureResult[] = []
for (let i = 0; i < this.features.length; i++) {
const className = GetClassName(this.features[i])
const instant = Container.factory(this.features[i])
const description = instant.description()
const urlPath = location.href.replace(/^.+\/\/.+?\/(?!\/)?/, '/')
let status = FeatureStatus.ADDED
let isMatch = false
const urlIncludes = instant.urlIncludes()
let load = 0
for (let j = 0; j < urlIncludes.length; j++) {
if (urlIncludes[j].test(urlPath)) {
isMatch = true
const urlExcludes = instant.urlExcludes() ?? []
for (let k = 0; k < urlExcludes.length; k++) {
if (urlExcludes[k].test(urlPath)) {
isMatch = false
status = FeatureStatus.EXCLUDED
break
}
}
break
}
}
if (isMatch) {
try {
const start = performance.now()
let run = instant.iStart()
if (run instanceof Promise) {
run = await run
}
status = FeatureStatus.RUNNING
load = ((performance.now() - start) * 10 | 0) / 10
} catch (e) {
this.logger.error(e.message)
status = FeatureStatus.EXCEPTION
}
}
// else {
// status = FeatureStatus.EXCLUDED
// }
const result = {
desc: description,
status: status,
clazz: className,
load
}
manResult.push(result)
globVars.featureStatus.push(result)
}
this.result = manResult
}
printTable() {
console.table(this.result)
this.logger.info(this.result)
}
}

15
src/ts/man/IFeature.ts Normal file
View File

@ -0,0 +1,15 @@
export default interface IFeature {
iStart(): void | Promise<void>
/**
* urlnull
*/
urlIncludes(): RegExp[]
/**
* url
*/
urlExcludes(): RegExp[]
description(): string
}

View File

@ -0,0 +1,8 @@
import FeatureStatus from "./const/FeatureStatus";
export default interface IFeatureResult {
desc: string
status: FeatureStatus
clazz: string
load: number
}

View File

@ -0,0 +1,8 @@
enum FeatureStatus {
ADDED = '已添加',
RUNNING = '正常',
EXCLUDED = '页面排除',
EXCEPTION = '异常',
}
export default FeatureStatus

View File

@ -0,0 +1,47 @@
import IntervalType from "./WHIntervalLoader";
import ClassName from "../container/ClassName";
import IntervalSwitch from "./IntervalSwitch";
import { SWITCHER } from "./SWITCHER";
import getSidebarData from "../func/utils/getSidebarData";
import { Container } from "../container/Container";
import MsgWrapper from "../class/utils/MsgWrapper";
import LocalConfigWrapper from "../class/LocalConfigWrapper";
@ClassName('DrugCDMonitor')
class DrugCDMonitor implements IntervalType {
private id: number = null
private ms: number
private readonly msgWrapper = Container.factory(MsgWrapper)
getId(): number {
return this.id
}
getMs(): number {
return this.ms
}
handler(): void {
const data = getSidebarData()
if (data?.statusIcons?.icons?.drug_cooldown?.timerExpiresAt * 1000 < Date.now()) {
this.msgWrapper.create('警告: 药CD停转', { sysNotify: true })
}
}
setMs(ms: number): void {
this.ms = ms
}
switcher(s: IntervalSwitch): void {
SWITCHER(this, DrugCDMonitor, s)
}
setId(id: number): void {
this.id = id
}
}
const drugCDMonitor = new DrugCDMonitor()
drugCDMonitor.setMs(Container.factory(LocalConfigWrapper).config.drugCDMonitorInterval)
export default drugCDMonitor

View File

@ -0,0 +1,5 @@
enum IntervalSwitch {
ON = 9, OFF = 10
}
export default IntervalSwitch

View File

@ -0,0 +1,24 @@
import { GetClassName } from "../container/ClassName";
import IntervalType from "./WHIntervalLoader";
import IntervalSwitch from "./IntervalSwitch";
export function SWITCHER(self: IntervalType, clazz: new () => IntervalType, s: IntervalSwitch) {
if (self.getMs() === null) {
throw new Error('循环时间为空')
}
switch (s) {
case IntervalSwitch.ON:
if (self.getId()) {
throw new TypeError(GetClassName(clazz) + ' 正在启用')
}
self.setId(window.setInterval(() => self.handler(), self.getMs()))
break
case IntervalSwitch.OFF:
if (self.getId()) {
window.clearInterval(self.getId())
self.setId(null)
} else {
throw new TypeError(GetClassName(clazz) + ' 未启用')
}
}
}

View File

@ -0,0 +1,57 @@
/**
*
*/
import drugCDMonitor from "./DrugCDMonitor";
import { Injectable } from "../container/Injectable";
import ClassName from "../container/ClassName";
import LocalConfigWrapper from "../class/LocalConfigWrapper";
import IntervalSwitch from "./IntervalSwitch";
import IFeature from "../man/IFeature";
import ATK_PAGE_REG from "../feature/url/ATK_PAGE_REG";
import ALL_PAGE_REG from "../feature/url/ALL_PAGE_REG";
export default interface IntervalType {
handler: () => void
switcher: (s: IntervalSwitch) => void
getId: () => number
setId: (id: number) => void
getMs: () => number
setMs: (ms: number) => void
}
const intervalUnits: { [key: string]: IntervalType } = {
drugCDMonitor,
}
@Injectable()
@ClassName('WHIntervalLoader')
export class WHIntervalLoader implements IFeature {
description(): string {
return "新监控模块";
}
iStart(): void | Promise<void> {
this.init()
}
urlExcludes(): RegExp[] {
return [ATK_PAGE_REG];
}
urlIncludes(): RegExp[] {
return [ALL_PAGE_REG];
}
constructor(
private readonly localConfigWrapper: LocalConfigWrapper,
) {
}
init() {
const list = this.localConfigWrapper.config.monitorOn
for (let i = 0; i < list.length; i++) {
intervalUnits[list[i]].switcher(IntervalSwitch.ON)
}
}
}

View File

@ -1,32 +1,30 @@
import "reflect-metadata"; import "reflect-metadata";
import { Container } from "../../container/Container"; import Logger from "../class/Logger";
import Logger from "../Logger"; import globVars from "../globVars";
import globVars from "../../globVars"; import { Container } from "../container/Container";
export default function EntryPoint(T: { main: () => void }) { export default function EntryPoint(Index: ClassType<{}> & { main: () => void }) {
const logger = Logger.factory(Index);
if (window.WHTRANS) { if (window.WHTRANS) {
console.log('退出, 已运行次数' + window.WHTRANS) logger.warn('退出, 已运行次数' + window.WHTRANS)
} else { } else {
window.WHTRANS = window.WHTRANS === undefined ? 1 : window.WHTRANS++; window.WHTRANS = window.WHTRANS === undefined ? 1 : window.WHTRANS++;
const logger = Container.factory(Logger);
let started = false; let started = false;
const starter = () => { const starter = () => {
console.log('starter init...'); console.log('starter init...');
let started = performance.now(); let started = performance.now();
try { try {
T.main(); Container.setLogger(Container.factory(Logger))
Index.main()
} catch (e) { } catch (e) {
logger.error('[Starter]加载出错信息: ' + e.stack || e.message); logger.error('加载出错: ' + e.stack || e.message);
} }
let runTime: number = (performance.now() - started) | 0; let runTime: number = (performance.now() - started) | 0;
globVars.loadTime = runTime; globVars.loadTime = runTime;
logger.info(`芜湖脚本完成加载, 耗时${ runTime }ms`); logger.info(`芜湖脚本完成加载, 耗时${ runTime }ms`);
// if (ZhongIcon.ZhongNode && ZhongIcon.ZhongNode.initTimer)
// ZhongIcon.ZhongNode.initTimer.innerHTML = `加载时间 ${ runTime }ms`;
}; };
const evHandler = () => { const evHandler = () => {
// console.log('document.readyState: ' + document.readyState);
if (!started && (document.readyState === 'complete' || document.readyState === 'interactive')) { if (!started && (document.readyState === 'complete' || document.readyState === 'interactive')) {
document.removeEventListener('readystatechange', evHandler); document.removeEventListener('readystatechange', evHandler);
started = !started; started = !started;

View File

@ -4,7 +4,6 @@ import ClassName from "../container/ClassName";
import Debug from "../class/provider/Debug"; import Debug from "../class/provider/Debug";
import "reflect-metadata"; import "reflect-metadata";
import { Injectable } from "../container/Injectable"; import { Injectable } from "../container/Injectable";
import { ButtonHandler } from "../class/provider/ButtonHandler";
import { Container } from "../container/Container"; import { Container } from "../container/Container";
import NetHighLvlWrapper from "../class/utils/NetHighLvlWrapper"; import NetHighLvlWrapper from "../class/utils/NetHighLvlWrapper";
import Logger from "../class/Logger"; import Logger from "../class/Logger";
@ -12,7 +11,6 @@ import QuickGymTrain from "../class/action/QuickGymTrain";
import LocalConfigWrapper from "../class/LocalConfigWrapper"; import LocalConfigWrapper from "../class/LocalConfigWrapper";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
@ButtonHandler
@ClassName('Test') @ClassName('Test')
@Injectable() @Injectable()
class Test { class Test {

262
src/vue/BSEstView.vue Normal file
View File

@ -0,0 +1,262 @@
<script lang="ts" setup>
import { inject, onMounted, ref } from "vue"
import { LocalConfigWrapperKey } from "../ts/class/LocalConfigWrapper"
import { ElMessage } from "element-plus"
import { fetchYata, purge, YataApiDataWrap } from "../ts/func/module/fetchYata"
import { LoggerKey } from "../ts/class/Logger"
import toThousands from "../ts/func/utils/toThousands"
import { Download, QuestionFilled, Refresh, Search } from "@element-plus/icons-vue"
import { timePastFormat } from "../ts/func/utils/timePastFormat"
import { convertToCSV } from "../ts/func/utils/convert2Csv"
const isMiniProfOn = ref<boolean>(false)
const isProfOn = ref<boolean>(false)
const apikey = ref<string>('')
const targetId = ref<string>('')
const tableData = ref<Partial<YataApiDataWrap>[]>([])
const isLoading = ref(false)
const idKey: { [key: string]: number } = {}
const buildType = { Offensive: '攻击型', Defensive: '防御型', Balanced: '平衡型' }
const localConfigWrapper = inject(LocalConfigWrapperKey)
const logger = inject(LoggerKey)
const saveApikey = () => {
if (!apikey.value) {
ElMessage({
message: 'apikey保存时输入为空',
type: 'error',
showClose: true
})
throw new TypeError('apikey保存时输入为空')
}
localStorage.setItem('APIKey', apikey.value)
ElMessage.success({
message: 'APIKey设置成功',
showClose: true
})
}
const onMiniProfSwitchChange = () => localConfigWrapper.config.isBSEstMiniProfOn = isMiniProfOn.value
const onProfSwitchChange = () => localConfigWrapper.config.isBSEstProfOn = isProfOn.value
const doRequest = async () => {
if (!apikey.value) {
ElMessage.warning({
message: '未设置APIKey',
showClose: true
})
return
}
isLoading.value = true
for (let i = 0; i < tableData.value.length; i++) {
const item = tableData.value[i]
if (!item.total) {
let est: YataApiDataWrap
try {
est = await fetchYata(parseInt(item.id), apikey.value)
} catch (e) {
ElMessage.error({
message: e.message,
showClose: true
})
logger.error(e.stack)
continue
}
item.total = est.total
item.score = est.score
item.type = est.type
item.skewness = est.skewness
item.timestamp = est.timestamp
item.version = est.version
item.isCache = est.isCache
}
}
isLoading.value = false
}
/**
* 查询列表中添加新的id
* @param _id
*/
const add = (_id: string) => {
const id = parseInt(_id)
if (id) {
// id
if (!idKey[_id]) {
tableData.value.push({ id: _id })
idKey[_id] = 1
targetId.value = ''
} else {
ElMessage.warning({
message: '重复的id',
showClose: true
})
}
} else {
ElMessage.error({
message: 'id有误',
showClose: true
})
}
}
/**
* 清空查询列表和唯一id表
*/
const emptyList = () => {
tableData.value = []
const keys = Object.keys(idKey)
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
idKey[key] = 0
}
}
const purgeCache = () => {
purge()
ElMessage.warning({
message: '本地缓存已清除',
showClose: true
})
}
const exportCsv = () => {
const exportList: any[] = [{
id: "ID", total: "BS", score: "评分", type: "风格", skewness: "偏差", timestamp: "时间戳"
}]
for (let i = 0; i < tableData.value.length; i++) {
const item = tableData.value[i]
const newItem = {
id: item.id ?? '-1',
total: item.total ?? null,
score: item.score ?? null,
type: buildType[item.type] ?? null,
skewness: item.skewness ?? null,
timestamp: item.timestamp ?? null,
// isCache: item.isCache ?? null,
// version: item.version ?? null
}
exportList.push(newItem)
}
const csvContent = convertToCSV(exportList)
let url: string
let isBlob = false
const UTF8 = "\uFEFF"
if (window.Blob && window.URL && window.URL.createObjectURL) {
const csvData = new window.Blob([UTF8 + csvContent], {
type: 'text/csv'
})
url = window.URL.createObjectURL(csvData)
isBlob = true
} else {
url = window.encodeURI(csvContent)
}
window.location.href = isBlob ? url : 'data:text/csv;charset=utf-8,' + UTF8 + url
}
onMounted(() => {
// APIKey
const key = localStorage.getItem('APIKey')
if (!key) {
ElMessage.error({
message: '尚未配置APIKey',
showClose: true
})
} else {
apikey.value = key
}
//
isMiniProfOn.value = localConfigWrapper.config.isBSEstMiniProfOn
isProfOn.value = localConfigWrapper.config.isBSEstProfOn
})
</script>
<template>
<el-space :fill="true">
<el-card shadow="never">
<template #header>设置</template>
<el-form label-width="auto" @submit.prevent>
<el-form-item label="MINI资料卡中显示">
<el-switch v-model="isMiniProfOn" @change="onMiniProfSwitchChange"/>
</el-form-item>
<el-form-item label="个人资料中显示">
<el-switch v-model="isProfOn" @change="onProfSwitchChange"/>
</el-form-item>
<el-form-item label="API KEY">
<el-input v-model="apikey">
<template #append>
<el-button :loading="isLoading" @click="saveApikey">保存</el-button>
</template>
</el-input>
</el-form-item>
<el-form-item>
<el-popconfirm cancel-button-text="取消" confirm-button-text="确定" title="确定操作" @confirm="purgeCache">
<template #reference>
<el-button type="danger">清空缓存</el-button>
</template>
</el-popconfirm>
</el-form-item>
</el-form>
</el-card>
<el-card shadow="never">
<template #header>使用</template>
<p class="mt-4">BS估算功能使用了
<el-link href="https://yata.yt/api/v1/" target="_blank">yata</el-link>
神经网络模型接口需要提供torn apikey (上面输入)
</p>
<p class="mt-4">本插件缓存机制1</p>
<el-form label-width="auto" @submit="add(targetId)" @submit.prevent>
<el-form-item label="目标数字ID">
<el-input v-model="targetId" clearable/>
</el-form-item>
<el-form-item>
<el-button :loading="isLoading" type="primary" @click="add(targetId)">添加</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card shadow="never">
<template #header>结果</template>
<el-button-group>
<el-button :icon="Search" :loading="isLoading" type="primary" @click="doRequest">估算</el-button>
<el-button :icon="Refresh" :loading="isLoading" @click="emptyList">清空</el-button>
<el-button :icon="Download" :loading="isLoading" @click="exportCsv">导出CSV</el-button>
</el-button-group>
<el-row>
<el-col :span="23">
<el-table :data="tableData" empty-text=" ">
<el-table-column label="ID" prop="id"/>
<el-table-column label="BS">
<template #default="scope">
{{ toThousands(scope.row.total) }}
</template>
</el-table-column>
<el-table-column label="评分" prop="score"/>
<el-table-column label="风格">
<template #default="scope">
{{ buildType[scope.row.type] }}
</template>
</el-table-column>
<el-table-column>
<template #header>
<el-tooltip content="越低越准" effect="light" placement="top">
<span>偏差<el-icon><QuestionFilled/></el-icon></span>
</el-tooltip>
</template>
<template #default="scope">
{{ scope.row.skewness }}%
</template>
</el-table-column>
<el-table-column label="时间">
<template #default="scope">
{{ timePastFormat(Date.now() - scope.row.timestamp * 1000) }}
</template>
</el-table-column>
</el-table>
</el-col>
</el-row>
</el-card>
</el-space>
</template>
<style scoped>
</style>

54
src/vue/ChangeLogView.vue Normal file
View File

@ -0,0 +1,54 @@
<template>
<el-card shadow="never">
<el-row>
<el-col span="24">
<el-button @click="refresh">刷新</el-button>
</el-col>
</el-row>
<el-row>
<el-col span="24">
<el-link href="https://gitlab.com/JJins/wuhu-torn-helper/-/raw/dev/CHANGELOG.md" target="_blank">
https://gitlab.com/JJins/wuhu-torn-helper/-/raw/dev/CHANGELOG.md
</el-link>
</el-col>
</el-row>
<el-row>
<el-col span="24">
<div v-loading="!respond" class="wh-change-log-view-render" v-html="html"></div>
</el-col>
</el-row>
</el-card>
</template>
<script lang="ts" setup>
import { onMounted, ref } from "vue";
import CommonUtils from "../ts/class/utils/CommonUtils";
import { Container } from "../ts/container/Container";
import MDUtils from "../ts/class/utils/MDUtils";
const mdUtils = Container.factory(MDUtils)
const respond = ref('')
const html = ref('')
const refresh = async () => {
respond.value = ''
html.value = ''
try {
respond.value = await CommonUtils.COFetch('https://gitlab.com/JJins/wuhu-torn-helper/-/raw/dev/CHANGELOG.md?' + performance.now())
html.value = mdUtils.parse(respond.value).innerHTML
} catch (e) {
html.value = '错误: ' + e.message
}
}
onMounted(() => {
refresh()
})
</script>
<style>
.wh-change-log-view-render h2, .wh-change-log-view-render h3, .wh-change-log-view-render h4 {
margin-top: 20px;
}
</style>

View File

@ -124,6 +124,13 @@ onMounted(() => {
</template> </template>
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-button-group>
<el-button type="primary" @click="()=>inputMoney='1000000'">1m</el-button>
<el-button type="primary" @click="()=>inputMoney='2000000'">2m</el-button>
<el-button type="primary" @click="()=>inputMoney='5000000'">5m</el-button>
<el-button type="primary" @click="()=>inputMoney='10000000'">10m</el-button>
<el-button type="primary" @click="()=>inputMoney='20000000'">20m</el-button>
</el-button-group>
</el-form> </el-form>
</template> </template>

View File

@ -1,10 +1,10 @@
<script lang="ts" setup> <script lang="ts" setup>
import { inject, onMounted, Ref, ref } from "vue"; import { inject, onMounted, Ref, ref } from "vue"
import { LoggerKey } from "../ts/class/Logger"; import { LoggerKey } from "../ts/class/Logger"
const logger = inject(LoggerKey); const logger = inject(LoggerKey)
const loading = ref(true); const loading = ref(true)
const doFetch = () => fetch("https://www.torn.com/page.php", { const doFetch = () => fetch(window.addRFC("https://www.torn.com/page.php?sid=eventsData"), {
"headers": { "headers": {
"accept": "*/*", "accept": "*/*",
"sec-ch-ua-mobile": "?0", "sec-ch-ua-mobile": "?0",
@ -24,7 +24,7 @@ const doFetch = () => fetch("https://www.torn.com/page.php", {
"mode": "cors", "mode": "cors",
"credentials": "include" "credentials": "include"
}); });
const events: Ref<{ ID: string, message: string, time: number, isNew: boolean }[]> = ref([]); const events: Ref<{ ID: string, message: string, time: string, date: string, isNew: boolean, ts: number }[]> = ref([]);
const DateTimeFormatter = (ts: number) => { const DateTimeFormatter = (ts: number) => {
let date = new Date(ts); let date = new Date(ts);
return date.toLocaleDateString() + ' ' + date.toLocaleTimeString(); return date.toLocaleDateString() + ' ' + date.toLocaleTimeString();
@ -36,11 +36,26 @@ const loadData = async () => {
try { try {
res = await (await doFetch()).json(); res = await (await doFetch()).json();
if (res.success) { if (res.success) {
events.value = res.events; events.value = res.list;
if (res['newEventsAmount']) { let newEventsAmount = res['newEventsAmount']
for (let i = 0; i < res['newEventsAmount']; i++) { for (let i = 0; i < res.list.length; i++) {
events.value[i].isNew = true; if (i < newEventsAmount) {
events.value[i].isNew = true
} }
let date = new Date(0)
try {
let dateResponseSplit = events.value[i].date.split('/')
let timeResponseSplit = events.value[i].time.split(':')
date.setUTCDate(parseInt(dateResponseSplit[0]))
date.setUTCMonth(parseInt(dateResponseSplit[1]) - 1)
date.setUTCFullYear(2000 + parseInt(dateResponseSplit[2]))
date.setUTCHours(parseInt(timeResponseSplit[0]))
date.setUTCMinutes(parseInt(timeResponseSplit[1]))
date.setUTCSeconds(parseInt(timeResponseSplit[2]))
} catch (e) {
logger.error('未能解析时间', e)
}
events.value[i].ts = date.getTime()
} }
loading.value = false; loading.value = false;
} }
@ -57,7 +72,7 @@ onMounted(loadData);
<el-divider/> <el-divider/>
<el-timeline v-if="!loading"> <el-timeline v-if="!loading">
<el-timeline-item v-for="ev in events" :color="ev.isNew?'#0bbd87':''" <el-timeline-item v-for="ev in events" :color="ev.isNew?'#0bbd87':''"
:timestamp="DateTimeFormatter(ev.time*1000)"> :timestamp="DateTimeFormatter(ev.ts)">
<span v-html="ev.message"></span> <span v-html="ev.message"></span>
</el-timeline-item> </el-timeline-item>
</el-timeline> </el-timeline>

View File

@ -6,7 +6,7 @@
<MoonNight/> <MoonNight/>
</el-icon> </el-icon>
</el-button> </el-button>
<el-button v-for="item in _globVars.actionList" @click="item.func">{{ item.txt }}</el-button> <el-button v-for="item in globVars.buttons" @click="item.func">{{ item.txt }}</el-button>
<el-button v-if="editableTabs.length > 0" circle @click="showDrawer"> <el-button v-if="editableTabs.length > 0" circle @click="showDrawer">
<el-badge :value="editableTabs.length" type="primary"> <el-badge :value="editableTabs.length" type="primary">
<el-icon> <el-icon>
@ -15,36 +15,18 @@
</el-badge> </el-badge>
</el-button> </el-button>
</el-button-group> </el-button-group>
<el-dialog v-model="drawer" :fullscreen="isMobilePhone" :lock-scroll="true" <el-dialog v-model="drawer" :fullscreen="isMobilePhone" :lock-scroll="true" width="65%">
width="65%">
<el-tabs v-model="editableTabsValue" closable style="margin-top: -1em" type="border-card" @tab-remove="removeTab"> <el-tabs v-model="editableTabsValue" closable style="margin-top: -1em" type="border-card" @tab-remove="removeTab">
<el-tab-pane v-for="item in editableTabs" :key="item.name" :label="item.title" :name="item.name"> <el-tab-pane v-for="item in editableTabs" :key="item.name" :label="item.title" :name="item.name">
<component :is="item.content"/> <component :is="item.content"/>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
</el-dialog> </el-dialog>
<el-drawer v-model="expanded" :show-close="false" :size="isMobilePhone ? '85%' : '30%'" direction="rtl"> <el-drawer v-model="expanded" :show-close="false" :size="isMobilePhone ? '85%' : '30%'" class="whDrawer"
<template #header="{ close, titleId, titleClass }"> :with-header="false" direction="rtl">
<div :id="titleId" :class="titleClass"> <el-card :body-style="{ 'padding': '0' }" class="innerCard" style="margin-bottom: 0.5em">
<el-tooltip content="更新?" placement="bottom-start"> <template #header>
<el-button link @click="menuClick({ title: '更新', template: UpdateDate })">芜湖助手</el-button> <div>快捷动作</div>
</el-tooltip>
<el-row>
<el-tag size="small" type="info">{{
globVars.version.startsWith('$') ? 'dev' : 'v' + globVars.version
}}
</el-tag>
</el-row>
<el-row v-if="_globVars.loadTime">
<el-text size="small">{{ _globVars.loadTime }}ms</el-text>
</el-row>
</div>
<el-button type="danger" @click="close">
<el-icon>
<CircleCloseFilled/>
</el-icon>
关闭
</el-button>
</template> </template>
<el-menu :unique-opened="true"> <el-menu :unique-opened="true">
<el-sub-menu index="1"> <el-sub-menu index="1">
@ -55,16 +37,16 @@
<el-sub-menu v-for="(item, i) in travelData" :index="'1-' + (i + 1)"> <el-sub-menu v-for="(item, i) in travelData" :index="'1-' + (i + 1)">
<template #title>{{ item.cName }}</template> <template #title>{{ item.cName }}</template>
<el-menu-item :index="'1-' + (i + 1) + '1'" @click="travelConfirm(item.index, 0)"> <el-menu-item :index="'1-' + (i + 1) + '1'" @click="travelConfirm(item.index, 0)">
普通飞机 普通飞机
</el-menu-item> </el-menu-item>
<el-menu-item :index="'1-' + (i + 1) + '2'" @click="travelConfirm(item.index, 1)"> <el-menu-item :index="'1-' + (i + 1) + '2'" @click="travelConfirm(item.index, 1)">
PI飞机 PI飞机
</el-menu-item> </el-menu-item>
<el-menu-item :index="'1-' + (i + 1) + '3'" @click="travelConfirm(item.index, 2)"> <el-menu-item :index="'1-' + (i + 1) + '3'" @click="travelConfirm(item.index, 2)">
股票飞机 👍 股票飞机
</el-menu-item> </el-menu-item>
<el-menu-item :index="'1-' + (i + 1) + '4'" @click="travelConfirm(item.index, 3)"> <el-menu-item :index="'1-' + (i + 1) + '4'" @click="travelConfirm(item.index, 3)">
商务飞机(机票或内衣店) 🥵 商务飞机(机票或内衣店)
</el-menu-item> </el-menu-item>
</el-sub-menu> </el-sub-menu>
</el-sub-menu> </el-sub-menu>
@ -73,110 +55,131 @@
<el-icon>💪</el-icon> <el-icon>💪</el-icon>
<span>锻炼</span> <span>锻炼</span>
</template> </template>
<el-menu-item @click="quickGymTrain.doTrain(BATTLE_STAT.STR)">力量 <el-menu-item @click="quickGymTrain.doTrain(BATTLE_STAT.STR)">力量 STR
</el-menu-item> </el-menu-item>
<el-menu-item @click="quickGymTrain.doTrain(BATTLE_STAT.DEF)">防御 <el-menu-item @click="quickGymTrain.doTrain(BATTLE_STAT.DEF)">防御 DEF
</el-menu-item> </el-menu-item>
<el-menu-item @click="quickGymTrain.doTrain(BATTLE_STAT.SPD)">速度 <el-menu-item @click="quickGymTrain.doTrain(BATTLE_STAT.SPD)">速度 SPD
</el-menu-item> </el-menu-item>
<el-menu-item @click="quickGymTrain.doTrain(BATTLE_STAT.DEX)">闪避 <el-menu-item @click="quickGymTrain.doTrain(BATTLE_STAT.DEX)">闪避 DEX
</el-menu-item> </el-menu-item>
</el-sub-menu> </el-sub-menu>
<el-sub-menu index="3"> <el-sub-menu index="3">
<template #title> <template #title>
<el-icon>🪓</el-icon> <el-icon></el-icon>
<span>老功能</span> <span>REFILL</span>
</template> </template>
<el-menu-item index="3-1" @click="_adHelper">📜 传单助手</el-menu-item> <el-menu-item @click="handleRefil('refillEnergy')">能量E
<el-menu-item index="3-2" @click="_safeKeeper">🛡 守望者</el-menu-item>
<el-menu-item index="3-3" @click="MUZHUANG">🌲 寻找木桩</el-menu-item>
<el-menu-item index="3-4" @click="_setting"> 助手设置</el-menu-item>
</el-sub-menu>
<el-menu-item v-for="(item, i) in menuItemList" :index="(4 + i).toString()" @click="menuClick(item)">
<el-icon>{{ item.title.slice(0, 2) }}</el-icon>
<span>{{ item.title.slice(2, item.title.length) }}</span>
</el-menu-item> </el-menu-item>
<el-menu-item @click="handleRefil('refillNerve')">犯罪N
</el-menu-item>
<el-menu-item @click="handleRefil('refillCasinoTokens')">赌场代币
</el-menu-item>
</el-sub-menu>
</el-menu> </el-menu>
</el-card>
<el-card :body-style="{ 'padding': '4px' }" class="innerCard">
<template #header>
<div>快捷功能</div>
</template>
<el-row style="display: flex;">
<el-col v-for="item in menuItemList" class="featureCol">
<el-card :body-class="'featureMenuBody'" class="featureMenu" shadow="never" @click="menuClick(item)">
<div>
<el-icon class="icon">{{ item.title.slice(0, 2) }}</el-icon>
</div>
<div class="title">
<span>{{ item.title.slice(2, item.title.length) }}</span>
</div>
</el-card>
</el-col>
</el-row>
</el-card>
<div style="margin-top: 1em; text-align: center">
<el-row>
<el-col :span="24">
<el-tooltip content="更新?" placement="bottom-start">
<el-button link @click="menuClick({ title: '关于助手', template: UpdateDate })">芜湖助手
<el-icon>
<Refresh/>
</el-icon>
</el-button>
</el-tooltip>
<el-tag size="small" type="info">{{
globVars.version.startsWith('$') ? 'dev' : 'v' + globVars.version
}}
</el-tag>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-text v-if="_globVars.loadTime" size="small">
{{ _globVars.loadTime }}ms
</el-text>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-text size="small">
2021-{{ new Date().getFullYear() }}
</el-text>
</el-col>
</el-row>
</div>
</el-drawer> </el-drawer>
</el-config-provider> </el-config-provider>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { CircleCloseFilled, CopyDocument, MoonNight } from "@element-plus/icons-vue"; import { CopyDocument, MoonNight, Refresh } from "@element-plus/icons-vue"
import { ElMessage, ElMessageBox } from "element-plus"; import { ElMessage, ElMessageBox } from "element-plus"
import { Component, inject, onMounted, ref, shallowRef, triggerRef } from 'vue'; import { Component, inject, onMounted, ref, shallowRef, triggerRef } from 'vue'
import { LoggerKey } from "../ts/class/Logger"; import { LoggerKey } from "../ts/class/Logger"
import { QuickGymTrainKey } from "../ts/class/action/QuickGymTrain"; import { QuickGymTrainKey } from "../ts/class/action/QuickGymTrain"
import { ChangeLogHandler } from "../ts/class/handler/ChangeLogHandler"; import { QuickFlyBtnHandlerKey } from "../ts/class/handler/QuickFlyBtnHandler"
import { QuickFlyBtnHandlerKey } from "../ts/class/handler/QuickFlyBtnHandler"; import SettingsHandler from "../ts/class/handler/SettingsHandler"
import SettingsHandler from "../ts/class/handler/SettingsHandler"; import { BATTLE_STAT } from "../ts/class/utils/NetHighLvlWrapper"
import { BATTLE_STAT } from "../ts/class/utils/NetHighLvlWrapper"; import adHelper from "../ts/func/module/adHelper"
import { Container } from "../ts/container/Container"; import safeKeeper from "../ts/func/module/safeKeeper"
import adHelper from "../ts/func/module/adHelper"; import getSidebarData from "../ts/func/utils/getSidebarData"
import safeKeeper from "../ts/func/module/safeKeeper"; import useItem from "../ts/func/utils/useItem"
import useItem from "../ts/func/utils/useItem"; import globVars from "../ts/globVars"
import globVars from "../ts/globVars"; import AutoLoginForm from "./AutoLoginForm.vue"
import AutoLoginForm from "./AutoLoginForm.vue"; import ChangeLogView from "./ChangeLogView.vue"
import CityUItems from "./CityUItems.vue"; import CityUItems from "./CityUItems.vue"
import CompanyWithdraw from "./CompanyWithdraw.vue"; import CompanyWithdraw from "./CompanyWithdraw.vue"
import EventsViewer from "./EventsViewer.vue"; import EventsViewer from "./EventsViewer.vue"
import ForeignStock from "./ForeignStock.vue"; import ForeignStock from "./ForeignStock.vue"
import MarketHelper from "./MarketHelper.vue"; import InventoryView from "./InventoryView.vue"
import PTMarketView from "./PTMarketView.vue"; import MarketHelper from "./MarketHelper.vue"
import QuickCrime from "./QuickCrime.vue"; import MonitorMgrView from "./MonitorMgrView.vue"
import UpdateDate from "./UpdateScript.vue"; import PTMarketView from "./PTMarketView.vue"
import VirusProgramming from "./VirusProgramming.vue"; import QuickCrime from "./QuickCrime.vue"
import InventoryView from "./InventoryView.vue"; import UpdateDate from "./UpdateScript.vue"
import VirusProgramming from "./VirusProgramming.vue"
import PropertyVault from "./PropertyVault.vue";
import BSEstView from "./BSEstView.vue";
const logger = inject(LoggerKey); const logger = inject(LoggerKey)
const quickGymTrain = inject(QuickGymTrainKey); const quickGymTrain = inject(QuickGymTrainKey)
const quickFlyBtnHandler = inject(QuickFlyBtnHandlerKey); const quickFlyBtnHandler = inject(QuickFlyBtnHandlerKey)
type MenuItem = { title: string, template?: Component, handler?: Function }; type MenuItem = { title: string, template?: Component, handler?: Function }
const menuItemList: MenuItem[] = [ const menuItemList: MenuItem[] = [
{ {
title: '💊 吃 XAN', title: '💊 吃 XAN',
handler: () => useItem('206'), handler: () => {
useItem('206')
const sidebarData = getSidebarData()
const cd = ((sidebarData.statusIcons.icons.drug_cooldown?.timerExpiresAt - sidebarData.statusIcons.icons.drug_cooldown?.serverTimestamp) / 36 | 0) / 100
ElMessage({ message: '毒 CD: ' + cd + 'h', showClose: true })
}, },
{
title: '🍺 喝啤酒',
handler: () => useItem('180'),
},
{
title: '♻️ REFILL',
handler: () => fetch(window.addRFC("https://www.torn.com/points.php?step=pointsbuy&action=energyrefill2"), {
"headers": {
"accept": "text/plain, */*; q=0.01",
"sec-ch-ua-mobile": "?0",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
"x-requested-with": "XMLHttpRequest"
},
"referrer": "https://www.torn.com/points.php",
"referrerPolicy": "strict-origin-when-cross-origin",
"body": null,
"method": "GET",
"mode": "cors",
"credentials": "include"
})
.then(res => res.json())
.then(res => ElMessage({
message: `<p>${ res.msg }</p><p>剩余refill: ${ res["refills"] }</p>`,
type: res.state === 'done' ? 'success' : 'error',
dangerouslyUseHTMLString: true
}))
.catch(e => ElMessage.error(e.toString)),
}, },
{ {
title: '🚓 快速犯罪', title: '🚓 快速犯罪',
template: QuickCrime, template: QuickCrime,
}, },
{
title: '🌸 飞花库存',
template: ForeignStock,
},
{ {
title: '📢 浏览通知', title: '📢 浏览通知',
template: EventsViewer, template: EventsViewer,
@ -185,14 +188,6 @@ const menuItemList: MenuItem[] = [
title: '🚮 地图垃圾', title: '🚮 地图垃圾',
template: CityUItems, template: CityUItems,
}, },
{
title: '🩼 配置自动登陆',
template: AutoLoginForm,
},
{
title: '💻 PC',
template: VirusProgramming,
},
{ {
title: '🛒 购物助手', title: '🛒 购物助手',
template: MarketHelper, template: MarketHelper,
@ -206,74 +201,136 @@ const menuItemList: MenuItem[] = [
template: CompanyWithdraw, template: CompanyWithdraw,
}, },
{ {
title: '🫵 关闭店铺(双击开启)', title: '💰 PI存钱',
handler: () => bazaarControl.method(), template: PropertyVault,
}, },
{ {
title: '📦 物品', title: '📦 物品',
template: InventoryView, template: InventoryView,
}, },
{ {
title: '🚀 更新历史', title: '🎯 BS估算',
template: BSEstView,
},
{
title: '🫵 关闭店铺(双击开启)',
handler: () => bazaarControl.method(),
},
{
title: '🌸 飞花库存',
template: ForeignStock,
},
{
title: '💻 PC',
template: VirusProgramming,
},
{
title: '🩼 配置自动登陆',
template: AutoLoginForm,
},
{
title: '🖥 监控',
template: MonitorMgrView,
},
{
title: '📜️ 传单助手',
handler: () => { handler: () => {
expanded.value = false expanded.value = false
Container.factory(ChangeLogHandler).show() adHelper()
}, },
}, },
// { {
// title: ' ', title: '🛡️ 守望者',
// template: Config, handler: () => {
// }, expanded.value = false
]; safeKeeper()
const drawer = ref(false); },
const isMobilePhone = ref(false); },
const documentHeight = ref(0); {
const expanded = ref(false); title: '🌲 寻找木桩',
const _globVars = ref(globVars); handler: () => window.location.replace('https://www.torn.com/item.php?temp=4#xunzhaomuzhuang'),
logger.info({ _globVars }) },
{
title: '🚀 更新历史',
template: ChangeLogView
},
{
title: '⚙️ 助手设置',
handler: () => {
expanded.value = false
SettingsHandler.clickFunc()
},
},
]
const drawer = ref(false)
const isMobilePhone = ref(false)
const documentHeight = ref(0)
const expanded = ref(false)
const _globVars = ref(globVars)
// tabs // tabs
const editableTabsValue = ref(''); const editableTabsValue = ref('')
const editableTabs = shallowRef([]); const editableTabs = shallowRef([])
// refil
const handleRefil = (method: 'refillEnergy' | 'refillNerve' | 'refillCasinoTokens') => {
fetch(window.addRFC("https://www.torn.com/page.php?sid=pointsBuildingExchange"), {
"headers": {
"accept": "*/*",
"accept-language": "zh-CN,zh;q=0.9",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
"x-requested-with": "XMLHttpRequest"
},
"referrer": "https://www.torn.com/page.php?sid=points",
"referrerPolicy": "strict-origin-when-cross-origin",
"body": (() => {
const data = new FormData();
data.append('key', method);
return data;
})(),
"method": "POST",
"mode": "cors",
"credentials": "include"
})
.then(res => res.json())
.catch(e => ElMessage.error('REFILL异常: ' + e.toString))
.then(res => {
ElMessage({
message: res.success ?
`<p>成功</p><p>剩余次数: ${ res["specialRefills"] } 剩余点数: ${ res['points'] }</p>` :
`REFILL异常: ${ res.error }`,
type: res.success ? 'success' : 'error',
dangerouslyUseHTMLString: true
})
throw new TypeError('REFILL异常: ' + res.error)
})
}
// fast travel // fast travel
const travelData = [ const travelData = [
{ cName: "墨西哥", index: 0 }, { cName: "🇲🇽 墨西哥", index: 0 },
{ cName: "开曼", index: 1 }, { cName: "🇰🇾 开曼", index: 1 },
{ cName: "加拿大", index: 2 }, { cName: "🇨🇦 加拿大", index: 2 },
{ cName: "夏威夷", index: 3 }, { cName: "🌺 夏威夷", index: 3 },
{ cName: "嘤国", index: 4 }, { cName: "🇬🇧 嘤国", index: 4 },
{ cName: "阿根廷", index: 5 }, { cName: "🇦🇷 阿根廷", index: 5 },
{ cName: "瑞士 (解毒)", index: 6 }, { cName: "🇨🇭 瑞士 (解毒)", index: 6 },
{ cName: "立本", index: 7 }, { cName: "🇯🇵 立本", index: 7 },
{ cName: "祖国", index: 8 }, { cName: "🇨🇳 祖国", index: 8 },
{ cName: "阿联酋 (UAE)", index: 9 }, { cName: "🇦🇪 阿联酋 (UAE)", index: 9 },
{ cName: "南非", index: 10 }, { cName: "🇿🇦 南非", index: 10 },
]; ]
const MUZHUANG = () => window.location.replace('https://www.torn.com/item.php?temp=4#xunzhaomuzhuang');
const _adHelper = () => {
expanded.value = false;
adHelper();
}
const _safeKeeper = () => {
expanded.value = false;
safeKeeper();
}
const _setting = () => {
expanded.value = false;
SettingsHandler.clickFunc();
}
const menuClick = (menuItem: MenuItem) => { const menuClick = (menuItem: MenuItem) => {
if (menuItem.handler) { if (menuItem.handler) {
menuItem.handler(); menuItem.handler();
} else if (menuItem.template) { } else if (menuItem.template) {
drawer.value = true; drawer.value = true;
// drawerTitle.value = menuItem.title;
// drawerContent.value = menuItem.template;
addTab(menuItem); addTab(menuItem);
} }
}; }
const showDrawer = () => { const showDrawer = () => {
if (editableTabs.value.length < 1) { if (editableTabs.value.length < 1) {
@ -331,10 +388,6 @@ const removeTab = (targetName: string) => {
} }
}; };
// const tabClick = (pane: TabsPaneContext) => {
// drawerTitle.value = <string>pane.paneName;
// }
const travelConfirm = (destIndex: number, typeIndex: number) => { const travelConfirm = (destIndex: number, typeIndex: number) => {
const destName = travelData[destIndex].cName; const destName = travelData[destIndex].cName;
const typeName = ['普通飞机', 'PI飞机', '股票飞机', '商务飞机(机票或内衣店)'][typeIndex]; const typeName = ['普通飞机', 'PI飞机', '股票飞机', '商务飞机(机票或内衣店)'][typeIndex];
@ -419,6 +472,7 @@ onMounted(() => {
} }
documentHeight.value = document.documentElement.scrollHeight; documentHeight.value = document.documentElement.scrollHeight;
}); });
</script> </script>
<style scoped> <style scoped>
@ -427,4 +481,34 @@ onMounted(() => {
top: 32px; top: 32px;
left: 10px; left: 10px;
} }
.featureCol {
max-width: 33.33%;
}
.featureMenu {
cursor: pointer;
margin: 4px;
border: none;
background-color: transparent;
}
.featureMenu div {
text-align: center;
}
.featureMenu .icon {
font-size: 28px;
}
.featureMenu .title {
font-size: 14px;
height: 32px;
margin-left: -0.5em;
margin-right: -0.5em;
}
.featureMenu:hover {
background-color: #E9EEF6;
}
</style> </style>

View File

@ -14,11 +14,12 @@
import { CommonUtilsKey } from "../ts/class/utils/CommonUtils"; import { CommonUtilsKey } from "../ts/class/utils/CommonUtils";
import { inject, onMounted, ref } from "vue"; import { inject, onMounted, ref } from "vue";
import UserScriptEngine from "../ts/enum/UserScriptEngine.js"; import UserScriptEngine from "../ts/enum/UserScriptEngine.js";
import { TravelItemKey } from "../ts/class/action/TravelItem"; import TravelItem from "../ts/feature/TravelItem";
import { LoggerKey } from "../ts/class/Logger"; import { LoggerKey } from "../ts/class/Logger";
import { Container } from "../ts/container/Container";
const commonUtils = inject(CommonUtilsKey); const commonUtils = inject(CommonUtilsKey);
const travelItem = inject(TravelItemKey); const travelItem = Container.factory(TravelItem)
const logger = inject(LoggerKey); const logger = inject(LoggerKey);
const loading = ref(true); const loading = ref(true);
const staticImageSrc = 'https://jjins.github.io/t2i/stock.png?' + Date.now(); const staticImageSrc = 'https://jjins.github.io/t2i/stock.png?' + Date.now();

View File

@ -23,7 +23,9 @@ type Item = {
damage: number damage: number
accuracy: number accuracy: number
defence: number defence: number
type: number //
type?: number
bonus: string
} }
type ItemDetails = { type ItemDetails = {
@ -39,6 +41,7 @@ type ItemDetails = {
itemInfoContent: string itemInfoContent: string
itemCirculation: string itemCirculation: string
armoryID: boolean armoryID: boolean
// TODO
itemRareIcon: string itemRareIcon: string
} }
@ -141,8 +144,7 @@ const parseListHtml = (html: string) => {
const ret: Item[] = [] const ret: Item[] = []
let tmp = document.createElement('div') let tmp = document.createElement('div')
tmp.innerHTML = html tmp.innerHTML = html
tmp.childNodes.forEach(li => { tmp.querySelectorAll(':scope > li').forEach(li => {
if (li.nodeType === 1) {
const item: Item = { const item: Item = {
type: 0, type: 0,
defence: 0, defence: 0,
@ -150,7 +152,8 @@ const parseListHtml = (html: string) => {
armoryId: 0, armoryId: 0,
details: undefined, details: undefined,
isDetailsLoading: false, isDetailsLoading: false,
amount: -1, isEquipped: false, id: -1, name: "", nameZh: "" amount: -1, isEquipped: false, id: -1, name: "", nameZh: "",
bonus: ""
} }
let elem = li as HTMLElement let elem = li as HTMLElement
// //
@ -169,13 +172,14 @@ const parseListHtml = (html: string) => {
item.damage = Number(elem.querySelector('.bonus-attachment-item-damage-bonus')?.nextElementSibling.innerText?.trim()) item.damage = Number(elem.querySelector('.bonus-attachment-item-damage-bonus')?.nextElementSibling.innerText?.trim())
// //
item.accuracy = Number(elem.querySelector('.bonus-attachment-item-accuracy-bonus')?.nextElementSibling.innerText?.trim()) item.accuracy = Number(elem.querySelector('.bonus-attachment-item-accuracy-bonus')?.nextElementSibling.innerText?.trim())
// //
item.defence = Number(elem.querySelector('.bonus-attachment-item-defence-bonus')?.nextElementSibling.innerText?.trim()) item.defence = Number(elem.querySelector('.bonus-attachment-item-defence-bonus')?.nextElementSibling.innerText?.trim())
// //
item.type = Number(elem.querySelector('ul.actions-wrap li[data-type]')?.getAttribute('data-type').trim()) item.type = Number(elem.querySelector('li[data-type][data-action="equip"][data-item][data-id]')?.getAttribute('data-type').trim())
//
item.bonus = elem.querySelector('li.bonus i[class*="bonus-attachment"][title]')?.getAttribute('title').trim()
ret.push(item) ret.push(item)
}
}) })
return ret return ret
} }
@ -227,6 +231,7 @@ const _equipItem = async (item: Item) => {
onMounted(async () => { onMounted(async () => {
itemList.value = parseListHtml(await fetchList()) itemList.value = parseListHtml(await fetchList())
}) })
</script> </script>
<template> <template>
@ -235,16 +240,18 @@ onMounted(async () => {
{{ item.label }} {{ item.label }}
</el-button> </el-button>
</el-button-group> </el-button-group>
<el-space size="default" wrap> <el-skeleton v-if="itemList.length === 0" :rows="5" animated/>
<el-space size="default" style="margin-top: 0.5em;" wrap>
<div v-for="item in itemList" v-infinite-scroll="loadMore" :infinite-scroll-delay="500" <div v-for="item in itemList" v-infinite-scroll="loadMore" :infinite-scroll-delay="500"
:infinite-scroll-disabled="disabledInfiniteLoading"> :infinite-scroll-disabled="disabledInfiniteLoading">
<el-badge :hidden="item.amount < 2" :value="item.amount"> <el-badge :hidden="item.amount < 2" :max="999" :value="item.amount">
<el-popover :show-after="300" :width="240" trigger="hover"> <el-popover :show-after="300" :width="240" trigger="hover">
<template #reference> <template #reference>
<el-card :body-style="{ padding: '0px' }" :style="item.isEquipped ? { borderColor: '#32CD32' } : null" <el-card :body-style="{ padding: '0px' }" :style="item.isEquipped ? { borderColor: '#32CD32' } : null"
shadow="hover"> shadow="hover">
<el-image :alt="item.id.toString()" :mouseover="itemHover(item)" <el-image :alt="item.id.toString()" :mouseover="itemHover(item)"
:src="`/images/items/${item.id}/medium.png`" style="width: 60px;height: 30px"/> :src="`/images/items/${item.id}/medium.png`" style="width: 60px;height: 30px"/>
<el-tag v-if="item.bonus">🌟</el-tag>
<el-row v-if="item.damage > 0 && item.accuracy > 0"> <el-row v-if="item.damage > 0 && item.accuracy > 0">
<el-col :span="12"> <el-col :span="12">
<el-tag>{{ item.damage }}</el-tag> <el-tag>{{ item.damage }}</el-tag>
@ -275,9 +282,12 @@ onMounted(async () => {
<el-row v-if="item.defence"> <el-row v-if="item.defence">
<el-text>防御: {{ item.defence }}</el-text> <el-text>防御: {{ item.defence }}</el-text>
</el-row> </el-row>
<el-row v-if="item.bonus">
<el-text>特效: <span v-html="item.bonus"></span></el-text>
</el-row>
<el-row :gutter="4"> <el-row :gutter="4">
<el-col :span="8"> <el-col :span="8">
<el-button :icon="Check" @click="item.armoryId ? _equipItem(item) : useItem(item.id.toString())"/> <el-button :icon="Check" @click="item.type ? _equipItem(item) : useItem(item.id.toString())"/>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="8">
<el-button :icon="Delete" disabled/> <el-button :icon="Delete" disabled/>
@ -286,11 +296,19 @@ onMounted(async () => {
<el-button :icon="Promotion" disabled/> <el-button :icon="Promotion" disabled/>
</el-col> </el-col>
</el-row> </el-row>
<div v-if="item.details || item.isDetailsLoading" v-loading="item.isDetailsLoading" <template v-if="item.details">
v-html="item.details?.itemInfoContent || ''"></div> <div v-if="item.isDetailsLoading || item.details?.itemInfoContent"
<el-row v-if="item.details"> v-html="item.details?.itemInfoContent.replace(item.details?.itemInfo, '') || ''"></div>
<el-row v-if="item.details.itemRareIcon">
<el-text>稀有度: {{ item.details.itemRareIcon }}</el-text>
</el-row>
<el-row v-if="item.details.itemCirculation">
<el-text>流通数: {{ item.details.itemCirculation }}</el-text>
</el-row>
<el-row v-if="item.details.itemValue">
<el-text>估值: {{ item.details.itemValue }}</el-text> <el-text>估值: {{ item.details.itemValue }}</el-text>
</el-row> </el-row>
</template>
</el-popover> </el-popover>
</el-badge> </el-badge>
</div> </div>

View File

@ -67,6 +67,7 @@ import { itemNameDict } from "../ts/dictionary/translation";
import toThousands from "../ts/func/utils/toThousands"; import toThousands from "../ts/func/utils/toThousands";
import Sleep from "../ts/func/utils/Sleep"; import Sleep from "../ts/func/utils/Sleep";
import { MathUtilsKey } from "../ts/class/utils/MathUtils"; import { MathUtilsKey } from "../ts/class/utils/MathUtils";
import { timePastFormat } from "../ts/func/utils/timePastFormat";
const logger = inject(LoggerKey); const logger = inject(LoggerKey);
const itemHelper = inject(ItemHelperKey); const itemHelper = inject(ItemHelperKey);
@ -90,25 +91,6 @@ const itemOnList = ref<ItemInfo[]>([]);
const listLoading = ref<boolean>(false); const listLoading = ref<boolean>(false);
const currentTs = ref<number>(Date.now()); const currentTs = ref<number>(Date.now());
const timePastFormat = (ts: number): string => {
//
if (ts < 1000) {
return ts + 'ms';
}
//
else if (ts < 60000) {
return (ts / 1000 | 0) + 's';
}
//
else if (ts < 3600000) {
return (ts / 60000 | 0) + 'm';
}
//
else {
return (ts / 3600000 | 0) + 'h';
}
};
const doGetIMarketList = async (id: number) => { const doGetIMarketList = async (id: number) => {
itemOnList.value = []; itemOnList.value = [];
listLoading.value = true; listLoading.value = true;

View File

@ -0,0 +1,70 @@
<template>
<el-card shadow="never">
<template #header>
<span> CD 空转</span>
</template>
<el-row>
<el-col span="1"></el-col>
<el-col span="23">
<el-form class="form" label-position="left" label-width="30%">
<el-form-item label="开启">
<el-switch v-model="_drugCDMonitor"/>
<span class="desc">开启后每个页面都会存在一个活跃的监视器但是后台页面需要刷新后才会开启</span>
</el-form-item>
<el-form-item label="检查间隔(毫秒)">
<el-input v-model="drugCDMonitorLoop" :disabled="_drugCDMonitor" type="number"/>
<span class="desc">每个监视器在间隔时间后会检查一次</span>
</el-form-item>
</el-form>
</el-col>
</el-row>
</el-card>
</template>
<script lang="ts" setup>
import { onMounted, ref, watch } from "vue";
import drugCDMonitor from "../ts/monitor/DrugCDMonitor";
import IntervalSwitch from "../ts/monitor/IntervalSwitch";
import { Container } from "../ts/container/Container";
import LocalConfigWrapper from "../ts/class/LocalConfigWrapper";
import Logger from "../ts/class/Logger";
const _drugCDMonitor = ref(false)
const drugCDMonitorLoop = ref(999999)
const localConfigWrapper = Container.factory(LocalConfigWrapper)
const logger = Container.factory(Logger)
onMounted(() => {
_drugCDMonitor.value = !!drugCDMonitor.getId() && localConfigWrapper.config.monitorOn.includes('drugCDMonitor')
drugCDMonitorLoop.value = localConfigWrapper.config.drugCDMonitorInterval
watch([_drugCDMonitor, drugCDMonitorLoop], ([_drugCDMonitor, drugCDMonitorLoop]) => {
logger.info('新变动', _drugCDMonitor, drugCDMonitorLoop)
const monitorOn = localConfigWrapper.config.monitorOn
if (_drugCDMonitor) {
drugCDMonitor.setMs(drugCDMonitorLoop)
drugCDMonitor.switcher(IntervalSwitch.ON)
monitorOn.push('drugCDMonitor')
} else {
drugCDMonitor.switcher(IntervalSwitch.OFF)
for (let i = 0; i < monitorOn.length; i++) {
if (monitorOn[i] === 'drugCDMonitor') {
monitorOn.splice(i, 1)
break
}
}
}
localConfigWrapper.config.monitorOn = monitorOn
localConfigWrapper.config.drugCDMonitorInterval = Number(drugCDMonitorLoop)
})
})
</script>
<style scoped>
.form span.desc {
width: 100%;
color: #363636;
}
</style>

189
src/vue/PropertyVault.vue Normal file
View File

@ -0,0 +1,189 @@
<script lang="ts" setup>
import { inject, onMounted, ref } from "vue"
import { LoggerKey } from "../ts/class/Logger"
import { fetchCurrentMoney, fetchCurrentPropVaultAvailableMoney } from "../ts/func/utils/fetchCurrentMoney"
import { ElMessage } from "element-plus"
import toThousands from "../ts/func/utils/toThousands"
const logger = inject(LoggerKey)
const inputMoney = ref('')
const inputWithdrawMoney = ref('')
const formModel = ref({
cash: 0, vault: 0,
})
const propId = ref(-1)
const updateCash = async () => {
formModel.value.cash = await fetchCurrentMoney()
return formModel.value.cash
}
const updateVault = async () => {
formModel.value.vault = await fetchCurrentPropVaultAvailableMoney()
return formModel.value.vault
}
/**
* 存取钱通用
* @param amount
* @param action {'deposit' | 'withdraw'}
*/
const deposit = async (amount: number, action: 'deposit' | 'withdraw' = 'deposit') => {
if (amount < 1) {
ElMessage.error('数额不能小于1')
logger.error('数额不能小于1')
throw new Error('数额不能小于1')
}
if (propId.value === -1) {
ElMessage.error('未获取到房产id')
logger.error('未获取到房产id')
throw new Error('未获取到房产id')
}
let response: string
try {
response = await (await fetch(window.addRFC("https://www.torn.com/properties.php"), {
"headers": {
"accept": "*/*",
"accept-language": "zh-CN,zh;q=0.9",
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
"x-requested-with": "XMLHttpRequest"
},
"referrer": "https://www.torn.com/properties.php",
"referrerPolicy": "strict-origin-when-cross-origin",
"body": "step=vaultProperty&" + action + "=" + amount + "&ID=" + propId.value,
"method": "POST",
"mode": "cors",
"credentials": "include"
})).text()
} catch (e) {
ElMessage.error('请求出错 ' + e.message)
logger.error(e.stack)
throw e
}
let error: string, text: string
try {
let json = JSON.parse(response)
error = json.error
text = json.text
} catch (e) {
}
if (error) {
ElMessage.error('$' + toThousands(amount) + ' 存取请求失败 ' + text)
logger.error('存取请求失败 ' + text)
throw new Error('存取请求失败 ' + text)
} else {
ElMessage.success('$' + toThousands(amount) + ' 存取请求完成')
}
if (action === 'deposit') {
inputWithdrawMoney.value = ''
} else {
inputMoney.value = ''
}
updateVault().then()
updateCash().then()
}
const getPropId = async () => {
let response: string
try {
response = await (await fetch("https://www.torn.com/properties.php", {
"headers": {
"accept": "*/*",
"accept-language": "zh-CN,zh;q=0.9",
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
"x-requested-with": "XMLHttpRequest"
},
"referrer": "https://www.torn.com/properties.php",
"referrerPolicy": "strict-origin-when-cross-origin",
"body": "p=options&tab=vault&step=options",
"method": "POST",
"mode": "cors",
"credentials": "include"
})).text()
} catch (e) {
ElMessage.error('获取房产id失败请重试')
logger.error('获取房产id失败', e)
throw e
}
const reg = /&ID=(\d+)"/
const match = response.match(reg)
if (match) {
propId.value = Number(match[1])
} else {
ElMessage.error('获取房产id失败报文未能成功匹配')
logger.error('获取房产id失败报文未能成功匹配')
throw new Error('获取房产id失败报文未能成功匹配')
}
}
onMounted(async () => {
await getPropId()
await updateCash()
await updateVault()
})
</script>
<template>
<div class="gap4">
<el-card shadow="never">当前默认房产id{{ propId }}</el-card>
<el-card shadow="never">
<template #header>
存钱{{ toThousands(formModel.cash) }}
</template>
<el-row>
<el-col :span="3">
<el-button @click="async ()=>inputMoney=String(await updateCash())">$</el-button>
</el-col>
<el-col :span="18">
<el-input v-model="inputMoney"/>
</el-col>
<el-col :span="3">
<el-button @click="()=>{deposit(Number(inputMoney),'deposit');inputMoney=''}">确定</el-button>
</el-col>
</el-row>
</el-card>
<el-card shadow="never">
<template #header>
取钱{{ toThousands(formModel.vault) }}
</template>
<el-row>
<el-button-group>
<el-button type="primary" @click="()=>inputWithdrawMoney='1000000'">1m</el-button>
<el-button type="primary" @click="()=>inputWithdrawMoney='2000000'">2m</el-button>
<el-button type="primary" @click="()=>inputWithdrawMoney='5000000'">5m</el-button>
<el-button type="primary" @click="()=>inputWithdrawMoney='10000000'">10m</el-button>
<el-button type="primary" @click="()=>inputWithdrawMoney='20000000'">20m</el-button>
</el-button-group>
</el-row>
<el-row>
<el-col :span="3">
<el-button @click="async ()=>inputWithdrawMoney=String(await updateVault())">$</el-button>
</el-col>
<el-col :span="18">
<el-input v-model="inputWithdrawMoney"/>
</el-col>
<el-col :span="3">
<el-button @click="()=>{deposit(Number(inputWithdrawMoney),'withdraw');inputWithdrawMoney=''}">确定
</el-button>
</el-col>
</el-row>
</el-card>
</div>
</template>
<style scoped>
.gap4 .el-card {
margin-bottom: 4px;
}
</style>

View File

@ -1,21 +1,62 @@
<template> <template>
<el-button @click="doCrime(15,'warehouse')">烧仓库</el-button> <!-- <el-row>-->
<el-button @click="doCrime(18,'hackbank')">做18</el-button> <!-- <el-col :span="12">-->
<div class="mt-4">
<el-space>
<el-button-group>
<el-button @click="doCrime(11, 'warehouse')">烧仓库</el-button>
<el-button @click="doCrime(15, 'napcop')">做15-3</el-button>
<el-button @click="doCrime(18, 'hackbank')">做18-1</el-button>
</el-button-group>
<!-- </el-col>-->
<!-- <el-col :span="12">-->
<el-button type="primary" @click="doYouLikeABeer()">🍺 你喜欢啤酒吗</el-button>
</el-space>
</div>
<!-- </el-col>-->
<!-- </el-row>-->
<!-- <el-row>-->
<div class="mt-4">
<el-space>
<el-cascader v-model="crimeSelected" :options="CrimeData" :show-all-levels="false" clearable
placeholder="选择其他"/>
<el-button :disabled="!(crimeSelected && crimeSelected.length)"
:icon="Coffee"
round type="danger" @click="doCrime(crimeSelected[0], crimeSelected[1])">
</el-button>
</el-space>
</div>
<!-- </el-row>-->
<div class="mt-4">
<el-space>
<el-tag type="primary">NERVE {{ nerve }}</el-tag>
<el-tag type="danger">CD {{ boostCoolDown }}</el-tag>
</el-space>
</div>
<div v-if="responseHtmlString" v-html="responseHtmlString"></div>
<div v-if="results" v-html="results"></div> <div v-if="results" v-html="results"></div>
<el-skeleton v-if="loading" :rows="3" animated/> <el-skeleton v-if="loading" :rows="3" animated/>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { inject, ref } from 'vue'; import { Coffee } from "@element-plus/icons-vue"
import { ElMessage } from "element-plus"; import { inject, onBeforeUnmount, onMounted, ref } from 'vue'
import { LoggerKey } from "../ts/class/Logger"; import { LoggerKey } from "../ts/class/Logger"
import { CrimeData } from "./data/CrimeData"
import { useItemSync } from "../ts/func/utils/useItem"
import getSidebarData from "../ts/func/utils/getSidebarData"
const logger = inject(LoggerKey); const logger = inject(LoggerKey)
const results = ref(""); const results = ref("")
const loading = ref(false); const loading = ref(false)
const doCrime = async (nerve, crime: "hackbank" | "warehouse") => { const crimeSelected = ref(null)
loading.value = true; const responseHtmlString = ref('')
results.value = ''; const boostCoolDown = ref(0)
const nerve = ref(0)
const doCrime = async (nerve, crime: "hackbank" | "warehouse" | 'napcop') => {
loading.value = true
results.value = ''
try { try {
results.value = await (await fetch(window.addRFC("https://www.torn.com/crimes.php?step=docrime4&timestamp=" + Date.now()), { results.value = await (await fetch(window.addRFC("https://www.torn.com/crimes.php?step=docrime4&timestamp=" + Date.now()), {
"headers": { "headers": {
@ -33,25 +74,48 @@ const doCrime = async (nerve, crime: "hackbank" | "warehouse") => {
"method": "POST", "method": "POST",
"mode": "cors", "mode": "cors",
"credentials": "include" "credentials": "include"
})).text(); })).text()
} catch (e) { } catch (e) {
logger.error(e.stack); logger.error(e.stack)
results.value = e.message; results.value = e.message
} }
let err; let err: string
try { try {
err = JSON.parse(results.value).error; err = JSON.parse(results.value).error
} catch (e) { } catch (e) {
} }
if (err) { if (err) {
results.value = '出错了'; results.value = '错误: ' + err
ElMessage.error('错误: ' + err); // ElMessage.error(': ' + err)
logger.error(err); logger.error(err)
} }
loading.value = false; loading.value = false
}; updateData()
}
const doYouLikeABeer = async () => {
await useItemSync('180', false, (res) => {
responseHtmlString.value = res.text
updateData()
}
)
}
const updateData = () => {
const sidebarData = getSidebarData()
nerve.value = sidebarData.bars.nerve.amount
boostCoolDown.value = (sidebarData.statusIcons.icons.booster_cooldown?.timerExpiresAt - sidebarData.statusIcons.icons.booster_cooldown?.serverTimestamp) / 3600 | 0
}
let intervalId = 0
onMounted(() => {
updateData()
intervalId = window.setInterval(() => updateData(), 60000)
})
onBeforeUnmount(() => {
if (intervalId) window.clearInterval(intervalId)
})
</script> </script>
<style scoped> <style scoped></style>
</style>

View File

@ -1,49 +0,0 @@
<template>
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
<p>1</p>
</template>
<script>
export default {
name: "Test"
}
</script>
<style scoped>
</style>

View File

@ -1,13 +1,15 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import { inject, ref } from "vue"; import { inject, onMounted, ref } from "vue";
import { LoggerKey } from "../ts/class/Logger"; import { LoggerKey } from "../ts/class/Logger";
import CommonUtils from "../ts/class/utils/CommonUtils"; import CommonUtils from "../ts/class/utils/CommonUtils";
import globVars from "../ts/globVars";
const logger = inject(LoggerKey) const logger = inject(LoggerKey)
const loading = ref(false) const loading = ref(false)
const featureStatus = ref(globVars.featureStatus)
const now = performance.now() const now = performance.now()
const loadAndCopy = async () => { const loadAndCopy = async () => {
@ -31,9 +33,13 @@ const loadAndCopy = async () => {
loading.value = false loading.value = false
} }
onMounted(() => logger.info({ featureStatus }))
</script> </script>
<template> <template>
<el-card class="card" shadow="never">
<template #header>更新</template>
<el-space direction="vertical" style="width: 100%"> <el-space direction="vertical" style="width: 100%">
<el-row> <el-row>
<el-text>最新版本: <el-text>最新版本:
@ -74,10 +80,25 @@ const loadAndCopy = async () => {
<el-text>加载脚本后复制粘贴到用户脚本处</el-text> <el-text>加载脚本后复制粘贴到用户脚本处</el-text>
<el-button :loading="loading" @click="loadAndCopy">加载</el-button> <el-button :loading="loading" @click="loadAndCopy">加载</el-button>
</el-space> </el-space>
</el-card>
<el-card class="card" shadow="never">
<template #header>功能状况</template>
<el-table :data="featureStatus" border style="width: 100%">
<el-table-column label="描述" prop="desc"/>
<el-table-column label="状态" prop="status"/>
<el-table-column label="类名" prop="clazz"/>
<el-table-column label="加载(ms)" prop="load"/>
</el-table>
</el-card>
</template> </template>
<style scoped> <style scoped>
.el-image { .el-image {
width: 20px; width: 20px;
} }
.card {
margin-bottom: 10px;
}
</style> </style>

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