@bettergi/utils 0.0.11 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -57
- package/dist/asserts.d.ts +57 -0
- package/dist/asserts.js +91 -0
- package/dist/game.d.ts +35 -22
- package/dist/game.js +92 -55
- package/dist/http.js +2 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/mouse.d.ts +48 -22
- package/dist/mouse.js +78 -38
- package/dist/ocr.d.ts +44 -34
- package/dist/ocr.js +40 -43
- package/dist/store.d.ts +1 -0
- package/dist/store.js +38 -6
- package/dist/workflow.d.ts +52 -0
- package/dist/workflow.js +72 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -21,8 +21,6 @@ pnpm install @bettergi/utils
|
|
|
21
21
|
> 常见游戏内操作封装,省去手动实现的繁琐。
|
|
22
22
|
|
|
23
23
|
```ts
|
|
24
|
-
import { openMenu, openMenuPage, openPaimonMenu, setTime } from "@bettergi/utils";
|
|
25
|
-
|
|
26
24
|
// 打开派蒙菜单
|
|
27
25
|
await openPaimonMenu();
|
|
28
26
|
|
|
@@ -32,7 +30,10 @@ await openMenu("邮件");
|
|
|
32
30
|
// 打开菜单页面
|
|
33
31
|
await openMenuPage("问卷");
|
|
34
32
|
|
|
35
|
-
//
|
|
33
|
+
// 调整游戏到指定时间
|
|
34
|
+
await setTimeTo(12, 35);
|
|
35
|
+
|
|
36
|
+
// 调整游戏时间段
|
|
36
37
|
await setTime("evening");
|
|
37
38
|
```
|
|
38
39
|
|
|
@@ -41,16 +42,6 @@ await setTime("evening");
|
|
|
41
42
|
> 对 RecognitionObject 代码的封装,对于简单的找图、找字操作,不再需要编写复杂的代码。
|
|
42
43
|
|
|
43
44
|
```ts
|
|
44
|
-
import {
|
|
45
|
-
findImage,
|
|
46
|
-
findImageInDirection,
|
|
47
|
-
findImageWithinBounds,
|
|
48
|
-
findText,
|
|
49
|
-
findTextInDirection,
|
|
50
|
-
findTextWithinBounds
|
|
51
|
-
} from "@bettergi/utils";
|
|
52
|
-
|
|
53
|
-
// 在整个画面内搜索图片,找不到返回 undefined
|
|
54
45
|
const i1 = findImage("assets/关闭.png");
|
|
55
46
|
|
|
56
47
|
// 在指定方向上搜索图片,找不到返回 undefined
|
|
@@ -60,13 +51,13 @@ const i2 = findImageInDirection("assets/关闭.png", "north-east");
|
|
|
60
51
|
const i3 = findImageWithinBounds("assets/关闭.png", 960, 0, 960, 1080);
|
|
61
52
|
|
|
62
53
|
// 在整个画面内搜索文本(不包含、忽略大小写),找不到返回 undefined
|
|
63
|
-
const t1 = findText("购买"
|
|
54
|
+
const t1 = findText("购买");
|
|
64
55
|
|
|
65
56
|
// 在指定方向上搜索文本(包含、忽略大小写),找不到返回 undefined
|
|
66
|
-
const t2 = findTextInDirection("师傅",
|
|
57
|
+
const t2 = findTextInDirection("师傅", "east", { contains: true, ignoreCase: true });
|
|
67
58
|
|
|
68
59
|
// 在指定区域内搜索文本(不包含、忽略大小写),找不到返回 undefined
|
|
69
|
-
const t3 = findTextWithinBounds("确认",
|
|
60
|
+
const t3 = findTextWithinBounds("确认", 960, 540, 960, 540);
|
|
70
61
|
```
|
|
71
62
|
|
|
72
63
|
### 行为流程
|
|
@@ -74,36 +65,28 @@ const t3 = findTextWithinBounds("确认", false, true, 960, 540, 960, 540);
|
|
|
74
65
|
> 对脚本开发过程中常见工作流的抽象,例如:等待 XXX 完成/出现/消失。
|
|
75
66
|
|
|
76
67
|
```ts
|
|
77
|
-
import {
|
|
78
|
-
assertExists,
|
|
79
|
-
assertNotExists,
|
|
80
|
-
findImageInDirection,
|
|
81
|
-
findTextInDirection,
|
|
82
|
-
findTextWithinBounds,
|
|
83
|
-
waitUntil
|
|
84
|
-
} from "@bettergi/utils";
|
|
85
|
-
|
|
86
68
|
// 等待直到找不到[关闭按钮] 或 5秒后超时,每隔1秒检查一次,期间按 Esc 键
|
|
87
|
-
const done = await
|
|
69
|
+
const done = await waitForAction(
|
|
88
70
|
() => findImageInDirection("assets/关闭.png", "north-east") === undefined,
|
|
89
|
-
|
|
90
|
-
1000
|
|
91
|
-
() => keyPress("ESCAPE")
|
|
71
|
+
() => keyPress("ESCAPE"),
|
|
72
|
+
{ maxAttempts: 5, retryInterval: 1000 }
|
|
92
73
|
);
|
|
93
74
|
if (!done) throw new Error("关闭页面超时");
|
|
94
75
|
|
|
95
|
-
// 断言 "世界等级"
|
|
96
|
-
await
|
|
97
|
-
() => findTextInDirection("世界等级",
|
|
76
|
+
// 断言 "世界等级" 区域即将出现 或 5秒后超时抛出异常,每隔1秒检查一次,期间按 Esc 键
|
|
77
|
+
await assertRegionAppearing(
|
|
78
|
+
() => findTextInDirection("世界等级", "north-west"),
|
|
98
79
|
"打开派蒙菜单超时",
|
|
99
|
-
|
|
100
|
-
1000
|
|
101
|
-
() => keyPress("ESCAPE")
|
|
80
|
+
() => keyPress("ESCAPE"),
|
|
81
|
+
{ maxAttempts: 5, retryInterval: 1000 }
|
|
102
82
|
);
|
|
103
83
|
|
|
104
84
|
// 断言 "购买" 区域不存在 或 5秒后超时抛出异常,每隔1秒检查一次,期间如果存在 "购买" 按钮则点击
|
|
105
|
-
const findButton = () => findTextWithinBounds("购买",
|
|
106
|
-
await
|
|
85
|
+
const findButton = () => findTextWithinBounds("购买", 500, 740, 900, 110);
|
|
86
|
+
await assertRegionDisappearing(findButton, "点击购买按钮超时", () => findButton()?.click(), {
|
|
87
|
+
maxAttempts: 5,
|
|
88
|
+
retryInterval: 1000
|
|
89
|
+
});
|
|
107
90
|
```
|
|
108
91
|
|
|
109
92
|
### 鼠标操作
|
|
@@ -111,15 +94,11 @@ await assertNotExists(findButton, "点击购买按钮超时", 5000, 1000, () =>
|
|
|
111
94
|
> 对常见鼠标操作的封装,如鼠标滚动、拖拽等。
|
|
112
95
|
|
|
113
96
|
```ts
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
mouseSlide,
|
|
120
|
-
mouseSlideX,
|
|
121
|
-
mouseSlideY
|
|
122
|
-
} from "@bettergi/utils";
|
|
97
|
+
// 鼠标从 (745, 610) 平滑自然地移动 (1920, 1080)
|
|
98
|
+
await naturalMouseMove(0, 0, 1920, 1080);
|
|
99
|
+
|
|
100
|
+
// 鼠标从 (745, 610) 拖拽到 (1280, 610)
|
|
101
|
+
await mouseDrag(745, 610, 1280, 610);
|
|
123
102
|
|
|
124
103
|
// 鼠标滚轮向上滚动 175 像素
|
|
125
104
|
await mouseScrollUp(175);
|
|
@@ -132,15 +111,6 @@ await mouseScrollUpLines(99);
|
|
|
132
111
|
|
|
133
112
|
// 鼠标滚轮向下滚动 1 行,行高 115(自定义:商店物品行高)
|
|
134
113
|
await mouseScrollDownLines(1, 115);
|
|
135
|
-
|
|
136
|
-
// 鼠标从 (745, 610) 拖拽到 (1280, 610)
|
|
137
|
-
await mouseSlide(745, 610, 1280, 610);
|
|
138
|
-
|
|
139
|
-
// 鼠标从 (745, 610) 向右拖拽 435 像素
|
|
140
|
-
await mouseSlideX(745, 610, 435);
|
|
141
|
-
|
|
142
|
-
// 鼠标从 (1290, 140) 向下拖拽 175 像素
|
|
143
|
-
await mouseSlideY(1290, 140, 175);
|
|
144
114
|
```
|
|
145
115
|
|
|
146
116
|
### 数据存储
|
|
@@ -174,8 +144,8 @@ import { getForBody, postForBody } from "@bettergi/utils";
|
|
|
174
144
|
|
|
175
145
|
// 发送 GET 请求获取响应体内容
|
|
176
146
|
// 提示:需要在 `manifest.json` 文件中配置 `http_allowed_urls`,并在 调度器 -> 修改通用配置 中启用
|
|
177
|
-
const body1 = await getForBody("https://example.com/",
|
|
178
|
-
const body2 = await postForBody("https://example.com/",
|
|
147
|
+
const body1 = await getForBody("https://example.com/", undefined, { "User-Agent": "BetterGI" });
|
|
148
|
+
const body2 = await postForBody("https://example.com/", undefined, { "User-Agent": "BetterGI" });
|
|
179
149
|
log.info(`GET 请求响应体内容${body1}`);
|
|
180
150
|
log.info(`POST 请求响应体内容${body2}`);
|
|
181
151
|
```
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type Action, type RetryOptions } from "./workflow";
|
|
2
|
+
/**
|
|
3
|
+
* 断言某个区域当前存在,否则抛出异常
|
|
4
|
+
* @param regionProvider 返回区域的函数
|
|
5
|
+
* @param message 错误信息
|
|
6
|
+
*/
|
|
7
|
+
export declare const assertRegionPresent: (regionProvider: () => Region | null | undefined, message: string) => Promise<void>;
|
|
8
|
+
/**
|
|
9
|
+
* 断言某个区域当前不存在,否则抛出异常
|
|
10
|
+
* @param regionProvider 返回区域的函数
|
|
11
|
+
* @param message 错误信息
|
|
12
|
+
*/
|
|
13
|
+
export declare const assertRegionAbsent: (regionProvider: () => Region | null | undefined, message: string) => Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* 断言整个画面上某个元素当前存在,否则抛出异常
|
|
16
|
+
* @param recognitionObject 识别对象
|
|
17
|
+
* @param message 错误信息
|
|
18
|
+
*/
|
|
19
|
+
export declare const assertElementPresent: (recognitionObject: RecognitionObject, message: string) => Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* 断言整个画面上某个元素当前不存在,否则抛出异常
|
|
22
|
+
* @param recognitionObject 识别对象
|
|
23
|
+
* @param message 错误信息
|
|
24
|
+
*/
|
|
25
|
+
export declare const assertElementAbsent: (recognitionObject: RecognitionObject, message: string) => Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* 断言某个区域即将出现,否则抛出异常
|
|
28
|
+
* @param regionProvider 返回区域的函数
|
|
29
|
+
* @param message 错误信息
|
|
30
|
+
* @param retryAction 每次重试时执行的操作(可选)
|
|
31
|
+
* @param options 配置选项
|
|
32
|
+
*/
|
|
33
|
+
export declare const assertRegionAppearing: (regionProvider: () => Region | null | undefined, message: string, retryAction?: Action, options?: RetryOptions) => Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* 断言某个区域即将消失,否则抛出异常
|
|
36
|
+
* @param regionProvider 返回区域的函数
|
|
37
|
+
* @param message 错误信息
|
|
38
|
+
* @param retryAction 每次重试时执行的操作(可选)
|
|
39
|
+
* @param options 配置选项
|
|
40
|
+
*/
|
|
41
|
+
export declare const assertRegionDisappearing: (regionProvider: () => Region | null | undefined, message: string, retryAction?: Action, options?: RetryOptions) => Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* 断言整个画面上某个元素即将出现,否则抛出异常
|
|
44
|
+
* @param recognitionObject 识别对象
|
|
45
|
+
* @param message 错误信息
|
|
46
|
+
* @param retryAction 每次重试时执行的操作(可选)
|
|
47
|
+
* @param options 配置选项
|
|
48
|
+
*/
|
|
49
|
+
export declare const assertElementAppearing: (recognitionObject: RecognitionObject, message: string, retryAction?: Action, options?: RetryOptions) => Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* 断言整个画面上某个元素即将消失,否则抛出异常
|
|
52
|
+
* @param recognitionObject 识别对象
|
|
53
|
+
* @param message 错误信息
|
|
54
|
+
* @param retryAction 每次重试时执行的操作(可选)
|
|
55
|
+
* @param options 配置选项
|
|
56
|
+
*/
|
|
57
|
+
export declare const assertElementDisappearing: (recognitionObject: RecognitionObject, message: string, retryAction?: Action, options?: RetryOptions) => Promise<void>;
|
package/dist/asserts.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { waitForElementAppear, waitForElementDisappear, waitForRegionAppear, waitForRegionDisappear } from "./workflow";
|
|
2
|
+
/**
|
|
3
|
+
* 断言某个区域当前存在,否则抛出异常
|
|
4
|
+
* @param regionProvider 返回区域的函数
|
|
5
|
+
* @param message 错误信息
|
|
6
|
+
*/
|
|
7
|
+
export const assertRegionPresent = async (regionProvider, message) => {
|
|
8
|
+
const region = regionProvider();
|
|
9
|
+
if (region == null || !region.isExist()) {
|
|
10
|
+
throw new Error(message);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* 断言某个区域当前不存在,否则抛出异常
|
|
15
|
+
* @param regionProvider 返回区域的函数
|
|
16
|
+
* @param message 错误信息
|
|
17
|
+
*/
|
|
18
|
+
export const assertRegionAbsent = async (regionProvider, message) => {
|
|
19
|
+
const region = regionProvider();
|
|
20
|
+
if (region != null && region.isExist()) {
|
|
21
|
+
throw new Error(message);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* 断言整个画面上某个元素当前存在,否则抛出异常
|
|
26
|
+
* @param recognitionObject 识别对象
|
|
27
|
+
* @param message 错误信息
|
|
28
|
+
*/
|
|
29
|
+
export const assertElementPresent = async (recognitionObject, message) => {
|
|
30
|
+
return assertRegionPresent(() => captureGameRegion().find(recognitionObject), message);
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* 断言整个画面上某个元素当前不存在,否则抛出异常
|
|
34
|
+
* @param recognitionObject 识别对象
|
|
35
|
+
* @param message 错误信息
|
|
36
|
+
*/
|
|
37
|
+
export const assertElementAbsent = async (recognitionObject, message) => {
|
|
38
|
+
return assertRegionAbsent(() => captureGameRegion().find(recognitionObject), message);
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* 断言某个区域即将出现,否则抛出异常
|
|
42
|
+
* @param regionProvider 返回区域的函数
|
|
43
|
+
* @param message 错误信息
|
|
44
|
+
* @param retryAction 每次重试时执行的操作(可选)
|
|
45
|
+
* @param options 配置选项
|
|
46
|
+
*/
|
|
47
|
+
export const assertRegionAppearing = async (regionProvider, message, retryAction, options) => {
|
|
48
|
+
const isAppeared = await waitForRegionAppear(regionProvider, retryAction, options);
|
|
49
|
+
if (!isAppeared) {
|
|
50
|
+
throw new Error(message);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* 断言某个区域即将消失,否则抛出异常
|
|
55
|
+
* @param regionProvider 返回区域的函数
|
|
56
|
+
* @param message 错误信息
|
|
57
|
+
* @param retryAction 每次重试时执行的操作(可选)
|
|
58
|
+
* @param options 配置选项
|
|
59
|
+
*/
|
|
60
|
+
export const assertRegionDisappearing = async (regionProvider, message, retryAction, options) => {
|
|
61
|
+
const isDisappeared = await waitForRegionDisappear(regionProvider, retryAction, options);
|
|
62
|
+
if (!isDisappeared) {
|
|
63
|
+
throw new Error(message);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* 断言整个画面上某个元素即将出现,否则抛出异常
|
|
68
|
+
* @param recognitionObject 识别对象
|
|
69
|
+
* @param message 错误信息
|
|
70
|
+
* @param retryAction 每次重试时执行的操作(可选)
|
|
71
|
+
* @param options 配置选项
|
|
72
|
+
*/
|
|
73
|
+
export const assertElementAppearing = async (recognitionObject, message, retryAction, options) => {
|
|
74
|
+
const isAppeared = await waitForElementAppear(recognitionObject, retryAction, options);
|
|
75
|
+
if (!isAppeared) {
|
|
76
|
+
throw new Error(message);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* 断言整个画面上某个元素即将消失,否则抛出异常
|
|
81
|
+
* @param recognitionObject 识别对象
|
|
82
|
+
* @param message 错误信息
|
|
83
|
+
* @param retryAction 每次重试时执行的操作(可选)
|
|
84
|
+
* @param options 配置选项
|
|
85
|
+
*/
|
|
86
|
+
export const assertElementDisappearing = async (recognitionObject, message, retryAction, options) => {
|
|
87
|
+
const isDisappeared = await waitForElementDisappear(recognitionObject, retryAction, options);
|
|
88
|
+
if (!isDisappeared) {
|
|
89
|
+
throw new Error(message);
|
|
90
|
+
}
|
|
91
|
+
};
|
package/dist/game.d.ts
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
import { ListView } from "./ocr";
|
|
2
|
+
/**
|
|
3
|
+
* 临时设置游戏分辨率和DPI缩放比例,执行指定动作后恢复
|
|
4
|
+
* 适用于使用了鼠标移动的操作,保证在不同的分辨率和DPI下都能正确地复现鼠标操作
|
|
5
|
+
* @param w 游戏宽度
|
|
6
|
+
* @param y 游戏高度
|
|
7
|
+
* @param dpi 系统屏幕的DPI缩放比例
|
|
8
|
+
* @param action 执行动作
|
|
9
|
+
*/
|
|
10
|
+
export declare const withGameMetrics: <T>(w: number, y: number, dpi: number, action: () => Promise<T> | T) => Promise<T>;
|
|
1
11
|
/**
|
|
2
12
|
* 打开派蒙菜单
|
|
3
13
|
*/
|
|
@@ -5,34 +15,37 @@ export declare const openPaimonMenu: () => Promise<void>;
|
|
|
5
15
|
/**
|
|
6
16
|
* 打开游戏菜单(左侧按钮)
|
|
7
17
|
* @param name 菜单名称
|
|
8
|
-
* @param
|
|
9
|
-
* @param
|
|
18
|
+
* @param stepBackwards 是否反向步进(可选)
|
|
19
|
+
* @param options 搜索参数(可选)
|
|
10
20
|
*/
|
|
11
|
-
export declare const openMenu: (name: "\u62CD\u7167" | "\u516C\u544A" | "\u90AE\u4EF6" | "\u65F6\u95F4" | "\u8BBE\u7F6E" | (string & {}),
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
timeout?: number;
|
|
21
|
+
export declare const openMenu: (name: "\u62CD\u7167" | "\u516C\u544A" | "\u90AE\u4EF6" | "\u65F6\u95F4" | "\u8BBE\u7F6E" | (string & {}), stepBackwards?: boolean, options?: {
|
|
22
|
+
navWidth?: number;
|
|
23
|
+
steps?: number;
|
|
15
24
|
}) => Promise<void>;
|
|
16
25
|
/**
|
|
17
26
|
* 打开游戏菜单页面(菜单按钮列表)
|
|
18
27
|
* @param name 菜单页面名称
|
|
19
|
-
* @param
|
|
20
|
-
*/
|
|
21
|
-
export declare const openMenuPage: (name: string, config?: {
|
|
22
|
-
x?: number;
|
|
23
|
-
y?: number;
|
|
24
|
-
w?: number;
|
|
25
|
-
h?: number;
|
|
26
|
-
lineHeight?: number;
|
|
27
|
-
}) => Promise<void>;
|
|
28
|
-
/**
|
|
29
|
-
* 调整游戏时间
|
|
30
|
-
* @param period 时间段
|
|
31
|
-
* @param config 时钟参数
|
|
28
|
+
* @param listView 菜单页面视图参数
|
|
32
29
|
*/
|
|
33
|
-
export declare const
|
|
30
|
+
export declare const openMenuPage: (name: string, listView?: ListView) => Promise<void>;
|
|
31
|
+
type ClockOptions = {
|
|
34
32
|
centerX?: number;
|
|
35
33
|
centerY?: number;
|
|
34
|
+
offsetHours?: number;
|
|
36
35
|
radius?: number;
|
|
37
|
-
|
|
38
|
-
}
|
|
36
|
+
smooth?: number;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* 调整游戏时间到指定时分
|
|
40
|
+
* @param hour 小时
|
|
41
|
+
* @param minute 分钟
|
|
42
|
+
* @param options 时钟参数
|
|
43
|
+
*/
|
|
44
|
+
export declare const setTimeTo: (hour: number, minute: number, options?: ClockOptions) => Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* 调整游戏时间到指定时间段
|
|
47
|
+
* @param period 时间段
|
|
48
|
+
* @param options 时钟参数
|
|
49
|
+
*/
|
|
50
|
+
export declare const setTime: (period: "night" | "morning" | "noon" | "evening", options?: ClockOptions) => Promise<void>;
|
|
51
|
+
export {};
|
package/dist/game.js
CHANGED
|
@@ -1,6 +1,24 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { assertRegionAppearing, assertRegionDisappearing } from "./asserts";
|
|
2
|
+
import { mouseMoveAlongWaypoints } from "./mouse";
|
|
3
3
|
import { findTextInDirection, findTextWithinBounds, findTextWithinListView } from "./ocr";
|
|
4
|
+
/**
|
|
5
|
+
* 临时设置游戏分辨率和DPI缩放比例,执行指定动作后恢复
|
|
6
|
+
* 适用于使用了鼠标移动的操作,保证在不同的分辨率和DPI下都能正确地复现鼠标操作
|
|
7
|
+
* @param w 游戏宽度
|
|
8
|
+
* @param y 游戏高度
|
|
9
|
+
* @param dpi 系统屏幕的DPI缩放比例
|
|
10
|
+
* @param action 执行动作
|
|
11
|
+
*/
|
|
12
|
+
export const withGameMetrics = async (w, y, dpi, action) => {
|
|
13
|
+
const { width, height, screenDpiScale } = genshin;
|
|
14
|
+
try {
|
|
15
|
+
setGameMetrics(w, y, dpi);
|
|
16
|
+
return await action();
|
|
17
|
+
}
|
|
18
|
+
finally {
|
|
19
|
+
setGameMetrics(width, height, screenDpiScale);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
4
22
|
/**
|
|
5
23
|
* 打开派蒙菜单
|
|
6
24
|
*/
|
|
@@ -8,34 +26,36 @@ export const openPaimonMenu = async () => {
|
|
|
8
26
|
// 1.返回主界面
|
|
9
27
|
await genshin.returnMainUi();
|
|
10
28
|
// 2.打开派蒙菜单
|
|
11
|
-
await
|
|
29
|
+
await assertRegionAppearing(() => findTextWithinBounds("世界等级", 300, 230, 440, 100), "打开派蒙菜单超时", () => keyPress("ESCAPE"));
|
|
12
30
|
};
|
|
13
31
|
/**
|
|
14
32
|
* 打开游戏菜单(左侧按钮)
|
|
15
33
|
* @param name 菜单名称
|
|
16
|
-
* @param
|
|
17
|
-
* @param
|
|
34
|
+
* @param stepBackwards 是否反向步进(可选)
|
|
35
|
+
* @param options 搜索参数(可选)
|
|
18
36
|
*/
|
|
19
|
-
export const openMenu = async (name,
|
|
37
|
+
export const openMenu = async (name, stepBackwards = false, options = {}) => {
|
|
20
38
|
// 1.打开派蒙菜单
|
|
21
39
|
await openPaimonMenu();
|
|
22
|
-
// 2
|
|
23
|
-
const {
|
|
24
|
-
const findTooltip = () => findTextWithinBounds(name,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
40
|
+
// 2.步进搜索菜单按钮
|
|
41
|
+
const { navWidth = 95, steps: step = 30 } = options;
|
|
42
|
+
const findTooltip = () => findTextWithinBounds(name, navWidth, 0, 20 + name.length * 20, genshin.height);
|
|
43
|
+
const tooltip = await withGameMetrics(1920, 1080, 1.5, async () => {
|
|
44
|
+
let result = undefined;
|
|
45
|
+
const steps = Math.ceil(genshin.height / step);
|
|
46
|
+
for (let i = 0; i < steps; i++) {
|
|
47
|
+
const o = i * step;
|
|
48
|
+
const y = stepBackwards ? genshin.height - o : o;
|
|
49
|
+
moveMouseTo(Math.round(navWidth / 2), y);
|
|
50
|
+
await sleep(30); // 等待提示文字出现
|
|
51
|
+
if ((result = findTooltip()) !== undefined)
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
});
|
|
35
55
|
// 3.点击菜单按钮
|
|
36
|
-
if (
|
|
37
|
-
await
|
|
38
|
-
click(
|
|
56
|
+
if (tooltip != undefined) {
|
|
57
|
+
await assertRegionDisappearing(findTooltip, `打开菜单 ${name} 超时`, () => {
|
|
58
|
+
click(Math.round(navWidth / 2), tooltip.y);
|
|
39
59
|
});
|
|
40
60
|
}
|
|
41
61
|
else {
|
|
@@ -45,50 +65,67 @@ export const openMenu = async (name, reverse = false, config = {}) => {
|
|
|
45
65
|
/**
|
|
46
66
|
* 打开游戏菜单页面(菜单按钮列表)
|
|
47
67
|
* @param name 菜单页面名称
|
|
48
|
-
* @param
|
|
68
|
+
* @param listView 菜单页面视图参数
|
|
49
69
|
*/
|
|
50
|
-
export const openMenuPage = async (name,
|
|
70
|
+
export const openMenuPage = async (name, listView) => {
|
|
51
71
|
// 1.打开派蒙菜单
|
|
52
72
|
await openPaimonMenu();
|
|
53
|
-
// 2
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
x,
|
|
57
|
-
y,
|
|
58
|
-
w,
|
|
59
|
-
h,
|
|
60
|
-
maxListItems: 5,
|
|
61
|
-
lineHeight
|
|
73
|
+
// 2.搜索菜单页面按钮
|
|
74
|
+
const button = await withGameMetrics(1920, 1080, 1.5, async () => {
|
|
75
|
+
const { x = 95, y = 330, w = 670, h = 730, lineHeight = 142 } = listView || {};
|
|
76
|
+
return await findTextWithinListView(name, { x, y, w, h, lineHeight, scrollLines: 2 });
|
|
62
77
|
});
|
|
63
|
-
if (!
|
|
78
|
+
if (!button)
|
|
64
79
|
throw new Error(`搜索菜单页面 ${name} 失败`);
|
|
65
|
-
// 3
|
|
66
|
-
|
|
80
|
+
// 3.点击打开菜单页面
|
|
81
|
+
await assertRegionDisappearing(() => findTextWithinBounds(name, button.x, button.y, button.width, button.height), `打开菜单页面 ${name} 超时`, () => {
|
|
82
|
+
button.click();
|
|
83
|
+
});
|
|
67
84
|
};
|
|
68
85
|
/**
|
|
69
|
-
*
|
|
70
|
-
* @param
|
|
71
|
-
* @param
|
|
86
|
+
* 调整游戏时间到指定时分
|
|
87
|
+
* @param hour 小时
|
|
88
|
+
* @param minute 分钟
|
|
89
|
+
* @param options 时钟参数
|
|
72
90
|
*/
|
|
73
|
-
export const
|
|
91
|
+
export const setTimeTo = async (hour, minute, options) => {
|
|
74
92
|
// 1.打开时间页面
|
|
75
93
|
await openMenu("时间", true);
|
|
76
|
-
// 2
|
|
77
|
-
const { centerX = 1440, centerY = 502, radius =
|
|
78
|
-
const
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
() =>
|
|
82
|
-
|
|
83
|
-
(
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
findTextInDirection("
|
|
94
|
+
// 2.计算调整时钟的路径点
|
|
95
|
+
const { centerX = 1440, centerY = 502, offsetHours = 6, radius = 154, smooth = 3 } = options || {};
|
|
96
|
+
const radian = ((((hour + offsetHours) * 60 + minute) % 1440) / 1440) * Math.PI * 2;
|
|
97
|
+
const waypoints = [{ x: centerX, y: centerY }].concat(Array.from({ length: Math.max(smooth, 3) })
|
|
98
|
+
// 计算弧度
|
|
99
|
+
.map((_, i) => radian + (1 + i / (Math.max(smooth, 3) - 1)) * Math.PI)
|
|
100
|
+
// 计算相对圆点坐标
|
|
101
|
+
.map(rad => ({ x: radius * Math.cos(rad), y: radius * Math.sin(rad) }))
|
|
102
|
+
// 计算绝对坐标
|
|
103
|
+
.map(p => ({ x: Math.round(p.x + centerX), y: Math.round(p.y + centerY) })));
|
|
104
|
+
// 3.拨动指针
|
|
105
|
+
await withGameMetrics(1920, 1080, 1.5, async () => {
|
|
106
|
+
await mouseMoveAlongWaypoints(waypoints, { shouldDrag: true });
|
|
107
|
+
// 3.点击确认按钮,等待调整结束
|
|
108
|
+
await assertRegionAppearing(() => findTextInDirection("时间少于", "south-east", { contains: true }), "调整时间超时", () => {
|
|
109
|
+
findTextInDirection("确认", "south-east")?.click();
|
|
110
|
+
}, { maxAttempts: 20, retryInterval: 1000 });
|
|
91
111
|
});
|
|
92
112
|
// 4.返回主界面
|
|
93
113
|
await genshin.returnMainUi();
|
|
94
114
|
};
|
|
115
|
+
/**
|
|
116
|
+
* 调整游戏时间到指定时间段
|
|
117
|
+
* @param period 时间段
|
|
118
|
+
* @param options 时钟参数
|
|
119
|
+
*/
|
|
120
|
+
export const setTime = async (period, options) => {
|
|
121
|
+
switch (period) {
|
|
122
|
+
case "night":
|
|
123
|
+
return setTimeTo(0, 0, options);
|
|
124
|
+
case "morning":
|
|
125
|
+
return setTimeTo(6, 0, options);
|
|
126
|
+
case "noon":
|
|
127
|
+
return setTimeTo(12, 0, options);
|
|
128
|
+
case "evening":
|
|
129
|
+
return setTimeTo(18, 0, options);
|
|
130
|
+
}
|
|
131
|
+
};
|
package/dist/http.js
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
* @param headers 请求头
|
|
7
7
|
* @returns 响应体内容
|
|
8
8
|
*/
|
|
9
|
-
export const requestForBody = async (method, url, body
|
|
10
|
-
const resp = await http.request(method, url, body, headers ? JSON.stringify(headers) : "null");
|
|
9
|
+
export const requestForBody = async (method, url, body, headers) => {
|
|
10
|
+
const resp = await http.request(method, url, body ?? "null", headers ? JSON.stringify(headers) : "null");
|
|
11
11
|
if (resp.status_code >= 200 && resp.status_code < 400) {
|
|
12
12
|
return resp.body;
|
|
13
13
|
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/mouse.d.ts
CHANGED
|
@@ -1,3 +1,50 @@
|
|
|
1
|
+
type MouseWaypointsOptions = {
|
|
2
|
+
shouldDrag?: boolean;
|
|
3
|
+
timeout?: number;
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* 沿着指定路径点移动鼠标
|
|
7
|
+
* @param waypoints 鼠标路径点
|
|
8
|
+
* @param options 鼠标移动选项
|
|
9
|
+
*/
|
|
10
|
+
export declare const mouseMoveAlongWaypoints: (waypoints: {
|
|
11
|
+
x: number;
|
|
12
|
+
y: number;
|
|
13
|
+
delay?: number;
|
|
14
|
+
}[], options?: MouseWaypointsOptions) => Promise<boolean>;
|
|
15
|
+
/**
|
|
16
|
+
* 鼠标拖拽滑动到指定位置
|
|
17
|
+
* @param x1 起始水平方向偏移量(像素)
|
|
18
|
+
* @param y1 起始垂直方向偏移量(像素)
|
|
19
|
+
* @param x2 终止水平方向偏移量(像素)
|
|
20
|
+
* @param y2 终止垂直方向偏移量(像素)
|
|
21
|
+
*/
|
|
22
|
+
export declare const mouseDrag: (x1: number, y1: number, x2: number, y2: number) => Promise<boolean>;
|
|
23
|
+
type NaturalMouseMoveOptions = {
|
|
24
|
+
/** 移动持续时间(毫秒) */
|
|
25
|
+
duration: number;
|
|
26
|
+
/** 摆动幅度(像素) */
|
|
27
|
+
wiggle?: number;
|
|
28
|
+
/** 随机种子 */
|
|
29
|
+
seed?: number;
|
|
30
|
+
/** 缓动函数 */
|
|
31
|
+
easing?: (progress: number) => number;
|
|
32
|
+
};
|
|
33
|
+
/** 贝塞尔曲线生成鼠标移动路径点 */
|
|
34
|
+
export declare const bezierWaypoints: (x1: number, y1: number, x2: number, y2: number, options?: NaturalMouseMoveOptions) => Promise<{
|
|
35
|
+
x: number;
|
|
36
|
+
y: number;
|
|
37
|
+
delay: number;
|
|
38
|
+
}[]>;
|
|
39
|
+
/**
|
|
40
|
+
* 自然地移动鼠标到指定位置
|
|
41
|
+
* @param x1 起始水平方向偏移量(像素)
|
|
42
|
+
* @param y1 起始垂直方向偏移量(像素)
|
|
43
|
+
* @param x2 终止水平方向偏移量(像素)
|
|
44
|
+
* @param y2 终止垂直方向偏移量(像素)
|
|
45
|
+
* @param options 鼠标移动选项
|
|
46
|
+
*/
|
|
47
|
+
export declare const naturalMouseMove: (x1: number, y1: number, x2: number, y2: number, options?: NaturalMouseMoveOptions & MouseWaypointsOptions) => Promise<boolean>;
|
|
1
48
|
/**
|
|
2
49
|
* 鼠标滚轮向上滚动指定高度
|
|
3
50
|
* @param height 滚动高度
|
|
@@ -22,25 +69,4 @@ export declare const mouseScrollUpLines: (lines: number, lineHeight?: number) =>
|
|
|
22
69
|
* @param lineHeight 行高(默认值为175像素)
|
|
23
70
|
*/
|
|
24
71
|
export declare const mouseScrollDownLines: (lines: number, lineHeight?: number) => Promise<void>;
|
|
25
|
-
|
|
26
|
-
* 鼠标拖拽滑动到指定位置
|
|
27
|
-
* @param x1 起始水平方向偏移量(像素)
|
|
28
|
-
* @param y1 起始垂直方向偏移量(像素)
|
|
29
|
-
* @param x2 终止水平方向偏移量(像素)
|
|
30
|
-
* @param y2 终止垂直方向偏移量(像素)
|
|
31
|
-
*/
|
|
32
|
-
export declare const mouseSlide: (x1: number, y1: number, x2: number, y2: number) => Promise<void>;
|
|
33
|
-
/**
|
|
34
|
-
* 鼠标水平拖拽滑动指定距离
|
|
35
|
-
* @param x 起始水平方向偏移量(像素)
|
|
36
|
-
* @param y 起始垂直方向偏移量(像素)
|
|
37
|
-
* @param distance 水平拖拽滑动距离(像素) 正数向右,负数向左
|
|
38
|
-
*/
|
|
39
|
-
export declare const mouseSlideX: (x: number, y: number, distance: number) => Promise<void>;
|
|
40
|
-
/**
|
|
41
|
-
* 鼠标垂直拖拽滑动指定距离
|
|
42
|
-
* @param x 起始水平方向偏移量(像素)
|
|
43
|
-
* @param y 起始垂直方向偏移量(像素)
|
|
44
|
-
* @param distance 垂直拖拽滑动距离(像素) 正数向下,负数向上
|
|
45
|
-
*/
|
|
46
|
-
export declare const mouseSlideY: (x: number, y: number, distance: number) => Promise<void>;
|
|
72
|
+
export {};
|
package/dist/mouse.js
CHANGED
|
@@ -1,7 +1,81 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* 沿着指定路径点移动鼠标
|
|
3
|
+
* @param waypoints 鼠标路径点
|
|
4
|
+
* @param options 鼠标移动选项
|
|
5
|
+
*/
|
|
6
|
+
export const mouseMoveAlongWaypoints = async (waypoints, options) => {
|
|
7
|
+
const { shouldDrag = false, timeout = 0 } = options || {};
|
|
8
|
+
try {
|
|
9
|
+
const startTime = Date.now();
|
|
10
|
+
for (let i = 0; i < waypoints.length; i++) {
|
|
11
|
+
// 开始拖拽
|
|
12
|
+
if (i === 0 && shouldDrag)
|
|
13
|
+
leftButtonDown();
|
|
14
|
+
moveMouseTo(Math.trunc(waypoints[i].x), Math.trunc(waypoints[i].y));
|
|
15
|
+
// 等待指定延迟
|
|
16
|
+
const delay = Math.trunc(waypoints[i].delay || 50);
|
|
17
|
+
if (delay > 0)
|
|
18
|
+
await sleep(delay);
|
|
19
|
+
// 超时检查
|
|
20
|
+
if (timeout > 0 && Date.now() - startTime > timeout)
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
finally {
|
|
25
|
+
// 结束拖拽
|
|
26
|
+
if (shouldDrag)
|
|
27
|
+
leftButtonUp();
|
|
28
|
+
}
|
|
29
|
+
return true;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* 鼠标拖拽滑动到指定位置
|
|
33
|
+
* @param x1 起始水平方向偏移量(像素)
|
|
34
|
+
* @param y1 起始垂直方向偏移量(像素)
|
|
35
|
+
* @param x2 终止水平方向偏移量(像素)
|
|
36
|
+
* @param y2 终止垂直方向偏移量(像素)
|
|
37
|
+
*/
|
|
38
|
+
export const mouseDrag = async (x1, y1, x2, y2) => {
|
|
39
|
+
return mouseMoveAlongWaypoints([
|
|
40
|
+
{ x: x1, y: y1 },
|
|
41
|
+
{ x: x2, y: y2 }
|
|
42
|
+
], { shouldDrag: true });
|
|
43
|
+
};
|
|
44
|
+
/** 贝塞尔曲线生成鼠标移动路径点 */
|
|
45
|
+
export const bezierWaypoints = async (x1, y1, x2, y2, options) => {
|
|
46
|
+
const { duration = 800, wiggle = 30, seed = Math.random(), easing = (t) => 1 - Math.pow(1 - t, 3) // ease-out-cubic
|
|
47
|
+
} = options || {};
|
|
48
|
+
const random = () => ((seed * 9301 + 49297) % 233280) / 233280;
|
|
49
|
+
const controlX = (x1 + x2) / 2 + random() * wiggle * 2 - wiggle;
|
|
50
|
+
const controlY = (y1 + y2) / 2 + random() * wiggle * 2 - wiggle;
|
|
51
|
+
const steps = Math.max(duration / 16, 10);
|
|
52
|
+
return Array.from({ length: steps + 1 }, (_, i) => {
|
|
53
|
+
const t = easing(i / steps);
|
|
54
|
+
const u = 1 - t;
|
|
55
|
+
return {
|
|
56
|
+
x: u * u * x1 + 2 * u * t * controlX + t * t * x2,
|
|
57
|
+
y: u * u * y1 + 2 * u * t * controlY + t * t * y2,
|
|
58
|
+
delay: Math.round((duration / steps) * (0.8 + Math.random() * 0.4))
|
|
59
|
+
};
|
|
60
|
+
}).concat([{ x: x2, y: y2, delay: 0 }]);
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* 自然地移动鼠标到指定位置
|
|
64
|
+
* @param x1 起始水平方向偏移量(像素)
|
|
65
|
+
* @param y1 起始垂直方向偏移量(像素)
|
|
66
|
+
* @param x2 终止水平方向偏移量(像素)
|
|
67
|
+
* @param y2 终止垂直方向偏移量(像素)
|
|
68
|
+
* @param options 鼠标移动选项
|
|
69
|
+
*/
|
|
70
|
+
export const naturalMouseMove = async (x1, y1, x2, y2, options) => {
|
|
71
|
+
const waypoints = await bezierWaypoints(x1, y1, x2, y2, options);
|
|
72
|
+
return mouseMoveAlongWaypoints(waypoints, options);
|
|
73
|
+
};
|
|
74
|
+
/** 使用回放脚本模拟滚动 */
|
|
75
|
+
const simulateScroll = async (scrollAmountInClicks, times) => {
|
|
2
76
|
const script = {
|
|
3
77
|
macroEvents: Array(times).fill({ type: 6, mouseX: 0, mouseY: scrollAmountInClicks, time: 0 }),
|
|
4
|
-
info: { name: "", description: "", x: 0, y: 0, width: 1920, height: 1080, recordDpi: 1.
|
|
78
|
+
info: { name: "", description: "", x: 0, y: 0, width: 1920, height: 1080, recordDpi: 1.5 }
|
|
5
79
|
};
|
|
6
80
|
await keyMouseScript.run(JSON.stringify(script));
|
|
7
81
|
};
|
|
@@ -11,7 +85,7 @@ const _simulateScroll = async (scrollAmountInClicks, times) => {
|
|
|
11
85
|
* @param algorithm 自定义滚动算法函数,接收高度参数并返回滚动次数(默认算法为每18像素滚动一次)
|
|
12
86
|
*/
|
|
13
87
|
export const mouseScrollUp = (height, algorithm = h => Math.floor(h / 18)) => {
|
|
14
|
-
return
|
|
88
|
+
return simulateScroll(120, algorithm(height));
|
|
15
89
|
};
|
|
16
90
|
/**
|
|
17
91
|
* 鼠标滚轮向下滚动指定高度
|
|
@@ -19,7 +93,7 @@ export const mouseScrollUp = (height, algorithm = h => Math.floor(h / 18)) => {
|
|
|
19
93
|
* @param algorithm 自定义滚动算法函数,接收高度参数并返回滚动次数(默认算法为每18像素滚动一次)
|
|
20
94
|
*/
|
|
21
95
|
export const mouseScrollDown = (height, algorithm = h => Math.floor(h / 18)) => {
|
|
22
|
-
return
|
|
96
|
+
return simulateScroll(-120, algorithm(height));
|
|
23
97
|
};
|
|
24
98
|
/**
|
|
25
99
|
* 鼠标滚轮向上滚动指定行数
|
|
@@ -37,37 +111,3 @@ export const mouseScrollUpLines = (lines, lineHeight = 175) => {
|
|
|
37
111
|
export const mouseScrollDownLines = (lines, lineHeight = 175) => {
|
|
38
112
|
return mouseScrollDown(lines * lineHeight);
|
|
39
113
|
};
|
|
40
|
-
/**
|
|
41
|
-
* 鼠标拖拽滑动到指定位置
|
|
42
|
-
* @param x1 起始水平方向偏移量(像素)
|
|
43
|
-
* @param y1 起始垂直方向偏移量(像素)
|
|
44
|
-
* @param x2 终止水平方向偏移量(像素)
|
|
45
|
-
* @param y2 终止垂直方向偏移量(像素)
|
|
46
|
-
*/
|
|
47
|
-
export const mouseSlide = async (x1, y1, x2, y2) => {
|
|
48
|
-
moveMouseTo(x1, y1);
|
|
49
|
-
await sleep(100);
|
|
50
|
-
leftButtonDown();
|
|
51
|
-
await sleep(100);
|
|
52
|
-
moveMouseTo(x2, y2);
|
|
53
|
-
await sleep(100);
|
|
54
|
-
leftButtonUp();
|
|
55
|
-
};
|
|
56
|
-
/**
|
|
57
|
-
* 鼠标水平拖拽滑动指定距离
|
|
58
|
-
* @param x 起始水平方向偏移量(像素)
|
|
59
|
-
* @param y 起始垂直方向偏移量(像素)
|
|
60
|
-
* @param distance 水平拖拽滑动距离(像素) 正数向右,负数向左
|
|
61
|
-
*/
|
|
62
|
-
export const mouseSlideX = (x, y, distance) => {
|
|
63
|
-
return mouseSlide(x, y, x + distance, y);
|
|
64
|
-
};
|
|
65
|
-
/**
|
|
66
|
-
* 鼠标垂直拖拽滑动指定距离
|
|
67
|
-
* @param x 起始水平方向偏移量(像素)
|
|
68
|
-
* @param y 起始垂直方向偏移量(像素)
|
|
69
|
-
* @param distance 垂直拖拽滑动距离(像素) 正数向下,负数向上
|
|
70
|
-
*/
|
|
71
|
-
export const mouseSlideY = (x, y, distance) => {
|
|
72
|
-
return mouseSlide(x, y, x, y + distance);
|
|
73
|
-
};
|
package/dist/ocr.d.ts
CHANGED
|
@@ -1,73 +1,83 @@
|
|
|
1
|
-
|
|
1
|
+
import { RetryOptions } from "./workflow";
|
|
2
|
+
type MatchDirection = "north" | "north-east" | "east" | "south-east" | "south" | "south-west" | "west" | "north-west";
|
|
2
3
|
/**
|
|
3
4
|
* 在整个画面内搜索图片
|
|
4
5
|
* @param path 图片路径
|
|
5
|
-
* @returns
|
|
6
|
+
* @returns 如果找到匹配的图片区域,则返回该区域
|
|
6
7
|
*/
|
|
7
|
-
export declare const findImage: (path: string) =>
|
|
8
|
+
export declare const findImage: (path: string) => Region | undefined;
|
|
8
9
|
/**
|
|
9
10
|
* 在指定区域内搜索图片
|
|
10
11
|
* @param path 图片路径
|
|
11
|
-
* @param x
|
|
12
|
-
* @param y
|
|
12
|
+
* @param x 水平方向偏移量(像素)
|
|
13
|
+
* @param y 垂直方向偏移量(像素)
|
|
13
14
|
* @param w 宽度
|
|
14
15
|
* @param h 高度
|
|
15
|
-
* @returns
|
|
16
|
+
* @returns 如果找到匹配的图片区域,则返回该区域
|
|
16
17
|
*/
|
|
17
|
-
export declare const findImageWithinBounds: (path: string, x: number, y: number, w: number, h: number) =>
|
|
18
|
+
export declare const findImageWithinBounds: (path: string, x: number, y: number, w: number, h: number) => Region | undefined;
|
|
18
19
|
/**
|
|
19
20
|
* 在指定方向上搜索图片
|
|
20
21
|
* @param path 图片路径
|
|
21
22
|
* @param direction 搜索方向
|
|
22
|
-
* @returns
|
|
23
|
+
* @returns 如果找到匹配的图片区域,则返回该区域
|
|
23
24
|
*/
|
|
24
|
-
export declare const findImageInDirection: (path: string, direction:
|
|
25
|
+
export declare const findImageInDirection: (path: string, direction: MatchDirection) => Region | undefined;
|
|
26
|
+
/** 文本搜索选项 */
|
|
27
|
+
type TextMatchOptions = {
|
|
28
|
+
/** 是否忽略大小写(默认:是) */
|
|
29
|
+
ignoreCase?: boolean;
|
|
30
|
+
/** 是否非完全匹配(默认:否) */
|
|
31
|
+
contains?: boolean;
|
|
32
|
+
};
|
|
25
33
|
/**
|
|
26
34
|
* 在整个画面内搜索文本
|
|
27
35
|
* @param text 待搜索文本
|
|
28
|
-
* @param
|
|
29
|
-
* @
|
|
30
|
-
* @returns 如果找到匹配的文本区域,则返回该区域,否则返回 undefined
|
|
36
|
+
* @param options 搜索选项
|
|
37
|
+
* @returns 如果找到匹配的文本区域,则返回该区域
|
|
31
38
|
*/
|
|
32
|
-
export declare const findText: (text: string,
|
|
39
|
+
export declare const findText: (text: string, options?: TextMatchOptions) => Region | undefined;
|
|
33
40
|
/**
|
|
34
41
|
* 在指定区域内搜索文本
|
|
35
42
|
* @param text 待搜索文本
|
|
36
|
-
* @param contains 是否包含
|
|
37
|
-
* @param ignoreCase 是否忽略大小写
|
|
38
43
|
* @param x 水平方向偏移量(像素)
|
|
39
|
-
* @param
|
|
40
|
-
* @param y - 垂直方向偏移量(像素)
|
|
44
|
+
* @param y 垂直方向偏移量(像素)
|
|
41
45
|
* @param w 宽度
|
|
42
46
|
* @param h 高度
|
|
43
|
-
* @
|
|
47
|
+
* @param options 搜索选项
|
|
48
|
+
* @returns 如果找到匹配的文本区域,则返回该区域
|
|
44
49
|
*/
|
|
45
|
-
export declare const findTextWithinBounds: (text: string,
|
|
50
|
+
export declare const findTextWithinBounds: (text: string, x: number, y: number, w: number, h: number, options?: TextMatchOptions) => Region | undefined;
|
|
46
51
|
/**
|
|
47
52
|
* 在指定方向上搜索文本
|
|
48
53
|
* @param text 待搜索文本
|
|
49
|
-
* @param contains 是否包含
|
|
50
|
-
* @param ignoreCase 是否忽略大小写
|
|
51
54
|
* @param direction 搜索方向
|
|
52
|
-
* @
|
|
55
|
+
* @param options 搜索选项
|
|
56
|
+
* @returns 如果找到匹配的文本区域,则返回该区域
|
|
53
57
|
*/
|
|
54
|
-
export declare const findTextInDirection: (text: string,
|
|
58
|
+
export declare const findTextInDirection: (text: string, direction: MatchDirection, options?: TextMatchOptions) => Region | undefined;
|
|
59
|
+
/** 列表视图参数 */
|
|
60
|
+
export type ListView = {
|
|
61
|
+
x: number;
|
|
62
|
+
y: number;
|
|
63
|
+
w: number;
|
|
64
|
+
h: number;
|
|
65
|
+
/** 列表项高度 */
|
|
66
|
+
lineHeight: number;
|
|
67
|
+
/** 每次滚动的行数 */
|
|
68
|
+
scrollLines?: number;
|
|
69
|
+
/** 横向内边距 */
|
|
70
|
+
paddingX?: number;
|
|
71
|
+
/** 纵向内边距 */
|
|
72
|
+
paddingY?: number;
|
|
73
|
+
};
|
|
55
74
|
/**
|
|
56
75
|
* 在列表视图中滚动搜索文本
|
|
57
76
|
* @param text 待搜索文本
|
|
58
|
-
* @param contains 是否包含
|
|
59
|
-
* @param ignoreCase 是否忽略大小写
|
|
60
77
|
* @param listView 列表视图参数
|
|
78
|
+
* @param matchOptions 搜索选项
|
|
61
79
|
* @param timeout 搜索超时
|
|
62
80
|
* @returns 如果找到匹配的文本区域,则返回该区域,否则返回 undefined
|
|
63
81
|
*/
|
|
64
|
-
export declare const findTextWithinListView: (text: string,
|
|
65
|
-
x: number;
|
|
66
|
-
y: number;
|
|
67
|
-
w: number;
|
|
68
|
-
h: number;
|
|
69
|
-
maxListItems: number;
|
|
70
|
-
lineHeight: number;
|
|
71
|
-
padding?: number;
|
|
72
|
-
}, timeout?: number) => Promise<globalThis.Region | undefined>;
|
|
82
|
+
export declare const findTextWithinListView: (text: string, listView: ListView, matchOptions?: TextMatchOptions, retryOptions?: RetryOptions) => Promise<Region | undefined>;
|
|
73
83
|
export {};
|
package/dist/ocr.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { waitUntil } from "./flow";
|
|
2
1
|
import { mouseScrollDownLines } from "./mouse";
|
|
2
|
+
import { waitForAction } from "./workflow";
|
|
3
3
|
const findFirst = (ir, ro, predicate) => {
|
|
4
4
|
const candidates = ir.findMulti(ro);
|
|
5
5
|
for (let i = 0; i < candidates.count; i++) {
|
|
@@ -18,7 +18,7 @@ const directionToBounds = (direction) => {
|
|
|
18
18
|
/**
|
|
19
19
|
* 在整个画面内搜索图片
|
|
20
20
|
* @param path 图片路径
|
|
21
|
-
* @returns
|
|
21
|
+
* @returns 如果找到匹配的图片区域,则返回该区域
|
|
22
22
|
*/
|
|
23
23
|
export const findImage = (path) => {
|
|
24
24
|
try {
|
|
@@ -33,11 +33,11 @@ export const findImage = (path) => {
|
|
|
33
33
|
/**
|
|
34
34
|
* 在指定区域内搜索图片
|
|
35
35
|
* @param path 图片路径
|
|
36
|
-
* @param x
|
|
37
|
-
* @param y
|
|
36
|
+
* @param x 水平方向偏移量(像素)
|
|
37
|
+
* @param y 垂直方向偏移量(像素)
|
|
38
38
|
* @param w 宽度
|
|
39
39
|
* @param h 高度
|
|
40
|
-
* @returns
|
|
40
|
+
* @returns 如果找到匹配的图片区域,则返回该区域
|
|
41
41
|
*/
|
|
42
42
|
export const findImageWithinBounds = (path, x, y, w, h) => {
|
|
43
43
|
try {
|
|
@@ -53,7 +53,7 @@ export const findImageWithinBounds = (path, x, y, w, h) => {
|
|
|
53
53
|
* 在指定方向上搜索图片
|
|
54
54
|
* @param path 图片路径
|
|
55
55
|
* @param direction 搜索方向
|
|
56
|
-
* @returns
|
|
56
|
+
* @returns 如果找到匹配的图片区域,则返回该区域
|
|
57
57
|
*/
|
|
58
58
|
export const findImageInDirection = (path, direction) => {
|
|
59
59
|
const { x, y, w, h } = directionToBounds(direction);
|
|
@@ -62,11 +62,11 @@ export const findImageInDirection = (path, direction) => {
|
|
|
62
62
|
/**
|
|
63
63
|
* 在整个画面内搜索文本
|
|
64
64
|
* @param text 待搜索文本
|
|
65
|
-
* @param
|
|
66
|
-
* @
|
|
67
|
-
* @returns 如果找到匹配的文本区域,则返回该区域,否则返回 undefined
|
|
65
|
+
* @param options 搜索选项
|
|
66
|
+
* @returns 如果找到匹配的文本区域,则返回该区域
|
|
68
67
|
*/
|
|
69
|
-
export const findText = (text,
|
|
68
|
+
export const findText = (text, options) => {
|
|
69
|
+
const { ignoreCase = true, contains = false } = options || {};
|
|
70
70
|
const searchText = ignoreCase ? text.toLowerCase() : text;
|
|
71
71
|
const ir = captureGameRegion();
|
|
72
72
|
const ro = RecognitionObject.ocrThis;
|
|
@@ -79,16 +79,15 @@ export const findText = (text, contains, ignoreCase) => {
|
|
|
79
79
|
/**
|
|
80
80
|
* 在指定区域内搜索文本
|
|
81
81
|
* @param text 待搜索文本
|
|
82
|
-
* @param contains 是否包含
|
|
83
|
-
* @param ignoreCase 是否忽略大小写
|
|
84
82
|
* @param x 水平方向偏移量(像素)
|
|
85
|
-
* @param
|
|
86
|
-
* @param y - 垂直方向偏移量(像素)
|
|
83
|
+
* @param y 垂直方向偏移量(像素)
|
|
87
84
|
* @param w 宽度
|
|
88
85
|
* @param h 高度
|
|
89
|
-
* @
|
|
86
|
+
* @param options 搜索选项
|
|
87
|
+
* @returns 如果找到匹配的文本区域,则返回该区域
|
|
90
88
|
*/
|
|
91
|
-
export const findTextWithinBounds = (text,
|
|
89
|
+
export const findTextWithinBounds = (text, x, y, w, h, options) => {
|
|
90
|
+
const { ignoreCase = true, contains = false } = options || {};
|
|
92
91
|
const searchText = ignoreCase ? text.toLowerCase() : text;
|
|
93
92
|
const ir = captureGameRegion();
|
|
94
93
|
const ro = RecognitionObject.ocr(x, y, w, h);
|
|
@@ -101,49 +100,47 @@ export const findTextWithinBounds = (text, contains, ignoreCase, x, y, w, h) =>
|
|
|
101
100
|
/**
|
|
102
101
|
* 在指定方向上搜索文本
|
|
103
102
|
* @param text 待搜索文本
|
|
104
|
-
* @param contains 是否包含
|
|
105
|
-
* @param ignoreCase 是否忽略大小写
|
|
106
103
|
* @param direction 搜索方向
|
|
107
|
-
* @
|
|
104
|
+
* @param options 搜索选项
|
|
105
|
+
* @returns 如果找到匹配的文本区域,则返回该区域
|
|
108
106
|
*/
|
|
109
|
-
export const findTextInDirection = (text,
|
|
107
|
+
export const findTextInDirection = (text, direction, options) => {
|
|
110
108
|
const { x, y, w, h } = directionToBounds(direction);
|
|
111
|
-
return findTextWithinBounds(text,
|
|
109
|
+
return findTextWithinBounds(text, x, y, w, h, options);
|
|
112
110
|
};
|
|
113
111
|
/**
|
|
114
112
|
* 在列表视图中滚动搜索文本
|
|
115
113
|
* @param text 待搜索文本
|
|
116
|
-
* @param contains 是否包含
|
|
117
|
-
* @param ignoreCase 是否忽略大小写
|
|
118
114
|
* @param listView 列表视图参数
|
|
115
|
+
* @param matchOptions 搜索选项
|
|
119
116
|
* @param timeout 搜索超时
|
|
120
117
|
* @returns 如果找到匹配的文本区域,则返回该区域,否则返回 undefined
|
|
121
118
|
*/
|
|
122
|
-
export const findTextWithinListView = async (text,
|
|
123
|
-
const { x, y, w, h,
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
if (
|
|
132
|
-
if (
|
|
119
|
+
export const findTextWithinListView = async (text, listView, matchOptions, retryOptions) => {
|
|
120
|
+
const { x, y, w, h, lineHeight, scrollLines = 1, paddingX = 10, paddingY = 10 } = listView;
|
|
121
|
+
const { maxAttempts = 30, retryInterval = 1000 } = retryOptions || {};
|
|
122
|
+
const findTargetText = () => findTextWithinBounds(text, x, y, w, h, matchOptions);
|
|
123
|
+
let lastTextRegion;
|
|
124
|
+
const isReachedBottom = () => {
|
|
125
|
+
const textRegion = findFirst(captureGameRegion(), RecognitionObject.ocr(x, y, w, h), region => {
|
|
126
|
+
return region.isExist() && region.text.trim().length > 0;
|
|
127
|
+
});
|
|
128
|
+
if (textRegion) {
|
|
129
|
+
if (lastTextRegion?.text === textRegion.text &&
|
|
130
|
+
Math.abs(textRegion.y - lastTextRegion.y) < lineHeight) {
|
|
133
131
|
return true;
|
|
134
132
|
}
|
|
135
133
|
else {
|
|
136
|
-
|
|
134
|
+
lastTextRegion = textRegion;
|
|
137
135
|
return false;
|
|
138
136
|
}
|
|
139
137
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
138
|
+
// 异常情况:找不到任何文本
|
|
139
|
+
return true;
|
|
143
140
|
};
|
|
144
|
-
const
|
|
145
|
-
moveMouseTo(x + w -
|
|
146
|
-
await mouseScrollDownLines(
|
|
147
|
-
});
|
|
148
|
-
return
|
|
141
|
+
const isTextFoundOrBottomReached = await waitForAction(() => findTargetText() != undefined || isReachedBottom(), async () => {
|
|
142
|
+
moveMouseTo(x + w - paddingX, y + paddingY); // 移动到滚动条附近
|
|
143
|
+
await mouseScrollDownLines(scrollLines, lineHeight); // 滚动指定行数
|
|
144
|
+
}, { maxAttempts, retryInterval });
|
|
145
|
+
return isTextFoundOrBottomReached ? findTargetText() : undefined;
|
|
149
146
|
};
|
package/dist/store.d.ts
CHANGED
package/dist/store.js
CHANGED
|
@@ -1,20 +1,52 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 创建一个持久化存储对象,用于管理应用状态数据
|
|
3
3
|
* 该函数会创建一个代理对象,对该对象的所有属性的修改都会自动同步到相应的JSON文件(脚本的 `store` 目录下)中。
|
|
4
|
+
* 支持深层嵌套对象的代理。
|
|
4
5
|
* @param name 存储对象的名称,将作为文件名(不包扩展名)
|
|
5
6
|
*/
|
|
6
7
|
export const useStore = (name) => {
|
|
7
|
-
const
|
|
8
|
+
const filePath = `store/${name}.json`;
|
|
9
|
+
// 读取文件数据
|
|
8
10
|
const obj = (() => {
|
|
9
11
|
try {
|
|
10
|
-
const text = file.readTextSync(
|
|
12
|
+
const text = file.readTextSync(filePath);
|
|
11
13
|
return JSON.parse(text);
|
|
12
14
|
}
|
|
13
15
|
catch {
|
|
14
|
-
return {};
|
|
16
|
+
return {}; // 创建空对象
|
|
15
17
|
}
|
|
16
18
|
})();
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
// 创建代理函数
|
|
20
|
+
const createProxy = (target, parentPath = []) => {
|
|
21
|
+
if (typeof target !== "object" || target === null) {
|
|
22
|
+
return target;
|
|
23
|
+
}
|
|
24
|
+
return new Proxy(target, {
|
|
25
|
+
get: (target, key) => {
|
|
26
|
+
const value = Reflect.get(target, key);
|
|
27
|
+
return typeof value === "object" && value !== null
|
|
28
|
+
? createProxy(value, [...parentPath, key]) // 递归创建代理
|
|
29
|
+
: value;
|
|
30
|
+
},
|
|
31
|
+
set: (target, key, value) => {
|
|
32
|
+
const success = Reflect.set(target, key, value);
|
|
33
|
+
if (success) {
|
|
34
|
+
Promise.resolve().then(() => {
|
|
35
|
+
file.writeTextSync(filePath, JSON.stringify(obj, null, 2));
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
return success;
|
|
39
|
+
},
|
|
40
|
+
deleteProperty: (target, key) => {
|
|
41
|
+
const success = Reflect.deleteProperty(target, key);
|
|
42
|
+
if (success) {
|
|
43
|
+
Promise.resolve().then(() => {
|
|
44
|
+
file.writeTextSync(filePath, JSON.stringify(obj, null, 2));
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return success;
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
return createProxy(obj);
|
|
20
52
|
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export type Action = () => Promise<void> | void;
|
|
2
|
+
export type RetryOptions = {
|
|
3
|
+
/** 重试次数 */
|
|
4
|
+
maxAttempts?: number;
|
|
5
|
+
/** 重试间隔(毫秒) */
|
|
6
|
+
retryInterval?: number;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* 等待直到条件满足或超时,期间执行重试操作
|
|
10
|
+
* @param condition 返回条件是否满足的函数
|
|
11
|
+
* @param retryAction 每次重试时执行的操作(可选)
|
|
12
|
+
* @param options 配置选项
|
|
13
|
+
* @returns - true 条件满足
|
|
14
|
+
* - false 达到最大重试次数
|
|
15
|
+
*/
|
|
16
|
+
export declare const waitForAction: (condition: () => boolean, retryAction?: Action, options?: RetryOptions) => Promise<boolean>;
|
|
17
|
+
/**
|
|
18
|
+
* 等待某个区域出现,期间执行重试操作
|
|
19
|
+
* @param regionProvider 返回区域的函数
|
|
20
|
+
* @param retryAction 每次重试时执行的操作(可选)
|
|
21
|
+
* @param options 配置选项
|
|
22
|
+
* @returns - true 区域出现
|
|
23
|
+
* - false 达到最大重试次数
|
|
24
|
+
*/
|
|
25
|
+
export declare const waitForRegionAppear: (regionProvider: () => Region | null | undefined, retryAction?: Action, options?: RetryOptions) => Promise<boolean>;
|
|
26
|
+
/**
|
|
27
|
+
* 等待某个区域消失,期间执行重试操作
|
|
28
|
+
* @param regionProvider 返回区域的函数
|
|
29
|
+
* @param retryAction 每次重试时执行的操作(可选)
|
|
30
|
+
* @param options 配置选项
|
|
31
|
+
* @returns - true 区域消失
|
|
32
|
+
* - false 达到最大重试次数
|
|
33
|
+
*/
|
|
34
|
+
export declare const waitForRegionDisappear: (regionProvider: () => Region | null | undefined, retryAction?: Action, options?: RetryOptions) => Promise<boolean>;
|
|
35
|
+
/**
|
|
36
|
+
* 等待整个画面上某个元素出现,期间执行重试操作
|
|
37
|
+
* @param recognitionObject 识别对象
|
|
38
|
+
* @param retryAction 每次重试时执行的操作(可选)
|
|
39
|
+
* @param options 配置选项
|
|
40
|
+
* @returns - true 整个画面上某个元素出现
|
|
41
|
+
* - false 达到最大重试次数
|
|
42
|
+
*/
|
|
43
|
+
export declare const waitForElementAppear: (recognitionObject: RecognitionObject, retryAction?: Action, options?: RetryOptions) => Promise<boolean>;
|
|
44
|
+
/**
|
|
45
|
+
* 等待整个画面上某个元素消失,期间执行重试操作
|
|
46
|
+
* @param recognitionObject 识别对象
|
|
47
|
+
* @param retryAction 每次重试时执行的操作(可选)
|
|
48
|
+
* @param options 配置选项
|
|
49
|
+
* @returns - true 整个画面上某个元素消失
|
|
50
|
+
* - false 达到最大重试次数
|
|
51
|
+
*/
|
|
52
|
+
export declare const waitForElementDisappear: (recognitionObject: RecognitionObject, retryAction?: Action, options?: RetryOptions) => Promise<boolean>;
|
package/dist/workflow.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/** 默认最大重试次数 */
|
|
2
|
+
const defaultMaxAttempts = 5;
|
|
3
|
+
/** 默认重试间隔(毫秒) */
|
|
4
|
+
const defaultRetryInterval = 1000;
|
|
5
|
+
/**
|
|
6
|
+
* 等待直到条件满足或超时,期间执行重试操作
|
|
7
|
+
* @param condition 返回条件是否满足的函数
|
|
8
|
+
* @param retryAction 每次重试时执行的操作(可选)
|
|
9
|
+
* @param options 配置选项
|
|
10
|
+
* @returns - true 条件满足
|
|
11
|
+
* - false 达到最大重试次数
|
|
12
|
+
*/
|
|
13
|
+
export const waitForAction = async (condition, retryAction, options) => {
|
|
14
|
+
const { maxAttempts = defaultMaxAttempts, retryInterval = defaultRetryInterval } = options || {};
|
|
15
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
16
|
+
if (condition())
|
|
17
|
+
return true;
|
|
18
|
+
await retryAction?.();
|
|
19
|
+
await sleep(retryInterval);
|
|
20
|
+
}
|
|
21
|
+
return false;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* 等待某个区域出现,期间执行重试操作
|
|
25
|
+
* @param regionProvider 返回区域的函数
|
|
26
|
+
* @param retryAction 每次重试时执行的操作(可选)
|
|
27
|
+
* @param options 配置选项
|
|
28
|
+
* @returns - true 区域出现
|
|
29
|
+
* - false 达到最大重试次数
|
|
30
|
+
*/
|
|
31
|
+
export const waitForRegionAppear = async (regionProvider, retryAction, options) => {
|
|
32
|
+
return waitForAction(() => {
|
|
33
|
+
const region = regionProvider();
|
|
34
|
+
return region != null && region.isExist();
|
|
35
|
+
}, retryAction, options);
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* 等待某个区域消失,期间执行重试操作
|
|
39
|
+
* @param regionProvider 返回区域的函数
|
|
40
|
+
* @param retryAction 每次重试时执行的操作(可选)
|
|
41
|
+
* @param options 配置选项
|
|
42
|
+
* @returns - true 区域消失
|
|
43
|
+
* - false 达到最大重试次数
|
|
44
|
+
*/
|
|
45
|
+
export const waitForRegionDisappear = async (regionProvider, retryAction, options) => {
|
|
46
|
+
return waitForAction(() => {
|
|
47
|
+
const region = regionProvider();
|
|
48
|
+
return !region || !region.isExist();
|
|
49
|
+
}, retryAction, options);
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* 等待整个画面上某个元素出现,期间执行重试操作
|
|
53
|
+
* @param recognitionObject 识别对象
|
|
54
|
+
* @param retryAction 每次重试时执行的操作(可选)
|
|
55
|
+
* @param options 配置选项
|
|
56
|
+
* @returns - true 整个画面上某个元素出现
|
|
57
|
+
* - false 达到最大重试次数
|
|
58
|
+
*/
|
|
59
|
+
export const waitForElementAppear = async (recognitionObject, retryAction, options) => {
|
|
60
|
+
return waitForRegionAppear(() => captureGameRegion().find(recognitionObject), retryAction, options);
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* 等待整个画面上某个元素消失,期间执行重试操作
|
|
64
|
+
* @param recognitionObject 识别对象
|
|
65
|
+
* @param retryAction 每次重试时执行的操作(可选)
|
|
66
|
+
* @param options 配置选项
|
|
67
|
+
* @returns - true 整个画面上某个元素消失
|
|
68
|
+
* - false 达到最大重试次数
|
|
69
|
+
*/
|
|
70
|
+
export const waitForElementDisappear = async (recognitionObject, retryAction, options) => {
|
|
71
|
+
return waitForRegionDisappear(() => captureGameRegion().find(recognitionObject), retryAction, options);
|
|
72
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bettergi/utils",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "开发 BetterGI 脚本常用工具集",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "Bread Grocery<https://github.com/breadgrocery>",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"build": "tsc"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@bettergi/types": "^0.
|
|
36
|
+
"@bettergi/types": "^0.1.1",
|
|
37
37
|
"typescript": "^5.9.3"
|
|
38
38
|
}
|
|
39
39
|
}
|