@bettergi/utils 0.1.0 → 0.1.2

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 CHANGED
@@ -62,7 +62,7 @@ const t3 = findTextWithinBounds("确认", 960, 540, 960, 540);
62
62
 
63
63
  ### 行为流程
64
64
 
65
- > 对脚本开发过程中常见工作流的抽象,例如:等待 XXX 完成/出现/消失。
65
+ > 对脚本开发过程中常见工作流的抽象,例如: 等待/断言 操作/元素/区域 完成/出现/消失。
66
66
 
67
67
  ```ts
68
68
  // 等待直到找不到[关闭按钮] 或 5秒后超时,每隔1秒检查一次,期间按 Esc 键
@@ -106,10 +106,10 @@ await mouseScrollUp(175);
106
106
  // 鼠标滚轮向下滚动 175 像素
107
107
  await mouseScrollDown(175);
108
108
 
109
- // 鼠标滚轮向上滚动 99 行,行高 175(默认:背包物品行高)
109
+ // 鼠标滚轮向上滚动 99 行,行高 175(默认: 背包物品行高)
110
110
  await mouseScrollUpLines(99);
111
111
 
112
- // 鼠标滚轮向下滚动 1 行,行高 115(自定义:商店物品行高)
112
+ // 鼠标滚轮向下滚动 1 行,行高 115(自定义: 商店物品行高)
113
113
  await mouseScrollDownLines(1, 115);
114
114
  ```
115
115
 
@@ -123,7 +123,7 @@ import { useStore } from "@bettergi/utils";
123
123
  // 创建/读取存储对象,保存到存储文件 store/state.json 中
124
124
  const state = useStore<{ lastUsedTime?: number; count: number }>("state");
125
125
  if (state?.lastUsedTime) {
126
- log.info(`欢迎回来!上次使用时间:${state.lastUsedTime},计数器已累计至:${state.count}`);
126
+ log.info(`欢迎回来!上次使用时间: ${state.lastUsedTime},计数器已累计至: ${state.count}`);
127
127
  }
