场景说明
我们开发了一套守护大脚本,需要实现:APP 写入指令文件 → 大脚本检测指令 → 自动拉起 / 终止另一套业务子脚本,核心依赖 scripts.run() / scripts.stop() 接口调度子脚本。
已测试全部方案及对应故障
尝试方案 1:使用 engines.myEngine().on() 事件监听执行脚本调度
问题:运行直接报错,提示引擎实例不存在 .on() 方法,脚本强制退出。
尝试方案 2:新建独立 setInterval 定时器,定时器内部调用 scripts.run()
问题:定时器上下文内调用 scripts 接口,抛出 ReferenceError: "scripts" 未定义,脚本直接结束。
尝试方案 3:通过 threads.start() 新建独立子线程,子线程循环检测标记并调用 scripts
问题:所有 threads 创建的子线程环境均无法识别 scripts 对象,同样报 scripts 未定义 闪退。
尝试方案 4:脚本最末尾放置主线程同步 while(true) 死循环轮询标记,主线程原生上下文调用 scripts
问题:主线程无限死循环会独占调度资源,阻塞全部后台 threads 子线程(指令文件轮询、心跳、自动弹窗点击等后台循环全部停止,无法执行业务)。
折中留存方案:将脚本启停逻辑嵌入顶层全局心跳 setInterval(仅此上下文可正常调用 scripts 无报错)
现存缺陷:心跳固定 60 秒执行一次,APP 下发启停指令后最长需要等待 1 分钟才能执行调度,实时性完全不满足业务需求。
核心底层疑问
Hamibot 是否存在上下文隔离限制:仅脚本顶层同步代码、顶层初始化创建的 setInterval 能访问 scripts API,子线程 / 新建定时器均无法访问?
有无官方支持的、可高频实时轮询全局标记、同时稳定调用 scripts.run()/scripts.stop() 的实现方式?
如果必须依托顶层定时器,是否支持自定义高频短间隔定时器(1 秒轮询),不会出现上下文丢失、scripts 未定义报错?
主线程无限 while 死循环抢占调度阻塞全部后台 threads 子线程,是否属于底层调度机制限制,有无规避写法?

HAMI_6893877807 · 12 小时前
第 1 条补充
·
3 小时前

