@bettergi/utils 0.0.1 → 0.0.3
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 +68 -8
- package/dist/action.d.ts +2 -2
- package/dist/action.js +4 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/ocr.d.ts +25 -7
- package/dist/ocr.js +47 -19
- package/dist/store.d.ts +6 -0
- package/dist/store.js +20 -0
- package/package.json +1 -1
- package/dist/polyfill.d.ts +0 -1
- package/dist/polyfill.js +0 -10
package/README.md
CHANGED
|
@@ -1,21 +1,81 @@
|
|
|
1
|
-
|
|
1
|
+
本项目是一个为[Better Genshin Impact](https://github.com/babalae/better-genshin-impact) 设计的 JavaScript 开发工具函数,旨在帮助开发者简化代码。
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## 安装
|
|
4
4
|
|
|
5
5
|
```shell
|
|
6
6
|
npm install @bettergi/utils
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## 函数清单
|
|
10
|
+
|
|
11
|
+
### 图文识别
|
|
12
|
+
|
|
13
|
+
> 对 RecognitionObject 代码的封装,对于简单的 OCR 操作,无需写复杂的代码。
|
|
10
14
|
|
|
11
15
|
```ts
|
|
12
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
findImage,
|
|
18
|
+
findImageInDirection,
|
|
19
|
+
findImageWithinBounds,
|
|
20
|
+
findText,
|
|
21
|
+
findTextInDirection,
|
|
22
|
+
findTextWithinBounds
|
|
23
|
+
} from "@bettergi/utils";
|
|
24
|
+
|
|
25
|
+
// 在整个画面内搜索图片,找不到返回 undefined
|
|
26
|
+
const f1 = findImage("assets/关闭.png");
|
|
27
|
+
|
|
28
|
+
// 在指定方向上搜索图片,找不到返回 undefined
|
|
29
|
+
const f2 = findImageInDirection("assets/关闭.png", "north-east");
|
|
30
|
+
|
|
31
|
+
// 在指定区域内搜索图片,找不到返回 undefined
|
|
32
|
+
const f3 = findImageWithinBounds("assets/关闭.png", 960, 0, 960, 1080);
|
|
33
|
+
|
|
34
|
+
// 在整个画面内搜索文本(不包含、忽略大小写),找不到返回 undefined
|
|
35
|
+
const t1 = findText("购买", false, true);
|
|
36
|
+
|
|
37
|
+
// 在指定方向上搜索文本(包含、忽略大小写),找不到返回 undefined
|
|
38
|
+
const t2 = findTextInDirection("师傅", true, true, "east");
|
|
39
|
+
|
|
40
|
+
// 在指定区域内搜索文本(不包含、忽略大小写),找不到返回 undefined
|
|
41
|
+
const t3 = findTextWithinBounds("确认", false, true, 960, 540, 960, 540);
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 行为
|
|
13
45
|
|
|
14
|
-
|
|
46
|
+
> 对脚本工作流中常见行为的抽象,例如:等待 XXX 完成/出现/消失。
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import { findImageInDirection, waitUntil } from "@bettergi/utils";
|
|
15
50
|
|
|
16
|
-
|
|
51
|
+
// 等待直到找不到[关闭按钮] 或 5秒后超时,每隔1秒检查一次,期间按 Esc 键
|
|
52
|
+
const done = await waitUntil(
|
|
53
|
+
() => findImageInDirection("assets/关闭.png", "north-east") !== undefined,
|
|
54
|
+
5000,
|
|
55
|
+
1000,
|
|
56
|
+
() => keyPress("ESCAPE")
|
|
57
|
+
);
|
|
58
|
+
if (!done) throw new Error("关闭页面");
|
|
17
59
|
```
|
|
18
60
|
|
|
19
|
-
|
|
61
|
+
### 存储
|
|
20
62
|
|
|
21
|
-
|
|
63
|
+
> 对象数据持久化,通过代理实现自动存储。可以无感知地读取/更新数据,而无需考虑如何持久化。
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import { useStore } from "@bettergi/utils";
|
|
67
|
+
|
|
68
|
+
// 创建/读取存储对象,保存到存储文件 store/state.json 中
|
|
69
|
+
// 通过Proxy来实现:对存储对象的操作会同步保存到存储文件
|
|
70
|
+
const state = useStore<{ lastUsedTime?: number; count: number }>("state");
|
|
71
|
+
if (state?.lastUsedTime) {
|
|
72
|
+
log.info(`欢迎回来!上次使用时间:${state.lastUsedTime},计数器已累计至:${state.count}`);
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
for (let i = 0; i < Math.floor(Math.random() * 100); i++) {
|
|
76
|
+
state.count = (state.count || 0) + 1; // 同步保存到文件
|
|
77
|
+
}
|
|
78
|
+
} finally {
|
|
79
|
+
state.lastUsedTime = Date.now(); // 同步保存到文件
|
|
80
|
+
}
|
|
81
|
+
```
|
package/dist/action.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @param timeout 超时时间(毫秒),默认 5000 毫秒
|
|
5
5
|
* @param interval 等待间隔(毫秒),默认 200 毫秒
|
|
6
6
|
* @param action 每次等待循环中执行的操作(可选)
|
|
7
|
-
* @returns - true
|
|
7
|
+
* @returns - true 在超时前条件已满足
|
|
8
8
|
* - false 在超时后条件仍未满足
|
|
9
9
|
*/
|
|
10
|
-
export declare const waitUntil: (condition: () => boolean, timeout?: number, interval?: number, action?: () => void) => Promise<boolean>;
|
|
10
|
+
export declare const waitUntil: (condition: (context: Record<string, any>) => boolean, timeout?: number, interval?: number, action?: (context: Record<string, any>) => void) => Promise<boolean>;
|
package/dist/action.js
CHANGED
|
@@ -4,15 +4,16 @@
|
|
|
4
4
|
* @param timeout 超时时间(毫秒),默认 5000 毫秒
|
|
5
5
|
* @param interval 等待间隔(毫秒),默认 200 毫秒
|
|
6
6
|
* @param action 每次等待循环中执行的操作(可选)
|
|
7
|
-
* @returns - true
|
|
7
|
+
* @returns - true 在超时前条件已满足
|
|
8
8
|
* - false 在超时后条件仍未满足
|
|
9
9
|
*/
|
|
10
10
|
export const waitUntil = async (condition, timeout, interval, action) => {
|
|
11
|
+
const context = {};
|
|
11
12
|
const deadline = Date.now() + (timeout ?? 5 * 1000);
|
|
12
13
|
while (Date.now() < deadline) {
|
|
13
|
-
if (condition())
|
|
14
|
+
if (condition(context))
|
|
14
15
|
return true;
|
|
15
|
-
action?.();
|
|
16
|
+
action?.(context);
|
|
16
17
|
await sleep(interval ?? 200);
|
|
17
18
|
}
|
|
18
19
|
return false;
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/ocr.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type Region } from "@bettergi/types/csharp/BetterGenshinImpact/GameTask/Model/Area/Region";
|
|
2
|
+
type Direction = "north" | "north-east" | "east" | "south-east" | "south" | "south-west" | "west" | "north-west";
|
|
2
3
|
/**
|
|
3
|
-
*
|
|
4
|
+
* 在整个画面内搜索图片
|
|
4
5
|
* @param path 图片路径
|
|
5
6
|
* @returns 如果找到匹配的图片区域,则返回该区域,否则返回 undefined
|
|
6
7
|
*/
|
|
@@ -8,15 +9,22 @@ 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
16
|
* @returns 如果找到匹配的图片区域,则返回该区域,否则返回 undefined
|
|
16
17
|
*/
|
|
17
18
|
export declare const findImageWithinBounds: (path: string, x: number, y: number, w: number, h: number) => Region | undefined;
|
|
18
19
|
/**
|
|
19
|
-
*
|
|
20
|
+
* 在指定方向上搜索图片
|
|
21
|
+
* @param path 图片路径
|
|
22
|
+
* @param direction 搜索方向
|
|
23
|
+
* @returns 如果找到匹配的图片区域,则返回该区域,否则返回 undefined
|
|
24
|
+
*/
|
|
25
|
+
export declare const findImageInDirection: (path: string, direction: Direction) => Region | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* 在整个画面内搜索文本
|
|
20
28
|
* @param text 待搜索文本
|
|
21
29
|
* @param contains 是否包含
|
|
22
30
|
* @param ignoreCase 是否忽略大小写
|
|
@@ -28,11 +36,21 @@ export declare const findText: (text: string, contains: boolean, ignoreCase: boo
|
|
|
28
36
|
* @param text 待搜索文本
|
|
29
37
|
* @param contains 是否包含
|
|
30
38
|
* @param ignoreCase 是否忽略大小写
|
|
31
|
-
* @param x
|
|
32
|
-
* @param x -
|
|
33
|
-
* @param y -
|
|
39
|
+
* @param x 水平方向偏移量(像素)
|
|
40
|
+
* @param x - 水平方向偏移量(像素)
|
|
41
|
+
* @param y - 垂直方向偏移量(像素)
|
|
34
42
|
* @param w 宽度
|
|
35
43
|
* @param h 高度
|
|
36
44
|
* @returns 如果找到匹配的文本区域,则返回该区域,否则返回 undefined
|
|
37
45
|
*/
|
|
38
46
|
export declare const findTextWithinBounds: (text: string, contains: boolean, ignoreCase: boolean, x: number, y: number, w: number, h: number) => Region | undefined;
|
|
47
|
+
/**
|
|
48
|
+
* 在指定方向上搜索文本
|
|
49
|
+
* @param text 待搜索文本
|
|
50
|
+
* @param contains 是否包含
|
|
51
|
+
* @param ignoreCase 是否忽略大小写
|
|
52
|
+
* @param direction 搜索方向
|
|
53
|
+
* @returns 如果找到匹配的文本区域,则返回该区域,否则返回 undefined
|
|
54
|
+
*/
|
|
55
|
+
export declare const findTextInDirection: (text: string, contains: boolean, ignoreCase: boolean, direction: Direction) => Region | undefined;
|
|
56
|
+
export {};
|
package/dist/ocr.js
CHANGED
|
@@ -1,22 +1,28 @@
|
|
|
1
|
-
const findFirst = (
|
|
2
|
-
const candidates =
|
|
1
|
+
const findFirst = (ir, ro, predicate) => {
|
|
2
|
+
const candidates = ir.findMulti(ro);
|
|
3
3
|
for (let i = 0; i < candidates.count; i++) {
|
|
4
|
-
if (predicate(candidates[i]))
|
|
4
|
+
if (predicate(candidates[i]))
|
|
5
5
|
return candidates[i];
|
|
6
|
-
}
|
|
7
6
|
}
|
|
8
7
|
return undefined;
|
|
9
8
|
};
|
|
9
|
+
const directionToBounds = (direction) => {
|
|
10
|
+
const x = direction.includes("east") ? genshin.width / 2 : 0;
|
|
11
|
+
const y = direction.includes("south") ? genshin.height / 2 : 0;
|
|
12
|
+
const w = direction === "north" || direction === "south" ? genshin.width : genshin.width / 2;
|
|
13
|
+
const h = direction === "west" || direction === "east" ? genshin.height : genshin.height / 2;
|
|
14
|
+
return { x, y, w, h };
|
|
15
|
+
};
|
|
10
16
|
/**
|
|
11
|
-
*
|
|
17
|
+
* 在整个画面内搜索图片
|
|
12
18
|
* @param path 图片路径
|
|
13
19
|
* @returns 如果找到匹配的图片区域,则返回该区域,否则返回 undefined
|
|
14
20
|
*/
|
|
15
21
|
export const findImage = (path) => {
|
|
16
22
|
try {
|
|
17
|
-
const
|
|
23
|
+
const ir = captureGameRegion();
|
|
18
24
|
const ro = RecognitionObject.templateMatch(file.readImageMatSync(path));
|
|
19
|
-
return findFirst(
|
|
25
|
+
return findFirst(ir, ro, region => region.isExist());
|
|
20
26
|
}
|
|
21
27
|
catch (err) {
|
|
22
28
|
err?.message && log.warn(`${err.message}`);
|
|
@@ -25,24 +31,34 @@ export const findImage = (path) => {
|
|
|
25
31
|
/**
|
|
26
32
|
* 在指定区域内搜索图片
|
|
27
33
|
* @param path 图片路径
|
|
28
|
-
* @param x -
|
|
29
|
-
* @param y -
|
|
34
|
+
* @param x - 水平方向偏移量(像素)
|
|
35
|
+
* @param y - 垂直方向偏移量(像素)
|
|
30
36
|
* @param w 宽度
|
|
31
37
|
* @param h 高度
|
|
32
38
|
* @returns 如果找到匹配的图片区域,则返回该区域,否则返回 undefined
|
|
33
39
|
*/
|
|
34
40
|
export const findImageWithinBounds = (path, x, y, w, h) => {
|
|
35
41
|
try {
|
|
36
|
-
const
|
|
42
|
+
const ir = captureGameRegion();
|
|
37
43
|
const ro = RecognitionObject.templateMatch(file.readImageMatSync(path), x, y, w, h);
|
|
38
|
-
return findFirst(
|
|
44
|
+
return findFirst(ir, ro, region => region.isExist());
|
|
39
45
|
}
|
|
40
46
|
catch (err) {
|
|
41
47
|
err?.message && log.warn(`${err.message}`);
|
|
42
48
|
}
|
|
43
49
|
};
|
|
44
50
|
/**
|
|
45
|
-
*
|
|
51
|
+
* 在指定方向上搜索图片
|
|
52
|
+
* @param path 图片路径
|
|
53
|
+
* @param direction 搜索方向
|
|
54
|
+
* @returns 如果找到匹配的图片区域,则返回该区域,否则返回 undefined
|
|
55
|
+
*/
|
|
56
|
+
export const findImageInDirection = (path, direction) => {
|
|
57
|
+
const { x, y, w, h } = directionToBounds(direction);
|
|
58
|
+
return findImageWithinBounds(path, x, y, w, h);
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* 在整个画面内搜索文本
|
|
46
62
|
* @param text 待搜索文本
|
|
47
63
|
* @param contains 是否包含
|
|
48
64
|
* @param ignoreCase 是否忽略大小写
|
|
@@ -50,9 +66,9 @@ export const findImageWithinBounds = (path, x, y, w, h) => {
|
|
|
50
66
|
*/
|
|
51
67
|
export const findText = (text, contains, ignoreCase) => {
|
|
52
68
|
const searchText = ignoreCase ? text.toLowerCase() : text;
|
|
53
|
-
const
|
|
69
|
+
const ir = captureGameRegion();
|
|
54
70
|
const ro = RecognitionObject.ocrThis;
|
|
55
|
-
return findFirst(
|
|
71
|
+
return findFirst(ir, ro, region => {
|
|
56
72
|
const itemText = ignoreCase ? region.text.toLowerCase() : region.text;
|
|
57
73
|
const isMatch = contains ? itemText.includes(searchText) : itemText === searchText;
|
|
58
74
|
return isMatch && region.isExist();
|
|
@@ -63,20 +79,32 @@ export const findText = (text, contains, ignoreCase) => {
|
|
|
63
79
|
* @param text 待搜索文本
|
|
64
80
|
* @param contains 是否包含
|
|
65
81
|
* @param ignoreCase 是否忽略大小写
|
|
66
|
-
* @param x
|
|
67
|
-
* @param x -
|
|
68
|
-
* @param y -
|
|
82
|
+
* @param x 水平方向偏移量(像素)
|
|
83
|
+
* @param x - 水平方向偏移量(像素)
|
|
84
|
+
* @param y - 垂直方向偏移量(像素)
|
|
69
85
|
* @param w 宽度
|
|
70
86
|
* @param h 高度
|
|
71
87
|
* @returns 如果找到匹配的文本区域,则返回该区域,否则返回 undefined
|
|
72
88
|
*/
|
|
73
89
|
export const findTextWithinBounds = (text, contains, ignoreCase, x, y, w, h) => {
|
|
74
90
|
const searchText = ignoreCase ? text.toLowerCase() : text;
|
|
75
|
-
const
|
|
91
|
+
const ir = captureGameRegion();
|
|
76
92
|
const ro = RecognitionObject.ocr(x, y, w, h);
|
|
77
|
-
return findFirst(
|
|
93
|
+
return findFirst(ir, ro, region => {
|
|
78
94
|
const itemText = ignoreCase ? region.text.toLowerCase() : region.text;
|
|
79
95
|
const isMatch = contains ? itemText.includes(searchText) : itemText === searchText;
|
|
80
96
|
return isMatch && region.isExist();
|
|
81
97
|
});
|
|
82
98
|
};
|
|
99
|
+
/**
|
|
100
|
+
* 在指定方向上搜索文本
|
|
101
|
+
* @param text 待搜索文本
|
|
102
|
+
* @param contains 是否包含
|
|
103
|
+
* @param ignoreCase 是否忽略大小写
|
|
104
|
+
* @param direction 搜索方向
|
|
105
|
+
* @returns 如果找到匹配的文本区域,则返回该区域,否则返回 undefined
|
|
106
|
+
*/
|
|
107
|
+
export const findTextInDirection = (text, contains, ignoreCase, direction) => {
|
|
108
|
+
const { x, y, w, h } = directionToBounds(direction);
|
|
109
|
+
return findTextWithinBounds(text, contains, ignoreCase, x, y, w, h);
|
|
110
|
+
};
|
package/dist/store.d.ts
ADDED
package/dist/store.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 创建一个持久化存储对象,用于管理应用状态数据
|
|
3
|
+
* 该函数会创建一个代理对象,对该对象的所有属性的修改都会自动同步到相应的JSON文件(脚本的 `store` 目录下)中。
|
|
4
|
+
* @param name 存储对象的名称,将作为文件名(不包扩展名)
|
|
5
|
+
*/
|
|
6
|
+
export const useStore = (name) => {
|
|
7
|
+
const path = `store/${name}.json`;
|
|
8
|
+
const obj = (() => {
|
|
9
|
+
try {
|
|
10
|
+
const text = file.readTextSync(path);
|
|
11
|
+
return JSON.parse(text);
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return {};
|
|
15
|
+
}
|
|
16
|
+
})();
|
|
17
|
+
return new Proxy(obj, {
|
|
18
|
+
set: (target, key, value) => Reflect.set(target, key, value) && file.writeTextSync(path, JSON.stringify(target, null, 2))
|
|
19
|
+
});
|
|
20
|
+
};
|
package/package.json
CHANGED
package/dist/polyfill.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const verticalScroll: (scrollAmountInClicks: number) => void;
|
package/dist/polyfill.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { _simulateScroll } from "./mouse";
|
|
2
|
-
export const verticalScroll = (scrollAmountInClicks) => {
|
|
3
|
-
// @ts-ignore
|
|
4
|
-
if (globalThis.verticalScroll) {
|
|
5
|
-
globalThis.verticalScroll(scrollAmountInClicks);
|
|
6
|
-
}
|
|
7
|
-
else {
|
|
8
|
-
_simulateScroll(scrollAmountInClicks, 1);
|
|
9
|
-
}
|
|
10
|
-
};
|