128
128
  try {
129
129
  // 模拟脚本运行期间状态的变化
@@ -143,9 +143,18 @@ try {
143
143
  import { getForBody, postForBody } from "@bettergi/utils";
144
144
 
145
145
  // 发送 GET 请求获取响应体内容
146
- // 提示:需要在 `manifest.json` 文件中配置 `http_allowed_urls`,并在 调度器 -> 修改通用配置 中启用
146
+ // 提示: 需要在 `manifest.json` 文件中配置 `http_allowed_urls`,并在 调度器 -> 修改通用配置 中启用
147
147
  const body1 = await getForBody("https://example.com/", undefined, { "User-Agent": "BetterGI" });
148
148
  const body2 = await postForBody("https://example.com/", undefined, { "User-Agent": "BetterGI" });
149
149
  log.info(`GET 请求响应体内容${body1}`);
150
150
  log.info(`POST 请求响应体内容${body2}`);
151
151
  ```
152
+
153
+ ### 日期时间
154
+
155
+ ```ts
156
+ // 获取下一个(含当日)凌晨4点的时间
157
+ const d1 = getNextDay4AM();
158
+ // 获取下一个(含当日)周一凌晨4点的时间
159
+ const d2 = getNextMonday4AM();
160
+ ```
package/dist/game.d.ts CHANGED
@@ -3,11 +3,11 @@ import { ListView } from "./ocr";
3
3
  * 临时设置游戏分辨率和DPI缩放比例,执行指定动作后恢复
4
4
  * 适用于使用了鼠标移动的操作,保证在不同的分辨率和DPI下都能正确地复现鼠标操作
5
5
  * @param w 游戏宽度
6
- * @param y 游戏高度
6
+ * @param h 游戏高度
7
7
  * @param dpi 系统屏幕的DPI缩放比例
8
8
  * @param action 执行动作
9
9
  */
10
- export declare const withGameMetrics: <T>(w: number, y: number, dpi: number, action: () => Promise<T> | T) => Promise<T>;
10
+ export declare const withGameMetrics: <T>(w: number, h: number, dpi: number, action: () => Promise<T> | T) => Promise<T>;
11
11
  /**
12
12
  * 打开派蒙菜单
13
13
  */
@@ -27,12 +27,17 @@ export declare const openMenu: (name: "\u62CD\u7167" | "\u516C\u544A" | "\u90AE\
27
27
  * @param name 菜单页面名称
28
28
  * @param listView 菜单页面视图参数
29
29
  */
30
- export declare const openMenuPage: (name: string, listView?: ListView) => Promise<void>;
30
+ export declare const openMenuPage: (name: "\u5546\u57CE" | "\u961F\u4F0D\u914D\u7F6E" | "\u597D\u53CB" | "\u6210\u5C31" | "\u56FE\u9274" | "\u89D2\u8272\u56FE\u9274" | "\u89D2\u8272" | "\u63D0\u5347\u6307\u5357" | "\u80CC\u5305" | "\u4EFB\u52A1" | "\u5730\u56FE" | "\u6D3B\u52A8" | "\u5192\u9669\u4E4B\u8BC1" | "\u7948\u613F" | "\u7EAA\u884C" | "\u591A\u4EBA\u6E38\u620F" | "\u5343\u661F\u5546\u57CE" | "\u4EBA\u6C14\u5947\u57DF" | "\u5947\u57DF\u6536\u85CF" | "\u6211\u7684\u5947\u57DF" | "\u5927\u5385" | "\u88C5\u626E\u642D\u914D" | "\u9882\u613F" | "\u7EAA\u6E38" | (string & {}), listView?: ListView) => Promise<void>;
31
31
  type ClockOptions = {
32
+ /** 时钟中心X坐标 */
32
33
  centerX?: number;
34
+ /** 时钟中心Y坐标 */
33
35
  centerY?: number;
36
+ /** 偏移小时数(默认: 6) */
34
37
  offsetHours?: number;
38
+ /** 时钟半径(默认: 154) */
35
39
  radius?: number;
40
+ /** 平滑度(默认: 3) */
36
41
  smooth?: number;
37
42
  };
38
43
  /**
package/dist/game.js CHANGED
@@ -5,14 +5,14 @@ import { findTextInDirection, findTextWithinBounds, findTextWithinListView } fro
5
5
  * 临时设置游戏分辨率和DPI缩放比例,执行指定动作后恢复
6
6
  * 适用于使用了鼠标移动的操作,保证在不同的分辨率和DPI下都能正确地复现鼠标操作
7
7
  * @param w 游戏宽度
8
- * @param y 游戏高度
8
+ * @param h 游戏高度
9
9
  * @param dpi 系统屏幕的DPI缩放比例
10
10
  * @param action 执行动作
11
11
  */
12
- export const withGameMetrics = async (w, y, dpi, action) => {
12
+ export const withGameMetrics = async (w, h, dpi, action) => {
13
13
  const { width, height, screenDpiScale } = genshin;
14
14
  try {
15
- setGameMetrics(w, y, dpi);
15
+ setGameMetrics(w, h, dpi);
16
16
  return await action();
17
17
  }
18
18
  finally {
@@ -26,7 +26,7 @@ export const openPaimonMenu = async () => {
26
26
  // 1.返回主界面
27
27
  await genshin.returnMainUi();
28
28
  // 2.打开派蒙菜单
29
- await assertRegionAppearing(() => findTextWithinBounds("世界等级", 300, 230, 440, 100), "打开派蒙菜单超时", () => keyPress("ESCAPE"));
29
+ await assertRegionAppearing(() => findTextWithinBounds("生日", 300, 230, 440, 100), "打开派蒙菜单超时", () => keyPress("ESCAPE"));
30
30
  };
31
31
  /**
32
32
  * 打开游戏菜单(左侧按钮)
@@ -101,10 +101,11 @@ export const setTimeTo = async (hour, minute, options) => {
101
101
  .map(rad => ({ x: radius * Math.cos(rad), y: radius * Math.sin(rad) }))
102
102
  // 计算绝对坐标
103
103
  .map(p => ({ x: Math.round(p.x + centerX), y: Math.round(p.y + centerY) })));
104
- // 3.拨动指针
104
+ // 3.调整时间
105
105
  await withGameMetrics(1920, 1080, 1.5, async () => {
106
+ // 拨动指针
106
107
  await mouseMoveAlongWaypoints(waypoints, { shouldDrag: true });
107
- // 3.点击确认按钮,等待调整结束
108
+ // 点击确认按钮,等待调整结束
108
109
  await assertRegionAppearing(() => findTextInDirection("时间少于", "south-east", { contains: true }), "调整时间超时", () => {
109
110
  findTextInDirection("确认", "south-east")?.click();
110
111
  }, { maxAttempts: 20, retryInterval: 1000 });
@@ -120,12 +121,12 @@ export const setTimeTo = async (hour, minute, options) => {
120
121
  export const setTime = async (period, options) => {
121
122
  switch (period) {
122
123
  case "night":
123
- return setTimeTo(0, 0, options);
124
+ return setTimeTo(0, 5, options);
124
125
  case "morning":
125
- return setTimeTo(6, 0, options);
126
+ return setTimeTo(6, 5, options);
126
127
  case "noon":
127
- return setTimeTo(12, 0, options);
128
+ return setTimeTo(12, 5, options);
128
129
  case "evening":
129
- return setTimeTo(18, 0, options);
130
+ return setTimeTo(18, 5, options);
130
131
  }
131
132
  };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,16 @@
1
+ /** 断言 */
1
2
  export * from "./asserts";
2
- export * from "./workflow";
3
+ /** 游戏操作 */
3
4
  export * from "./game";
5
+ /** HTTP 请求 */
4
6
  export * from "./http";
7
+ /** 鼠标操作 */
5
8
  export * from "./mouse";
9
+ /** 图像识别 */
6
10
  export * from "./ocr";
11
+ /** 数据存储 */
7
12
  export * from "./store";
13
+ /** 日期时间 */
14
+ export * from "./time";
15
+ /** 流程控制 */
16
+ export * from "./workflow";
package/dist/index.js CHANGED
@@ -1,7 +1,16 @@
1
+ /** 断言 */
1
2
  export * from "./asserts";
2
- export * from "./workflow";
3
+ /** 游戏操作 */
3
4
  export * from "./game";
5
+ /** HTTP 请求 */
4
6
  export * from "./http";
7
+ /** 鼠标操作 */
5
8
  export * from "./mouse";
9
+ /** 图像识别 */
6
10
  export * from "./ocr";
11
+ /** 数据存储 */
7
12
  export * from "./store";
13
+ /** 日期时间 */
14
+ export * from "./time";
15
+ /** 流程控制 */
16
+ export * from "./workflow";
package/dist/mouse.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  type MouseWaypointsOptions = {
2
+ /** 是否按住鼠标左键拖动 */
2
3
  shouldDrag?: boolean;
4
+ /** 超时时间(毫秒,默认: 不超时) */
3
5
  timeout?: number;
4
6
  };
5
7
  /**
@@ -21,13 +23,13 @@ export declare const mouseMoveAlongWaypoints: (waypoints: {
21
23
  */
22
24
  export declare const mouseDrag: (x1: number, y1: number, x2: number, y2: number) => Promise<boolean>;
23
25
  type NaturalMouseMoveOptions = {
24
- /** 移动持续时间(毫秒) */
26
+ /** 移动持续时间(毫秒,默认: 800) */
25
27
  duration: number;
26
- /** 摆动幅度(像素) */
28
+ /** 摆动幅度(默认: 30) */
27
29
  wiggle?: number;
28
- /** 随机种子 */
30
+ /** 随机种子(可选) */
29
31
  seed?: number;
30
- /** 缓动函数 */
32
+ /** 缓动函数(默认: ease-out-cubic) */
31
33
  easing?: (progress: number) => number;
32
34
  };
33
35
  /** 贝塞尔曲线生成鼠标移动路径点 */
package/dist/ocr.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { RetryOptions } from "./workflow";
2
- type MatchDirection = "north" | "north-east" | "east" | "south-east" | "south" | "south-west" | "west" | "north-west";
2
+ type MatchDirection = "north" /** 上半边 */ | "north-east" /** 右上四分之一 */ | "east" /** 右半边 */ | "south-east" /** 右下四分之一 */ | "south" /** 下半边 */ | "south-west" /** 左下四分之一 */ | "west" /** 左半边 */ | "north-west"; /** 左上四分之一 */
3
3
  /**
4
4
  * 在整个画面内搜索图片
5
5
  * @param path 图片路径
@@ -25,9 +25,9 @@ export declare const findImageWithinBounds: (path: string, x: number, y: number,
25
25
  export declare const findImageInDirection: (path: string, direction: MatchDirection) => Region | undefined;
26
26
  /** 文本搜索选项 */
27
27
  type TextMatchOptions = {
28
- /** 是否忽略大小写(默认:是) */
28
+ /** 是否忽略大小写(默认: 是) */
29
29
  ignoreCase?: boolean;
30
- /** 是否非完全匹配(默认:否) */
30
+ /** 是否非完全匹配(默认: 否) */
31
31
  contains?: boolean;
32
32
  };
33
33
  /**
@@ -64,11 +64,11 @@ export type ListView = {
64
64
  h: number;
65
65
  /** 列表项高度 */
66
66
  lineHeight: number;
67
- /** 每次滚动的行数 */
67
+ /** 每次滚动的行数(默认: 1) */
68
68
  scrollLines?: number;
69
- /** 横向内边距 */
69
+ /** 横向内边距 (默认: 10) */
70
70
  paddingX?: number;
71
- /** 纵向内边距 */
71
+ /** 纵向内边距 (默认: 10) */
72
72
  paddingY?: number;
73
73
  };
74
74
  /**
package/dist/ocr.js CHANGED
@@ -135,7 +135,7 @@ export const findTextWithinListView = async (text, listView, matchOptions, retry
135
135
  return false;
136
136
  }
137
137
  }
138
- // 异常情况:找不到任何文本
138
+ // 异常情况: 找不到任何文本
139
139
  return true;
140
140
  };
141
141
  const isTextFoundOrBottomReached = await waitForAction(() => findTargetText() != undefined || isReachedBottom(), async () => {
package/dist/time.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ /**
2
+ * 获取下一个(含当日)凌晨4点的时间
3
+ */
4
+ export declare const getNextDay4AM: () => Date;
5
+ /**
6
+ * 获取下一个(含当日)周一凌晨4点的时间
7
+ */
8
+ export declare const getNextMonday4AM: () => Date;
package/dist/time.js ADDED
@@ -0,0 +1,25 @@
1
+ /**
2
+ * 获取下一个(含当日)凌晨4点的时间
3
+ */
4
+ export const getNextDay4AM = () => {
5
+ const now = new Date();
6
+ const result = new Date(now);
7
+ result.setHours(4, 0, 0, 0);
8
+ // 如果当前时间在4点前,则返回今天4点,否则返回明天4点
9
+ const daysUntilNextDay = now.getHours() < 4 ? 0 : 1;
10
+ result.setDate(now.getDate() + daysUntilNextDay);
11
+ return result;
12
+ };
13
+ /**
14
+ * 获取下一个(含当日)周一凌晨4点的时间
15
+ */
16
+ export const getNextMonday4AM = () => {
17
+ const now = new Date();
18
+ const result = new Date(now);
19
+ result.setHours(4, 0, 0, 0);
20
+ // 如果当前为周一且时间在4点前,则返回今天4点,否则返回下一个周一的4点
21
+ const currentDay = now.getDay();
22
+ const daysUntilNextMonday = currentDay === 1 && now.getHours() < 4 ? 0 : (8 - currentDay) % 7;
23
+ result.setDate(now.getDate() + daysUntilNextMonday);
24
+ return result;
25
+ };
@@ -1,8 +1,8 @@
1
1
  export type Action = () => Promise<void> | void;
2
2
  export type RetryOptions = {
3
- /** 重试次数 */
3
+ /** 重试次数(默认: 5) */
4
4
  maxAttempts?: number;
5
- /** 重试间隔(毫秒) */
5
+ /** 重试间隔(毫秒,默认: 1000) */
6
6
  retryInterval?: number;
7
7
  };
8
8
  /**
package/dist/workflow.js CHANGED
@@ -13,10 +13,12 @@ const defaultRetryInterval = 1000;
13
13
  export const waitForAction = async (condition, retryAction, options) => {
14
14
  const { maxAttempts = defaultMaxAttempts, retryInterval = defaultRetryInterval } = options || {};
15
15
  for (let i = 0; i < maxAttempts; i++) {
16
- if (condition())
17
- return true;
16
+ if (i === 0 && condition())
17
+ return true; // fast path
18
18
  await retryAction?.();
19
19
  await sleep(retryInterval);
20
+ if (condition())
21
+ return true;
20
22
  }
21
23
  return false;
22
24
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bettergi/utils",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "开发 BetterGI 脚本常用工具集",
5
5
  "type": "module",
6
6
  "author": "Bread Grocery<https://github.com/breadgrocery>",
package/dist/flow.d.ts DELETED
@@ -1,30 +0,0 @@
1
- /**
2
- * 等待直到条件满足或超时
3
- * @param condition 等待的条件判断函数,返回 true 表示条件满足
4
- * @param timeout 超时时间(毫秒),默认 3000 毫秒
5
- * @param interval 等待间隔(毫秒),默认 300 毫秒
6
- * @param action 每次等待循环中执行的操作(可选)
7
- * @returns - true 在超时前条件已满足
8
- * - false 在超时后条件仍未满足
9
- */
10
- export declare const waitUntil: (condition: (context: Record<string, any>) => boolean, timeout?: number, interval?: number, action?: (context: Record<string, any>) => Promise<void> | void) => Promise<boolean>;
11
- /**
12
- * 断言区域存在
13
- * @param 获取区域的函数
14
- * @param message 错误信息
15
- * @param timeout 超时时间(毫秒),默认 3000 毫秒
16
- * @param interval 等待间隔(毫秒),默认 300 毫秒
17
- * @param action 每次等待循环中执行的操作(可选)
18
- * @throws 如果区域在超时时间内未找到则抛出错误
19
- */
20
- export declare const assertExists: (regionProvider: () => Region | undefined, message?: string, timeout?: number, interval?: number, action?: (context: Record<string, any>) => Promise<void> | void) => Promise<void>;
21
- /**
22
- * 断言区域不存在
23
- * @param 获取区域的函数
24
- * @param message 错误信息
25
- * @param timeout 超时时间(毫秒),默认 3000 毫秒
26
- * @param interval 等待间隔(毫秒),默认 300 毫秒
27
- * @param action 每次等待循环中执行的操作(可选)
28
- * @throws 如果区域在超时时间内仍然存在则抛出错误
29
- */
30
- export declare const assertNotExists: (regionProvider: () => Region | undefined, message?: string, timeout?: number, interval?: number, action?: (context: Record<string, any>) => Promise<void> | void) => Promise<void>;
package/dist/flow.js DELETED
@@ -1,52 +0,0 @@
1
- /** 默认超时时间(毫秒) */
2
- const defaultTimeout = 3 * 1000;
3
- /** 默认等待间隔(毫秒) */
4
- const defaultInterval = 300;
5
- /**
6
- * 等待直到条件满足或超时
7
- * @param condition 等待的条件判断函数,返回 true 表示条件满足
8
- * @param timeout 超时时间(毫秒),默认 3000 毫秒
9
- * @param interval 等待间隔(毫秒),默认 300 毫秒
10
- * @param action 每次等待循环中执行的操作(可选)
11
- * @returns - true 在超时前条件已满足
12
- * - false 在超时后条件仍未满足
13
- */
14
- export const waitUntil = async (condition, timeout = defaultTimeout, interval = defaultInterval, action) => {
15
- const context = {};
16
- const deadline = Date.now() + timeout;
17
- while (Date.now() < deadline) {
18
- if (condition(context))
19
- return true;
20
- await action?.(context);
21
- await sleep(interval);
22
- }
23
- return false;
24
- };
25
- /**
26
- * 断言区域存在
27
- * @param 获取区域的函数
28
- * @param message 错误信息
29
- * @param timeout 超时时间(毫秒),默认 3000 毫秒
30
- * @param interval 等待间隔(毫秒),默认 300 毫秒
31
- * @param action 每次等待循环中执行的操作(可选)
32
- * @throws 如果区域在超时时间内未找到则抛出错误
33
- */
34
- export const assertExists = async (regionProvider, message = "断言区域存在失败", timeout = defaultTimeout, interval = defaultInterval, action) => {
35
- const ok = await waitUntil(() => regionProvider() !== undefined, timeout, interval, action);
36
- if (!ok)
37
- throw new Error(message);
38
- };
39
- /**
40
- * 断言区域不存在
41
- * @param 获取区域的函数
42
- * @param message 错误信息
43
- * @param timeout 超时时间(毫秒),默认 3000 毫秒
44
- * @param interval 等待间隔(毫秒),默认 300 毫秒
45
- * @param action 每次等待循环中执行的操作(可选)
46
- * @throws 如果区域在超时时间内仍然存在则抛出错误
47
- */
48
- export const assertNotExists = async (regionProvider, message = "断言区域不存在失败", timeout = defaultTimeout, interval = defaultInterval, action) => {
49
- const ok = await waitUntil(() => regionProvider() === undefined, timeout, interval, action);
50
- if (!ok)
51
- throw new Error(message);
52
- };