feat(auto-tools): 添加择优模式功能

- 修改路径列表处理逻辑,为每个路径对象添加选中状态字段
- 更新执行日志显示,添加模式和选中路径信息输出
- 实现择优运行函数,优先执行上次未完成的任务路径
- 添加choose_best配置选项,支持择优模式开关设置
- 优化记录检查逻辑,提升路径匹配性能
- 整理代码结构,将择优运行逻辑封装为独立函数

feat(auto-tools): 添加黑白名单配置功能

- 在设置中添加白名单和黑名单输入框配置
- 新增 config_list 对象存储黑白名单数组
- 实现从设置读取黑白名单并解析为数组
- 添加路径过滤逻辑,支持黑名单排除和白名单优先
- 实现黑白名单预处理,去除空字符串并trim
- 优化路径遍历,跳过黑名单中的路径
fix(FullyAutoAndSemiAutoTools): 修复空映射时的执行逻辑并移除调试代码

- 添加了 needRunMap 非空检查避免无效执行
- 移除了调试用的日志记录和按键等待功能
- 优化了代码流程控制结构
feat(auto-tools): 添加记录管理和用户配置功能

- 定义记录文件路径并实现saveRecord函数保存记录到JSON文件
- 在initRecord函数中添加读取记录文件逻辑,支持Set数据类型的转换
- 实现同一天记录数据合并功能,确保Set属性存在
- 添加用户配置映射管理,支持不同UID的配置存储
- 实现配置模式选择功能,支持刷新和加载两种模式
- 添加错误处理机制确保文件操作的安全性
- 优化配置文件的读写流程,提升数据持久化可靠性
This commit is contained in:
yan
2026-01-11 12:32:18 +08:00
parent 0ff05d960f
commit 44230d93e3
2 changed files with 219 additions and 42 deletions

View File

