开发日志25120809

date
Dec 9, 2025
slug
开发日志25120809
status
Published
tags
计算机
AI
summary
今天处理了若干BUG…
type
Post
今天处理一下 行为计数 功能
//==========总览
const root = 'LOG';
const today = new Date().toISOString().slice(0, 10); // YYYY-MM-DD
const path = `HOST/${today}.json`;

// 读取文件
let summary = {};
try {
    const resHost = await fetch(`/api/file/read?root=${encodeURIComponent(root)}&path=${encodeURIComponent(path)}`);
    if (resHost.ok || resHost.status === 404) {
        summary = await resHost.json();
        if (JSON.stringify(summary) === '{}') {
            summary = { summary: {}, devices: [] };
        }
    } else {
        throw new Error(`HTTP ${resHost.status}: ${resHost.statusText}`);
    }
} catch (error) {
    // 网络错误或其他异常,也使用空数组
    console.warn('获取文件失败,使用空数组:', error.message);
}

//总览
let summaryHost = summary.summary;
//找到对应项
let summaryDevice = summary.devices.find(item => item.deviceId === deviceId);

if (!summaryDevice) {
    summaryDevice = { deviceId }
    summary.devices.push(summaryDevice);
};

//如果返回脚本执行成功
let updparam = ['totalRuns'];//需要+1的参数
if (result) {
    updparam.push('successCount');
    if (returns.likeVideo) updparam.push('likeVideo');
    if (returns.commentVideo) updparam.push('commentVideo');
} else {
    updparam.push('failureCount');
}
updSummary(updparam, summaryHost, summaryDevice);

post(`/api/file/write`, {
    root,
    path,
    content: JSON.stringify(summary, null, 4)
});

function updSummary(keys, objs) {
    if (!keys || !objs) return;

    // 确保传入是数组(兼容字符串和数组两种传参方式)
    const keyList = Array.isArray(keys) ? keys : [keys];
    const objList = Array.isArray(objs) ? objs : [objs];

    //对每个对象的对应键值+1
    keyList.forEach(key => {
        objList.forEach(obj => {
            if (key in obj) {
                obj[key]++;//次数+1
            } else {
                obj[key] = 1;//置为1
            }
        });
    });
}
//====================执行结果记录
const root = 'SUMMARY';
const today = new Date().toISOString().slice(0, 10); // YYYY-MM-DD
const hostPath = `HOST/${today}.json`;
const devicePath = `设备${deviceNo} - ${deviceId}/${today}.json`;

// 读取文件
let summary = {};
let record = {};

//执行总数记录
try {
    const resHost = await fetch(`/api/file/read?root=${encodeURIComponent(root)}&path=${encodeURIComponent(hostPath)}`);
    if (resHost.ok) {
        summary = await resHost.json();
        if (Object.keys(summary).length === 0) {
            summary = { summary: {}, devices: [] };
        }
    } else if (resHost.status === 404) {
        summary = { summary: {}, devices: [] };
    } else {
        throw new Error(`HTTP ${resHost.status}: ${resHost.statusText}`);
    }
} catch (error) {
    // 网络错误或其他异常,也使用空数组
    console.warn('获取文件失败,使用空数组:', error.message);
}

//单台设备详细记录
try {
    const resDevice = await fetch(`/api/file/read?root=${encodeURIComponent(root)}&path=${encodeURIComponent(devicePath)}`);
    if (resDevice.ok) {
        record = await resDevice.json();
        if (Object.keys(record).length === 0) {
            record = { summary: {}, runs: [] };//若空则初始化
        }
    } else if (resDevice.status === 404) {
        record = { summary: {}, runs: [] };//若空则初始化
    } else {
        throw new Error(`HTTP ${resDevice.status}: ${resDevice.statusText}`);
    }
} catch (error) {
    // 网络错误或其他异常,也使用空数组
    console.warn('获取文件失败,使用空数组:', error.message);
}

//执行总数记录
let summaryHost = summary.summary;
//找到设备对应对象
let summaryDevice = summary.devices.find(item => item.deviceId === deviceId);
//为空时push到数组中
if (!summaryDevice) {
    summaryDevice = { deviceId }
    summary.devices.push(summaryDevice);
};

//设备执行记录
let recordSummary = record.summary;

//如果返回脚本执行成功
let keys = ['totalRuns'];//总运行次数+1
let updObj = [summaryHost, summaryDevice, recordSummary]
//...         所有设备总计数  单设备计数     单设备详细记录
if (result) {
    //更新到summary
    keys.push('successCount');//如果成功运行+1
} else {
    keys.push('failureCount');//如果失败运行+1
}
updSummary(keys, updObj);