现象
脚本 ID:PSc4h,代码语法完整,手机本地手动打开运行完全正常,无任何语法报错;
通过守护脚本 engines.execScript("PSc4h", 参数) 远程拉起该云端脚本时,直接报错:语句前缺少 ";" . (PSc4h.js#1);
守护脚本日志显示已【成功】发起启动指令,仅子脚本加载解析时报第一行语法错误;
同一套完整源码,本地手动运行正常、云端远程调用报错,怀疑云端存储的脚本文件存在字符截断 / 隐藏非法空白字符异常。
已做排查
核对完整源码,无缺分号、缺括号、语法错误;
重新清空云端脚本,完整粘贴源码重新发布审核;
手机端更新同步最新云端脚本,问题依旧复现;
调用逻辑使用官方标准 execScript 传参写法,无自定义特殊逻辑。
咨询诉求
云端发布后的脚本文件是否会自动插入不可见控制字符,导致第一行解析语法报错?
为什么本地缓存脚本可正常执行,远程拉取云端版本就报首行语法错误?
该问题有无官方修复方案 / 上传代码规范规避手段?

回答

Hamibot AI
1
· 12 小时前

你的问题核心源于 API 模块名称误用JavaScript 事件循环机制 的冲突。下面逐一解答你的底层疑问,并提供符合 Hamibot 运行环境的稳定调度方案。

一、 核心问题定位

Hamibot 中 不存在 scripts 模块。你遇到的 ReferenceError: "scripts" 未定义 并非上下文隔离限制,而是调用了未声明的全局对象。Hamibot 官方用于脚本调度、引擎管理的模块是 engines。将代码中的 scripts.run() / scripts.stop() 替换为 engines.execScriptFile()execution.getEngine().forceStop() 后,方案 2 和方案 3 的报错会直接消失。

二、 针对底层疑问的解答

1. 是否存在上下文隔离限制?
不存在。engineseventsthreads 等核心模块在 Hamibot 的顶层作用域、setInterval 回调、threads 子线程中均可直接访问。你遇到的“仅顶层定时器能访问”是 scripts 拼写错误导致的假象。

2. 有无官方支持的高频实时轮询 + 稳定调度方式?
有。推荐使用 主线程 setInterval 短间隔轮询events 模块事件驱动。Hamibot 基于 Rhino 引擎,主线程的 setInterval 回调由事件循环统一调度,不会丢失上下文,且能稳定调用 engines API。

3. 是否支持自定义高频短间隔定时器(1 秒轮询)?
完全支持。setInterval(function() {}, 1000) 在 Hamibot 中运行稳定,不会出现上下文丢失或 API 未定义。只要避免在回调内执行耗时同步操作(如大文件读写、长循环),1 秒轮询对性能影响极小。

4. 主线程 while(true) 阻塞后台线程是否属底层限制?有无规避写法?
属于 JavaScript 单线程事件循环的标准机制。主线程同步死循环会持续占用执行栈,导致事件队列中的定时器回调、线程消息、UI 事件永远无法出队执行。规避方法:绝对不要在主线程使用同步 while(true),改用 setIntervalevents.waitFor() 异步模型。


三、 官方推荐的最佳实践代码

以下代码完全兼容 Hamibot 的 Rhino 1.7.15 引擎,采用 1 秒高频轮询指令文件,并妥善管理子脚本生命周期。

// 文档地址 https://docs.hamibot.com/

// 记录子脚本执行实例,用于精准停止
var subExecutionMap = {};

// 模拟读取指令文件,需替换为你的实际文件读取逻辑
function readCommandFile() {
    // 示例:返回 'start' | 'stop' | 'none'
    // 实际可使用 files.read() 或 open() 读取 APP 写入的指令文件
    return 'none'; 
}

function pollAndSchedule() {
    var cmd = readCommandFile();
    
    if (cmd === 'start') {
        if (!subExecutionMap['business']) {
            // 拉起业务子脚本
            subExecutionMap['business'] = engines.execScriptFile('./business.js');
            log('守护脚本:已拉起业务子脚本');
        }
    } else if (cmd === 'stop') {
        if (subExecutionMap['business']) {
            // 终止业务子脚本
            subExecutionMap['business'].getEngine().forceStop();
            delete subExecutionMap['business'];
            log('守护脚本:已终止业务子脚本');
        }
    }
}

// 1秒高频轮询,由事件循环调度,不阻塞其他线程
setInterval(function() {
    pollAndSchedule();
}, 1000);

// 保持守护脚本常驻运行
setInterval(function() {}, 60000);

四、 关键注意事项

  1. 引擎兼容性:Hamibot 使用 Rhino 1.7.15,不支持箭头函数、默认参数、Promise 等 ES6+ 语法。所有回调必须使用 function() {} 声明。
  2. 停止脚本的正确姿势engines.execScriptFile() 返回的是 ScriptExecution 对象。停止脚本必须调用 execution.getEngine().forceStop(),直接操作引擎实例会导致状态不一致。
  3. 文件读写性能:高频轮询指令文件时,确保文件读取逻辑轻量。若文件较大或路径在外部存储,建议将读取操作放入 threads.start() 子线程,通过 threads 的消息机制或全局变量将指令传递给主线程定时器执行 engines 调度,避免 I/O 阻塞事件循环。
  4. 更优的通信方案:如果 APP 与 Hamibot 脚本运行在同一设备,强烈建议放弃文件轮询,改用 events 模块配合广播或 intent。APP 发送广播,Hamibot 使用 events.onBroadcast()events.on() 监听,可实现毫秒级响应且零轮询开销。

按照上述方案替换 scriptsengines,并移除主线程同步死循环,你的守护脚本即可实现高频、实时、稳定的子脚本调度。如有具体文件读取或跨进程通信的实现细节需要优化,可提供代码片段,我会进一步给出针对性调整建议。

ℹ️ 内容由 AI 生成,可能不准确,请注意核实
Hamibot AI
1
· 3 小时前

该现象是 Hamibot / Auto.js 生态中非常典型的 UTF-8 BOM(字节顺序标记)或隐藏控制字符 引发的引擎解析故障。下面直接给出底层原理与标准修复方案。

一、 核心原因定位

报错 语句前缺少 ";" . (PSc4h.js#1) 并非真正的语法缺失,而是 Rhino 1.7.15 引擎在解析文件首字节时,遇到了无法识别的 BOM 字符(\uFEFF 或其他不可见格式字符。引擎将其当作非法 Token,直接抛出首行语法错误。

二、 针对你的诉求逐一解答

1. 云端发布是否会自动插入不可见字符?
不会。云端存储与同步服务是二进制透传的,不会修改源码内容。该字符来源于你本地编辑器的保存编码设置(如 Windows 记事本、部分 IDE 默认保存为 UTF-8 with BOM),或在网页/文档中复制代码时混入了零宽空格、软换行等隐藏字符。云端同步会原样保留这些字节。

2. 为什么本地手动运行正常,云端远程调用报错?

  • 本地手动运行:Hamibot 客户端内置编辑器在加载本地脚本时,会执行一层预处理,自动过滤 BOM 及非法控制字符,因此 Rhino 引擎拿到的是干净代码,运行正常。
  • 云端同步调用:云端脚本下载时以原始字节流直接写入本地缓存目录。守护脚本通过引擎接口加载该文件时,绕过了编辑器的预处理环节,Rhino 直接读取原始文件,首字节的 BOM 立即触发解析崩溃。

3. 有无官方修复方案 / 上传规范?
有。需从 文件编码清洗API 调用规范 两方面彻底解决。


三、 标准修复步骤

步骤 1:彻底清除 BOM 与隐藏字符

  1. 使用 VS Code、Notepad++ 或 Sublime Text 打开源码。
  2. 将文件编码强制切换为 UTF-8(无 BOM)
    • VS Code:右下角点击 UTF-8 with BOM通过编码保存 → 选择 UTF-8
    • Notepad++:菜单栏 编码转为 UTF-8 无 BOM 编码
  3. 全选代码,粘贴到纯文本环境(如系统记事本)中转一次,清除零宽字符,再重新粘贴发布。
  4. 重新提交云端审核,并在手机端执行「同步脚本」覆盖旧缓存。

步骤 2:修正守护脚本的调用方式

engines.execScript(name, script) 的第二个参数必须是 JS 源码字符串,不能直接传脚本 ID 或名称。直接传 ID 会导致引擎将 ID 字符串当作代码解析,或触发非预期行为。

调用已同步的云端脚本,必须使用 engines.execScriptFile(本地绝对路径)。云端脚本同步后的默认存储路径为:
/sdcard/Android/data/com.hamibot.hamibot/files/scripts/脚本ID.js

步骤 3:标准调度代码示例

// 文档地址 https://docs.hamibot.com/

var scriptId = 'PSc4h';
// 拼接云端脚本同步后的本地绝对路径
var scriptPath = files.join('/sdcard/Android/data/com.hamibot.hamibot/files/scripts', scriptId + '.js');

function startCloudScript(args) {
    if (!files.exists(scriptPath)) {
        toast('脚本未同步或路径不存在');
        return null;
    }
    // 使用 execScriptFile 加载本地文件,避免字符串解析问题
    var config = {
        arguments: args || {}
    };
    return engines.execScriptFile(scriptPath, config);
}

// 示例:拉起子脚本并传参
var execution = startCloudScript({ action: 'run', taskId: 1001 });
if (execution) {
    log('守护脚本:成功拉起 ' + scriptId);
}

四、 关键注意事项

  1. Rhino 引擎限制:Hamibot 使用的 Rhino 1.7.15 对文件首部字符极度敏感。任何非标准 ASCII/UTF-8 字符出现在第一行都会导致 Missing ; before statement。务必保证源码为纯 UTF-8 无 BOM
  2. 路径兼容性:不同 Android 版本或 Hamibot 版本的数据目录可能略有差异。若上述路径不存在,可通过 context.getExternalFilesDir(null).getAbsolutePath() 动态获取 Hamibot 私有目录,再拼接 /scripts/
  3. 参数传递规范engines.execScriptFile()config.arguments 会注入到子脚本的 engines.myEngine().execArgv 中。子脚本需通过 var args = engines.myEngine().execArgv; 读取,不要依赖全局变量跨脚本共享。
  4. 避免字符串拼接执行:不要使用 engines.execScript() 拼接云端脚本内容执行。文件 I/O 加载由底层 C++ 桥接处理,性能与稳定性远高于字符串 eval 模式。

按照 UTF-8 无 BOM 重存 + execScriptFile 绝对路径调用 组合处理,首行语法报错将彻底消失。若同步后路径仍无法定位,可提供手机端 Hamibot 版本与 Android 版本,我会给出动态路径探测代码。

ℹ️ 内容由 AI 生成,可能不准确,请注意核实