@@ -10,9 +10,11 @@ const pathingName = "pathing"
// const pathRunMap = new Map([])
const needRunMap = new Map([])
const PATHING_ALL = new Array({level: 0, name: `${pathingName}`, parent_name: "", child_names: []})
const settingsNameList = new Array()
let settingsNameList = new Array()
const settingsNameAsList = new Array()
let PATH_JSON_LIST = new Array()
// 定义记录文件的路径
let RecordText = "Record\\record.json"
let RecordList = new Array()
let RecordLast = {
name: "",
@@ -30,6 +32,38 @@ const Record = {
errorPaths: new Set(), // 记录错误路径
groupPaths: new Set(), // 记录分组路径
}
const config_list = {
black: [],
white: [],
}
/**
* 保存当前记录到记录列表并同步到文件
* 该函数在保存前会将Set类型的数据转换为数组格式确保JSON序列化正常进行
*/
function saveRecord() {
// 保存前将 Set 转换为数组
// 创建一个新的记录对象,包含原始记录的所有属性
const recordToSave = {
// 使用展开运算符复制Record对象的所有属性
...Record,
// 将paths Set转换为数组
paths: [...Record.paths],
// 将errorPaths Set转换为数组
errorPaths: [...Record.errorPaths],
// 将groupPaths Set转换为数组并对每个元素进行特殊处理
groupPaths: [...Record.groupPaths].map(item => ({
// 保留name属性
name: item.name,
// 将item中的paths Set转换为数组
paths: [...item.paths]
}))
};
// 将处理后的记录添加到记录列表
RecordList.push(recordToSave)
// 将记录列表转换为JSON字符串并同步写入文件
file.writeTextSync(RecordText, JSON.stringify(RecordList))
}
/**
* 初始化记录函数
@@ -43,6 +77,28 @@ async function initRecord() {
Record.timestamp = Date.now()
// 获取并设置调整后的日期数据
Record.data = getAdjustedDate()
try {
// 尝试读取记录文件
// 读取后将数组转换回 Set处理特殊的数据结构
RecordList = JSON.parse(file.readTextSync(RecordText), (key, value) => {
// 处理普通路径集合
if (key === 'paths' || key === 'errorPaths') {
return new Set(value);
}
// 处理分组路径集合保持嵌套的Set结构
if (key === 'groupPaths') {
return new Set(value.map(item => ({
name: item.name,
paths: new Set(item.paths)
})));
}
return value;
});
} catch (e) {
// 如果读取文件出错,则忽略错误(可能是文件不存在或格式错误)
}
// 如果记录列表不为空,则查找最新记录
if (RecordList.length > 0) {
// 最优解:一次遍历找到最新的记录
@@ -61,6 +117,15 @@ async function initRecord() {
RecordLast = latestRecord ? latestRecord : RecordLast;
}
if (RecordLast.uid === Record.uid && Record.data === RecordLast.data) {
// 判断是否为同一天 合并跑过的数据
// 确保 RecordLast 的 Set 属性存在
if (!RecordLast.paths || !(RecordLast.paths instanceof Set)) {
RecordLast.paths = new Set();
}
if (!RecordLast.groupPaths || !(RecordLast.groupPaths instanceof Set)) {
RecordLast.groupPaths = new Set();
}
// 判断是否为同一天 合并跑过的数据
Record.paths = new Set([...Record.paths, ...RecordLast.paths])
Record.groupPaths = new Set([...Record.groupPaths, ...RecordLast.groupPaths])
@@ -214,6 +279,8 @@ async function init() {
debug = (debug) ? debug : settings.debug
isDebug = settings.isDebug
SEMI_AUTO = settings.mode === settings.mode
config_list.black = settings.config_black_list ? settings.config_black_list.split(",") : []
config_list.white = settings.config_white_list ? settings.config_white_list.split(",") : []
if (SEMI_AUTO && !AUTO_STOP) {
throw new Error("半自动模式下必须开启自动停止")
}
@@ -223,10 +290,24 @@ async function init() {
log.error("{0}文件夹不存在请在BetterGI中右键点击本脚本选择{1}。然后双击脚本目录下的{2}文件以创建文件夹链接", `${pathingName}`, "打开所在目录", batFile);
return false;
}
//刷新settings
if (true) {
//记录初始化
await initRecord();
// 读取现有配置并合并
let uidSettingsMap = new Map()
const uidSettingsJson = "settings/uid.json";
try {
const existingData = JSON.parse(file.readTextSync(uidSettingsJson))
uidSettingsMap = new Map(existingData)
} catch (e) {
// 文件不存在时使用空Map
log.debug("配置文件不存在,将创建新的");
}
let levelName = "treeLevel"
async function refreshALL() {
let level = 0
let levelName = "treeLevel"
// 获取当前路径下的所有文件/文件夹
let pathSyncList = file.readPathSync(`${PATHING_ALL[level].name}`);
log.debug("{0}文件夹下有{1}个文件/文件夹", `${pathingName}`, pathSyncList.length);
@@ -238,7 +319,7 @@ async function init() {
options: []
}
for (const element of pathSyncList) {
log.warn("element={0}", element)
// log.warn("element={0}", element)
parentJson.options.push(element.replace(`${pathingName}\\`, ""))
}
await addUniquePath({level: level, name: `${pathingName}`, parent_name: '', child_names: parentJson.options})
@@ -247,8 +328,28 @@ async function init() {
await debugKey('log-treePathList.json', JSON.stringify(treePathList))
let pathJsonList = await treeToList(treePathList)
PATH_JSON_LIST = pathJsonList
// 预处理黑白名单数组移除空字符串并trim
const processedBlackList = config_list.black
.map(item => item.trim())
.filter(item => item !== "");
const processedWhiteList = config_list.white
.map(item => item.trim())
.filter(item => item !== "");
for (const element of pathJsonList) {
const pathRun = element.path
// 检查路径是否被允许
const isBlacklisted = processedBlackList.some(item => pathRun.includes(item));
const isWhitelisted = processedWhiteList.some(item => pathRun.includes(item));
if (isBlacklisted && !isWhitelisted) {
continue;
}
const level_parent_name = getChildFolderNameFromRoot(pathRun, level + 1);
const level1_name = getChildFolderNameFromRoot(pathRun, level + 1 + 1);
let level2_name = getChildFolderNameFromRoot(pathRun, level + 2 + 1);
@@ -290,7 +391,7 @@ async function init() {
const groupLevel = groupByLevel(PATHING_ALL);
const initLength = settingsList.length
let parentNameLast = undefined
let parentNameNow = undefined
// let parentNameNow = undefined
const line = 30
const br = `${"=".repeat(line)}\n`
groupLevel.filter(list => list.length > 0).forEach(
@@ -298,7 +399,7 @@ async function init() {
let i = 0
list.filter(item => item && item.child_names && item.child_names.length > 0).forEach(item => {
const name = `${levelName}_${item.level}_${i}`
let prefix = "\n"
let prefix = ''
if (item.parent_name !== parentNameLast) {
parentNameLast = item.parent_name;
let b = (line - item.parent_name.length) % 2 === 0;
@@ -320,7 +421,17 @@ async function init() {
settings_as_name: item.name
})
settingsNameList.push(name)
settingsList.push(leveJson)
const existingIndex = settingsList.findIndex(item => item.name === leveJson.name);
if (existingIndex !== -1) {
// 替换已存在的配置项
settingsList[existingIndex] = leveJson;
} else {
if (item.parent_name !== parentNameLast) {
settingsList.push({type: "separator"})
}
// 添加新的配置项
settingsList.push(leveJson);
}
i++
}
})
@@ -334,9 +445,32 @@ async function init() {
// 刷新settings自动设置密钥
item.default = manifest.key
})
// 更新当前用户的配置
uidSettingsMap.set(Record.uid, settingsList)
// 安全写入配置文件
try {
file.writeTextSync(uidSettingsJson, JSON.stringify([...uidSettingsMap]))
log.debug("用户配置已保存: {uid}", Record.uid)
} catch (error) {
log.error("保存用户配置失败: {error}", error.message)
}
file.writeTextSync(manifest.settings_ui, JSON.stringify(settingsList))
}
//刷新settings
if (settings.config_run === "刷新") {
await refreshALL();
} else if (true) {
//直接从配置文件中加载对应账号的配置
let uidSettings = uidSettingsMap.get(Record.uid);
if (uidSettings) {
file.writeTextSync(manifest.settings_ui, JSON.stringify(uidSettings))
}
configSettings = await initSettings()
settingsNameList = settingsNameList.concat(await getMultiCheckboxMap().then(map => {
return map.keys().filter(key => key.startsWith(levelName))
}))
}
// 初始化needRunMap
if (true) {
// for (let key of pathAsMap.keys()) {
@@ -347,10 +481,18 @@ async function init() {
const multi = await getValueByMultiCheckboxName(settingsName);
const settingsAsName = settingsNameAsList.find(item => item.settings_name === settingsName)
let list = PATH_JSON_LIST.filter(item =>
multi.some(element => item.path.includes(element))
).map(item => {
// 找到匹配的元素并填充到 selected 字段
const matchedElement = multi.find(element => item.path.includes(element));
return {name: item.name, parent_name: item.parent_name, selected: matchedElement || "", path: item.path}
});
if (list.length <= 0) {
continue
}
needRunMap.set(settingsAsName.settings_as_name, {
paths: (PATH_JSON_LIST.filter(item =>
multi.some(element => item.path.includes(element))
).map(item => item.path)),
paths: list,
as_name: settingsAsName.settings_as_name,
name: settingsAsName.settings_name
})
@@ -478,7 +620,11 @@ async function runList(list = [], stopKey = AUTO_STOP) {
// 遍历路径列表
for (let i = 0; i < list.length; i++) {
const path = list[i];
const onePath = list[i];
const path = onePath.path;
if (i === 0) {
log.info(`[{mode}] 开始执行[{1}-{2}]列表`, settings.mode, onePath.selected, onePath.parent_name);
}
log.debug('正在执行第{index}/{total}个路径: {path}', i + 1, list.length, path);
try {
@@ -516,10 +662,14 @@ async function runMap(map = new Map(), stopKey = AUTO_STOP) {
// 遍历Map中的所有键
for (const [key, one] of map.entries()) {
if (one.paths.size <= 0) {
continue
}
try {
// 记录开始执行任务的日志信息
log.info(`[{0}] 开始执行[{1}]...`, settings.mode, one.as_name);
// 执行当前任务关联的路径列表
await runList(one.paths, stopKey);
Record.groupPaths.add({
name: one.as_name,
@@ -722,52 +872,52 @@ async function treeToList(treeList = []) {
}
(async function () {
let RecordText = "Record\\record.json"
try {
RecordList = JSON.parse(file.readTextSync(RecordText))
} catch (e) {
}
try {
if (await init()) {
//记录初始化
await initRecord();
await main()
}
} finally {
RecordList.push(Record)
file.writeTextSync(RecordText, JSON.stringify(RecordList))
saveRecord();
}
})()
async function main() {
let lastRunMap = new Map()
if (RecordLast.groupPaths.size > 0 && RecordLast.paths.size !== RecordLast.groupPaths.size) {
// 由于在迭代过程中删除元素会影响迭代,先收集要删除的键
const keysToDelete = [];
// 优先跑上次群组没跑过的
// 使用 Set 提高性能
const lastListSet = new Set([...RecordLast.groupPaths].map(item => item.name));
for (const [key, one] of needRunMap.entries()) {
if (!lastListSet.has(key)) {
lastRunMap.set(key, one);
keysToDelete.push(key);
function chooseBestRun() {
if (settings.choose_best && RecordLast.paths.size > 0) {
// 由于在迭代过程中删除元素会影响迭代,先收集要删除的键
const keysToDelete = [];
// 优先跑上次没跑过的路径
// 使用 Set 提高性能
const lastListSet = new Set([...RecordLast.paths]);
for (const [key, one] of needRunMap.entries()) {
// 检查当前任务的路径是否都不在上次执行的路径中
const allPathsInLast = one.paths.every(pathObj => lastListSet.has(pathObj.path));
if (!allPathsInLast) {
lastRunMap.set(key, one);
keysToDelete.push(key);
}
}
// 然后批量删除
for (const key of keysToDelete) {
needRunMap.delete(key);
}
}
// 然后批量删除
for (const key of keysToDelete) {
needRunMap.delete(key);
}
}
await runMap(needRunMap)
chooseBestRun();
if (needRunMap.size > 0) {
await runMap(needRunMap)
}
if (lastRunMap.size > 0) {
await runMap(lastRunMap)
}
log.info(`[{mode}] path==>{path},请按下{key}以继续执行[${manifest.name} JS]`, settings.mode, "path", AUTO_STOP)
await keyMousePress(AUTO_STOP);
log.info(`[{mode}] path==>{path},请按下{key}以继续执行[${manifest.name} JS]`, settings.mode, "path", AUTO_STOP)
// log.info(`[{mode}] path==>{path},请按下{key}以继续执行[${manifest.name} JS]`, settings.mode, "path", AUTO_STOP)
// await keyMousePress(AUTO_STOP);
// log.info(`[{mode}] path==>{path},请按下{key}以继续执行[${manifest.name} JS]`, settings.mode, "path", AUTO_STOP)
}

View File

@@ -1,4 +1,25 @@
[
{
"name": "config_run",
"type": "select",
"label": "配置模式",
"options": [
"刷新","加载"
],
"default": "刷新"
},
{
"name": "config_white_list",
"type": "input-text",
"label": "刷新白名单 以,分割",
"default": "晶蝶"
},
{
"name": "config_black_list",
"type": "input-text",
"label": "刷新黑名单 以,分割",
"default": "其他,锄地专区,食材与炼金"
},
{
"name": "key",
"type": "input-text",
@@ -25,6 +46,12 @@
],
"default": "全自动"
},
{
"name": "choose_best",
"type": "checkbox",
"label": "择优模式",
"default": false
},
{
"name": "autoStop",
"type": "input-text",