//分平台结果分别储存
keys = [];
summaryDevice[platform] = summaryDevice[platform] || {};
recordSummary[platform] = recordSummary[platform] || {};
updObj = [summaryDevice[platform], recordSummary[platform]];
//后期可能需要自动判断
if (returns.likeVideo) keys.push('likeVideo');//如果点赞了+1
if (returns.myComment) keys.push('commentVideo');//如果评论了+1
updSummary(keys, updObj);
//固定运行记录
let currentRecord = {
    task: task.name,
    platform,
    success: result,
    returns
}
//push到数组
record.runs.push(currentRecord);

//写入总记录
post(`/api/file/write`, {
    root,
    path: hostPath,
    content: JSON.stringify(summary, null, 4)
});
//写入详细记录
post(`/api/file/write`, {
    root,
    path: devicePath,
    content: JSON.stringify(record, null, 4)
});

优化暂停方法 现在可以强制停止脚本执行
runTaskChain() 函数中添加将任务放进数组的方法
let taskRunning = {};
if (!window.taskRunning.length) {
    window.taskRunning.push({ deviceId, task: task.script })
} else {
    taskRunning = window.taskRunning.find(item => item.deviceId === deviceId);//指示当前运行脚本的对象
}
taskRunning.task = task.script;
main.js 中添加对应方法执行暂停
logInfo.w('强制停止所有任务');
const allDevices = window.taskRunning.map(item => item.deviceId);
const uniqueDevices = [...new Set(allDevices)]
const allTasks = window.taskRunning.map(item => item.task);
// 使用 Set 去除重复值,然后展开为数组
const uniqueTasks = [...new Set(allTasks)];

uniqueTasks.forEach(item => {
    send({
        "action": "StopAutoJs",
        "comm": {
            "deviceIds": uniqueDevices.join(','),
            "filePath": item
        }
    });
});
});

用AI写了一个读取行为记录的模块 record.html

之前用比较蠢的方法实现空对象判断
JSON.stringify(json) === '{}'
后改用更好的
Object.keys(json).length === 0 

之前使用的 switch() 判断:
switch (true) {
            case author === null:
                logInfo.e(`找不到作者`);
                returns.likeVideo = false;
                break;
            case author === 'isLive':
                logInfo.w(`当前视频为直播间,跳过本次操作`);
                returns.likeVideo = false;
                break;
            default:
                logInfo.s(`当前视频作者: ${author},视频简介: ${desc}`);
        }
现发现确实并不好用 改回 if()
if (author === 'isLive') {
            logInfo.w(`当前视频为直播间,跳过本次操作`);
            returns.likeVideo = false;
        } else if (!author) {
            logInfo.e(`找不到作者`);
            returns.likeVideo = false;
        } else {
            logInfo.s(`当前视频作者: ${author},视频简介: ${desc}`);
}

之前使用的过滤屏幕外对象函数可靠性比较低 很大概率把我需要的东西过滤掉 因为传入的参数 maxTop maxBottom 有可能不满足要求 但是我不可能对每次过滤都测试太多次 进行了修改 现会先过滤一次 异常元素 例如元素的 .top.bottom 大( 较大的值在下方 ) 若过滤结果只剩一个则直接返回 若还剩下多个则按传入参数二次过滤:
function filterOnScreen(element, maxTop, maxBottom) {
    try {
        if (element) {
            // logInfo.d('element处理前\n' + element.join('\n'));
            maxBottom = maxBottom || height - 500;//默认最低屏幕高度-500px
            maxTop = maxTop || 100;//默认最高从上往下100px
            element = element.filter(obj => {
                let bounds = obj.bounds();
                let top = bounds.top;
                let bottom = bounds.bottom;
                return bottom > top;//直接过滤底部比顶部高的对象
            });
            if (element.length === 1) {
                logInfo.s('只剩下一个结果,过滤成功');
                return element;
            } else {
                logInfo.w('似乎剩下不止一个结果 正在尝试严格过滤');
                element = element.filter(obj => {
                    let bounds = obj.bounds();
                    logInfo.i(bounds.join());
                    let top = bounds.top;
                    return top > maxTop && top < maxBottom;//过滤不满足传入条件者
                });
            }

            logInfo.d('element处理后\n' + element.join('\n'));
            return element || [];
        }
    } catch (e) {
        logInfo.e(e);
    }
}

之前使用的查询 #title 方法只进行两次 而且代码冗长 现进行了调整 可以查询五次 增强可靠性
//寻找ID
let isFound = false;
let time = 0;
let candidates;
while (!isFound && time < 5) {
    candidates = id('title').textContains('@').find();
    logInfo.d(`${time}次查询candidates:\n + ${candidates.join('\n')}`);
    if (!candidates.empty()) {
        //若不为空则过滤屏幕外对象
        candidates = filterOnScreen(candidates, height / 2, height);
        //取第一个
        if (candidates.length > 0) {
            authorEle = candidates[0]
            author = authorEle.text();
            descEle = findDesc(authorEle);
            if (descEle) desc = descEle.text();
            isFound = true;
        }
    };
    time++;
}
if (!author) return { author: null, desc: null };
return { author, desc };

