我正在写一个抢购的脚本。脚本通过配置文件输入我们需要抢购的商品,运行脚本的时候会监控商品列表,商品列表如果出现我们需要的商品就会点击抢购按钮抢购,现在我遇到几个问题。
1、当商品列表没有我们需要的商品的时候会高速循环监控,监控一段时间后监控频率越来越低,最开始100ms可以完成一次循环,到后面需要1s甚至偶尔会停顿几s才完成下一次的监控。
2、抢购按钮可能是点击不会跳转到新页面的无任何响应的按钮,但是他的clickable和enabled和visibleToUser都是ture,唯一和正常抢购按钮不同的地方就是当它不能点击的时候颜色会稍微偏白一点点。频繁点击不可点击的按钮,会导致脚本进去无效的抢购步骤。
为什么解决这些问题应该怎么做?
DouyinBuyer.prototype.findAndClickProduct = function() {
// 1. 找到所有RecyclerView
var allRecyclerViews = className("androidx.recyclerview.widget.RecyclerView").find();
// 2. 必须保证页面上至少有两个列表,才能安全地选择第二个
if (allRecyclerViews.size() < 2) {
return false; // 不满足条件,直接返回,避免出错
}
// 3. 直接选择第二个RecyclerView作为商品列表
var productListView = allRecyclerViews.get(1);
if (!productListView) {
return false;
}
// 4. 遍历RecyclerView中的列表项(ListItem)
var listItems = productListView.children();
for (var i = 0; i < listItems.size(); i++) {
var listItem = listItems.get(i);
if (!listItem || listItem.childCount() === 0) continue;
// 5. 关键修正:深入一层,获取真正的商品卡片(ProductCard)
var productCard = listItem.child(0);
if (!productCard) continue;
// 6. 高效验证商品信息
var description = productCard.desc();
if (!description) {
continue;
}
// 7. 使用isTargetProduct进行统一判断
if (this.isTargetProduct(description)) {
// 8. 在已确认的目标卡片内,精准查找抢购按钮
var buyButton = productCard.findOne(textMatches(/(去抢购|领券抢购|立即抢购|领券购买|去购买)/));
if (buyButton && buyButton.clickable()) {
var title = description.split('\n')[0].replace(/\d+号商品/, ''); // 提取简短标题用于日志
this.ui.log("--> 目标锁定: [" + title + "],立即点击!");
//测试线注释先点击
buyButton.click();
sleep(50);
return true; // 点击成功,立即退出并返回true
}
}
}
return false; // 未找到目标商品
};
颜色判断应该怎么做才正确?
回答
抢购脚本优化建议
针对你遇到的抢购脚本问题,我提供以下解决方案:
1. 监控循环变慢问题
原因分析:可能是CPU占用过高或内存泄漏导致脚本执行变慢。
解决方案:
// 文档地址 https://docs.hamibot.com/
// 优化监控循环
function monitorItems() {
// 使用固定间隔循环而非递归
while (true) {
let startTime = new Date().getTime();
// 监控逻辑...
checkAvailableItems();
let elapsed = new Date().getTime() - startTime;
// 固定间隔,如果执行过快则等待
if (elapsed < 100) {
sleep(100 - elapsed);
}
}
}
// 添加更精准的延迟控制
function preciseSleep(ms) {
var start = new Date().getTime();
while (new Date().getTime() < start + ms) {
// 空循环
}
}
hamibot.exit();
2. 无效按钮点击问题
解决方案:
// 文档地址 https://docs.hamibot.com/
// 改进的抢购按钮点击逻辑
function tryPurchase(button) {
// 检查按钮颜色,判断是否真的可点击
var color = button.color();
if (isDisabledColor(color)) {
return false; // 颜色判断为不可点击
}
// 使用press而非click,更可控
if (button.bounds()) {
var bounds = button.bounds();
press(bounds.centerX(), bounds.centerY(), 50);
return true;
}
return false;
}
// 颜色判断函数示例
function isDisabledColor(color) {
// 根据你的实际情况调整阈值
var brightness = (color.r * 299 + color.g * 587 + color.b * 114) / 1000;
return brightness > 220; // 假设偏白时亮度较高
}
hamibot.exit();
其他建议:
- 可以考虑添加一个"重置"机制,当发现监控变慢时重新加载页面或重启脚本
- 对于抢购按钮,可以尝试先快速点击3-5次,而不是单次点击
- 添加日志系统,记录监控间隔和按钮状态变化,方便优化
- 考虑使用OCR识别商品名称而非UI控件查找,更可靠
这些改进应该能解决你的两个主要问题。记得在实际应用中测试和调整参数。
补充回答 1
// 文档地址 https://docs.hamibot.com/
// 优化的抢购脚本代码
DouyinBuyer.prototype.findAndClickProduct = function() {
try {
// 1. 增加超时检查
if (!this.checkTimeout()) {
return false;
}
// 2. 使用waitFor确保列表加载完成
var allRecyclerViews = className("RecyclerView").waitFor();
if (allRecyclerViews.size() < 2) {
this.ui.log("商品列表未完全加载");
return false;
}
// 3. 使用bounds检测可视区域
var screenHeight = device.height;
var productListView = allRecyclerViews.get(1);
if (!productListView || productListView.bounds().top > screenHeight * 0.8) {
this.ui.log("商品列表不在可视区域");
return false;
}
// 4. 优化遍历逻辑
var listItems = productListView.children();
for (var i = 0; i < Math.min(listItems.size(), 20); i++) { // 限制检查数量
var listItem = listItems.get(i);
if (!listItem) continue;
// 5. 增加滑动确保商品完整显示
if (i > 5 && i % 5 == 0) {
swipe(productListView.bounds().centerX(),
productListView.bounds().bottom - 100,
productListView.bounds().centerX(),
productListView.bounds().top + 100,
200);
sleep(300);
}
var productCard = listItem.child(0);
if (!productCard) continue;
// 6. 优化商品检查
var description = productCard.desc() || productCard.text();
if (!description || !this.isTargetProduct(description)) {
continue;
}
// 7. 改进按钮查找和点击
var buyButton = this.findBuyButton(productCard);
if (buyButton) {
var title = description.split('\n')[0].replace(/\d+号商品/, '');
this.ui.log("--> 目标锁定: [" + title + "]");
// 8. 改进点击方式
if (this.safeClick(buyButton)) {
return true;
}
}
}
} catch (e) {
this.ui.log("查找商品异常: " + e);
}
return false;
};
// 新增辅助方法
DouyinBuyer.prototype.findBuyButton = function(parent) {
var button = parent.findOne(textMatches(/(去抢购|领券抢购|立即抢购|领券购买|去购买)/));
if (!button || !button.clickable()) return null;
// 检查按钮颜色
var color = button.color();
if (this.isDisabledColor(color)) return null;
return button;
};
DouyinBuyer.prototype.safeClick = function(button) {
if (!button || !button.bounds()) return false;
// 使用坐标点击更可靠
var bounds = button.bounds();
click(bounds.centerX(), bounds.centerY());
sleep(50);
return true;
};
DouyinBuyer.prototype.isDisabledColor = function(color) {
if (!color) return false;
// 根据实际情况调整颜色判断逻辑
return (color.r + color.g + color.b) > 700; // 简单亮度判断
};
hamibot.exit();
主要改进点:
-
性能优化:
- 增加列表加载等待(waitFor)
- 限制检查数量避免无限循环
- 增加超时检查机制
-
可靠性改进:
- 检查列表是否在可视区域
- 定期滑动确保商品完整显示
- 改进商品描述获取方式(desc或text)
-
点击优化:
- 增加按钮颜色检查
- 使用坐标点击替代控件点击
- 将点击逻辑封装为独立方法
-
异常处理:
- 添加try-catch捕获异常
- 错误日志记录
-
代码结构:
- 拆分复杂逻辑为独立方法
- 增加注释说明关键逻辑
这些改进应该能解决你遇到的性能下降和无效点击问题,同时提高了脚本的可靠性。
补充回答 2
// 文档地址 https://docs.hamibot.com/
// 更精准的颜色判断方法
DouyinBuyer.prototype.isDisabledColor = function(obj) {
// 处理传入可能是控件或颜色对象的情况
var color = (obj && obj.color) ? obj.color() : obj;
if (!color || typeof color !== 'object') return false;
// 方法1:RGB亮度计算(权重法)
var brightness1 = (color.r * 299 + color.g * 587 + color.b * 114) / 1000;
// 方法2:HSL亮度计算(更符合人眼感知)
var max = Math.max(color.r, color.g, color.b);
var min = Math.min(color.r, color.g, color.b);
var lightness = (max + min) / 2 / 255;
// 方法3:HSV亮度值
var value = max / 255;
// 方法4:与参考颜色的Delta E距离(Lab色彩空间)
var referenceColor = {r: 255, g: 255, b: 255}; // 参考白色
var deltaE = this.calculateColorDistance(color, referenceColor);
// 综合判断(需根据实际按钮调整阈值)
if (brightness1 > 230 || lightness > 0.9 || value > 0.9 || deltaE < 15) {
return true; // 判定为不可点击状态
}
return false;
};
// 计算颜色距离(Delta E)
DouyinBuyer.prototype.calculateColorDistance = function(color1, color2) {
// 将RGB转换为Lab
var lab1 = this.rgbToLab(color1);
var lab2 = this.rgbToLab(color2);
// 计算Delta E
var deltaL = lab1.l - lab2.l;
var deltaA = lab1.a - lab2.a;
var deltaB = lab1.b - lab2.b;
return Math.sqrt(deltaL * deltaL + deltaA * deltaA + deltaB * deltaB);
};
// RGB转Lab的简化实现
DouyinBuyer.prototype.rgbToLab = function(rgb) {
// 简化实现,实际应用可能需要更精确的转换
var r = rgb.r / 255;
var g = rgb.g / 255;
var b = rgb.b / 255;
// 简单转换公式
return {
l: (0.4124 * r + 0.3576 * g + 0.1805 * b) * 100,
a: (-0.9689 * r + 1.8758 * g - 0.9065 * b) * 100,
b: (0.0557 * r - 0.2040 * g + 1.0570 * b) * 100
};
};
// 实际使用时可以这样调用
var button = id('buyButton').findOne();
if (button && !this.isDisabledColor(button)) {
this.safeClick(button);
}
hamibot.exit();
正确的颜色判断应该考虑以下关键点:
- 多维度判断:
- 同时使用RGB亮度、HSL亮度和HSV值进行综合判断
- 加入Delta E色差计算(Lab色彩空间)提高准确性
- 阈值调整:
- 初始阈值可以设为:亮度>230/255、HSL亮度>0.9、Delta E<15
- 建议通过截图工具获取实际按钮的禁用/启用状态颜色样本,然后调整这些阈值
- 颜色空间转换:
- 提供从RGB到Lab色彩空间的转换方法
- Lab色彩空间更接近人类视觉感知
-
实际应用建议:
-
在脚本开发阶段,先收集20-30个启用状态和禁用状态的按钮截图
-
分析这些样本的颜色特征来确定最佳阈值
-
添加调试日志输出颜色值,便于后期优化
-
考虑不同设备、不同主题可能导致的颜色差异
-
性能优化:
- 对于高频检测的场景,可以使用简化的亮度计算方法
- 只在亮度判断不确定时再进行更复杂的Delta E计算
注意:不同手机的屏幕色温、亮度设置可能会影响颜色值,因此在实际部署时可能需要根据目标设备进行微调。