js: CD-Aware-AutoGather: 扫描待处理的材料时跳过空文件夹 (#1336)

This commit is contained in:
Patrick-Ze
2025-07-12 11:31:10 +08:00
committed by GitHub
parent 0ed00bdf19
commit c94975b85e
7 changed files with 67 additions and 35 deletions

View File

@@ -28,11 +28,11 @@
| 选项 | 说明 |
| ---- | ---- |
| `地图追踪`中已订阅的任务目录的处理方式 | 有三种处理方式:<br>- `每次自动扫描,并采集扫描到的所有材料`<br>- `手动扫描,并采集扫描到的所有材料`<br>- `手动扫描,只采集已勾选的材料`<br>`自动扫描`会在每次执行采集前扫描当前已订阅的任务目录,`手动扫描`则是手动将运行模式切换到扫描模式执行脚本进行扫描。<br>`采集所有`会无视后面的每个材料⬇️是否选中,`采集已勾选`则是根据勾选的列表进行采集 |
| 设置首选队伍名称 | 执行采集任务前切换到指定的队伍,未设置则不切换。 |
| 设置备选队伍名称 | 首选队伍缺少对应的采集角色时使用。<br>两支队伍的名称不要存在包含关系,例如不能一支叫`特产`一支叫`特产备选` |
| 停止运行时间 | 超过此时间后停止后续的任务会等待正在运行的那条json路线结束。 |
| 我肝的账号不止一个 | 如果你有多个账号,可以选中此选项,选中后将分账号维护对应的材料刷新时间。 |
| `地图追踪`中已订阅的任务目录的处理方式 | 有三种处理方式:<br>- `每次自动扫描,并采集扫描到的所有材料`<br>- `手动扫描,并采集扫描到的所有材料`<br>- `手动扫描,只采集已勾选的材料`<br>`自动扫描`会在每次执行采集前扫描当前已订阅的任务目录,`手动扫描`则是手动将运行模式切换到扫描模式执行脚本进行扫描。<br>`采集所有`会无视后面的每个材料⬇️是否选中,`采集已勾选`则是根据勾选的列表进行采集 |
| 即使同一种材料有多个版本的路线,也全都执行采集 | 如果某种材料选中了多个版本的路线(常见于不同作者),默认只会执行第一个。勾选此选项后会每个版本都执行,可能造成部分点位重复(空跑)。 |
| `↓` 地方特产\稻妻\绯樱绣球 | 根据你订阅的路径追踪任务数量,这里将会显示相应个数的选择框。<br>勾选后将执行你选中的条目的采集任务。<br>Tip: `↓`符号是在提示你应该勾选文本下面的选择框 |

View File

@@ -7,17 +7,16 @@
let scriptContext = {
scriptStartTime: new Date(),
version: "1.0"
version: "1.1",
};
/**
* 将 Date 对象格式化为 ISO 8601 字符串包含本地时区2020-09-28T20:20:20.999+08:00
* 将 Date 对象格式化为 ISO 8601 字符串包含本地时区2020-09-28T20:20:20+08:00
* @param {Date} date - 要格式化的日期对象
* @returns {string} 格式化后的字符串
*/
function formatDateTime(date) {
const pad = (n) => n.toString().padStart(2, "0");
const padMs = (n) => n.toString().padStart(3, "0");
const year = date.getFullYear();
const month = pad(date.getMonth() + 1);
@@ -25,7 +24,6 @@ function formatDateTime(date) {
const hour = pad(date.getHours());
const minute = pad(date.getMinutes());
const second = pad(date.getSeconds());
const ms = padMs(date.getMilliseconds());
// 获取时区偏移分钟转换成±HH:MM
const offset = -date.getTimezoneOffset();
@@ -33,7 +31,7 @@ function formatDateTime(date) {
const offsetHour = pad(Math.floor(Math.abs(offset) / 60));
const offsetMin = pad(Math.abs(offset) % 60);
return `${year}-${month}-${day}T${hour}:${minute}:${second}.${ms}${sign}${offsetHour}:${offsetMin}`;
return `${year}-${month}-${day}T${hour}:${minute}:${second}${sign}${offsetHour}:${offsetMin}`;
}
/**
@@ -329,9 +327,10 @@ async function switchPartySafely(partyName) {
*
* @async
* @param {*} multiAccount 是否使用OCR区分多个账号可以传入一个设置项
* @param {boolean} mask 对UID进行掩码只保留开头和结尾
* @returns {Promise<string>} 当前账号的UID如果不区分多账号或OCR失败则返回"默认账号"。
*/
async function getGameAccount(multiAccount = false) {
async function getGameAccount(multiAccount = false, mask = true) {
let account = "默认账号";
if (!multiAccount) {
return account;
@@ -352,6 +351,10 @@ async function getGameAccount(multiAccount = false) {
const match = text.match(/\d+/);
if (match) {
account = match[0];
if (mask) {
// 避免完整UID出现在log中造成意外暴露
account = account.replace(/\d*(\d{4})\d{4}/, (match, group1) => match.replace(group1, "xxxx"));
}
}
break;
}
@@ -397,7 +400,7 @@ function getScriptName() {
* @returns {string} - 文件名。
*/
function basename(filePath) {
const lastSlashIndex = filePath.lastIndexOf('\\'); // 或者使用 '/'
const lastSlashIndex = filePath.lastIndexOf("\\"); // 或者使用 '/'
return filePath.substring(lastSlashIndex + 1);
}
@@ -504,8 +507,9 @@ function _fakeLogCore(name, isJs = true, dateIn = null) {
logTime = dateIn;
}
// 时间部分从第11位开始长度是12"20:20:20.999"
const formattedTime = formatDateTime(logTime).slice(11, 23);
const ms = logTime.getMilliseconds().toString().padStart(3, "0");
// 时间部分从第11位开始长度是12"20:20:20"
const formattedTime = formatDateTime(logTime).slice(11, 19) + "." + ms;
if (isStart) {
logMessage =
@@ -594,3 +598,24 @@ function logFakeScriptEnd({ scriptName = null, startTime = new Date() } = {}) {
}
return _fakeLogCore(scriptName, true, startTime);
}
/**
* 等待传送结束
* @param {Int} timeout 单位为ms
* @note 参考了七圣召唤七日历练脚本
*/
async function waitTpFinish(timeout = 30000) {
const region = RecognitionObject.ocr(1690, 230, 75, 350); // 队伍名称区域
const startTime = Date.now();
await sleep(500); //点击传送后等待一段时间避免误判
while (Date.now() - startTime < timeout) {
let res = captureGameRegion().find(region);
if (!res.isEmpty()) {
await sleep(600); //传送结束后有僵直
return;
}
await sleep(100);
}
throw new Error("传送时间超时");
}

View File

@@ -167,9 +167,8 @@ async function runClearMode() {
const resetTimeStr = formatDateTime(getDefaultTime());
let account = await getGameAccount(settings.iHaveMultipleAccounts);
for (const pathTask of selectedMaterials) {
const jsonFiles = filterFilesInTaskDir(pathTask.label);
const recordFile = getRecordFilePath(account, pathTask);
const lines = jsonFiles.map((filePath) => {
const lines = pathTask.jsonFiles.map((filePath) => {
return `${basename(filePath)}\t${resetTimeStr}`;
});
const content = lines.join("\n");
@@ -280,7 +279,7 @@ async function runPathScriptFile(jsonPath) {
async function runPathTaskIfCooldownExpired(account, pathTask) {
const recordFile = getRecordFilePath(account, pathTask);
const jsonFiles = filterFilesInTaskDir(pathTask.label);
const jsonFiles = pathTask.jsonFiles;
log.info("{0}共有{1}条路线", pathTask.label, jsonFiles.length);
@@ -397,7 +396,13 @@ function getSelectedMaterials() {
if (selectAllMaterials || settings[entry.name] === true) {
let index = entry.label.indexOf(" ");
entry.label = entry.label.slice(index + 1); // 去除⬇️指示
const jsonFiles = filterFilesInTaskDir(entry.label);
if (jsonFiles.length > 0) {
entry.jsonFiles = jsonFiles;
selectedMaterials.push(entry);
} else {
log.debug("跳过空文件夹: {0}", entry.label);
}
}
}
}

View File

@@ -1,7 +1,7 @@
{
"manifest_version": 1,
"name": "带CD管理的自动采集",
"version": "1.4",
"version": "1.5",
"bgi_version": "0.45.0",
"description": "自动同步你通过BetterGI订阅的地图追踪任务执行采集任务并管理材料刷新时间支持多账号。\n首次使用前请先简单阅读说明可在`全自动`——`JS脚本`页面,点击本脚本名称查看)",
"authors": [

View File

@@ -9,6 +9,16 @@
"清除运行记录(重置材料刷新时间)"
]
},
{
"name": "subscribeMode",
"type": "select",
"label": "'地图追踪'中已订阅的任务目录的处理方式:",
"options": [
"每次自动扫描,并采集扫描到的所有材料",
"手动扫描,并采集扫描到的所有材料",
"手动扫描,只采集已勾选的材料"
]
},
{
"name": "partyName",
"type": "input-text",
@@ -29,16 +39,6 @@
"type": "checkbox",
"label": "我肝的账号不止一个(选中后将分账号维护对应的材料刷新时间)"
},
{
"name": "subscribeMode",
"type": "select",
"label": "'地图追踪'中已订阅的任务目录的处理方式:",
"options": [
"每次自动扫描,并采集扫描到的所有材料",
"手动扫描,并采集扫描到的所有材料",
"手动扫描,只采集已勾选的材料"
]
},
{
"name": "acceptMultiplePathOfSameMaterial",
"type": "checkbox",

View File

@@ -9,6 +9,16 @@
"清除运行记录(重置材料刷新时间)"
]
},
{
"name": "subscribeMode",
"type": "select",
"label": "'地图追踪'中已订阅的任务目录的处理方式:",
"options": [
"每次自动扫描,并采集扫描到的所有材料",
"手动扫描,并采集扫描到的所有材料",
"手动扫描,只采集已勾选的材料"
]
},
{
"name": "partyName",
"type": "input-text",
@@ -29,16 +39,6 @@
"type": "checkbox",
"label": "我肝的账号不止一个(选中后将分账号维护对应的材料刷新时间)"
},
{
"name": "subscribeMode",
"type": "select",
"label": "'地图追踪'中已订阅的任务目录的处理方式:",
"options": [
"每次自动扫描,并采集扫描到的所有材料",
"手动扫描,并采集扫描到的所有材料",
"手动扫描,只采集已勾选的材料"
]
},
{
"name": "acceptMultiplePathOfSameMaterial",
"type": "checkbox",

View File

@@ -34,6 +34,8 @@
这种情况只能手动操作小号投降(见前面的[使用说明](#使用说明))。由于发起邀请的角色(大号)操作会更复杂,建议用脚本来发起邀请。
或者你也可以在QQ群或者QQ频道找同样想刷熟练度的玩家互相挂投降模式协助对方。[右击此链接——在新窗口中打开](https://bettergi.com/community.html)查看QQ群和QQ频道信息。
- **只有一台电脑,能不能运行一个原神,运行一个云原神?**
Better GI在检测到已经有运行中的实例时不会再启动第二份但并不像原神那样针对同一台物理设备。我也尝试过通过创建多个Windows账号以双开Better GI但是后启动的截图器会报错可能是无法同时运行多个截图器。