现在的暂停按钮功能单一 只能 全部暂停全部强制停止 现添加函数可以暂停特定设备
合并了window.isPaused 和window.taskRunning 到window.taskStatus 通过一个全局数组控制任务的进行
判断是否暂停
while (true) {
    // 每次都重新查找最新的状态
    const currentData = window.taskStatus.find(item => item.deviceId === deviceId);

    if (!currentData || currentData.status !== "paused") {
        break; // 设备不存在或状态不是暂停,退出循环
    }

    await sleep(1000); // 等待1秒后再次检查
}
更新任务列表方法 现在都是针对设备进行调控 不会因为不断push导致数组巨大重复数量又太多
//在数组中找到设备
const found = window.taskStatus.find(item => item.deviceId === deviceId);
if (found) {//如果找到设备 则替换任务
    found.task = task.script;
} else {
    //如果没找到 则push到数组
    window.taskStatus.push({ deviceId, task: task.script, status: 'running' });
}
修改了继续任务的条件 可以在判断onComplete的同时判断任务是否被停止
const deviceStatus = window.taskStatus.find(item => item.deviceId === deviceId).status;
const onComplete = task.onComplete ? task.onComplete(result, deviceLog) : (result);
const shouldContinue = deviceStatus && onComplete;
重构了暂停和停止的方法 有点容易晕 明天可能要再调试一下
//查询当前设备的运行状态
const thisStatus = window.taskStatus.find(item => item.deviceId === deviceId);
//获取按钮元素
const btn = document.getElementById('pause_' + deviceId);
if (thisStatus) {//如果不为空
    if (thisStatus.status === 'running') {//值为running
        thisStatus.status = 'paused';
        btn.textContent = '恢复';
        btn.style.backgroundColor = '#e74c3c';
        logInfo.w(`设备${deviceNo}已暂停`);
    } else if (thisStatus.status === 'paused') {//值为paused
        thisStatus.status = 'running';
        btn.textContent = '暂停';
        btn.style.backgroundColor = '';
        logInfo.i(`设备${deviceNo}已恢复`);
    }
} else {
    console.log('不知道什么情况 反正找不到这台设备的运行状态');
}
});
stop.addEventListener('click', () => {
    const thisStatus = window.taskStatus.find(item => item.deviceId == deviceId);
    let taskPath = '';
    if (thisStatus) {
        taskPath = thisStatus.task;
        send({
            "action": "StopAutoJs",
            "comm": {
                "deviceIds": deviceId,
                "filePath": taskPath
            }
        });
        logInfo.w(`强制停止设备 ${deviceNo} 任务`);
        thisStatus.status = 'stopped';
    } else {
        logInfo.e(`找不到设备 ${deviceNo} 执行的任务`)
    }
});
//查询所有状态
const statusMap = taskStatus.map(item => item.status);
if (!statusMap.includes("paused") || btn.textContent === '暂停所有任务') {//如果不含paused 则现在状态为全部运行状态 暂停所有任务
    window.taskStatus.forEach(task => {//对任务状态表里的每个项
        if (task.status === 'running') {//如果状态为暂停
            const deviceId = task.deviceId;
            const btn = document.getElementById('pause_' + deviceId);//找到按钮
            task.status = 'paused';//修改为运行
            btn.textContent = '恢复';//列表按钮改为暂停
            btn.style.backgroundColor = '#e74c3c';//列表按钮背景清理
        }
    });
    btn.textContent = '恢复所有任务';
    btn.style.backgroundColor = '#e74c3c';
    logInfo.w('所有设备已暂停');
} else if (!statusMap.includes("running") || btn.textContent === '恢复所有任务') {//如果不含running 则现在状态为全部暂停状态 恢复所有任务
    window.taskStatus.forEach(task => {//对任务状态表里的每个项
        if (task.status === 'paused') {//如果状态为暂停
            const deviceId = task.deviceId;
            const btn = document.getElementById('pause_' + deviceId);//找到按钮
            task.status = 'running';//修改为运行
            btn.textContent = '暂停';//列表按钮改为暂停
            btn.style.backgroundColor = '';//列表按钮背景清理
        }
    });
    btn.textContent = '暂停所有任务';
    btn.style.backgroundColor = '';
    logInfo.w('所有设备已恢复');
}
结束的时候发现 全局停止的功能似乎不可靠
@TODO:修复全局停止的功能

需要处理广告跳过了 已经dump下了页面的结构 就是又臭又长需要分析一下
notion image
notion image
notion image
notion image
 
notion image
大概就这几种广告 明天处理

© Dominic Hodpel 2022 - 2026