|
|
资讯缩略图:
资讯发布日期:2026-08-03
资讯发布简介:无碍模式auto淘宝千牛自动发货系统
资讯关键词:无碍模式auto淘宝千牛自动发货系统
资讯所属分类:IT资讯
联系:
① 本信息收集于网络,如有不对的地方欢迎联系我纠正! ② 本信息免费收录,不存在价格的问题! ③ 如果您的网站也想这样出现在这里,请您加好友情链接,我当天会审核通过!
④友情链接关键字:软件网站分类目录 网址:http://www.postbbs.com/
资讯详细描述
 | |  |  | 无碍模式auto淘宝千牛自动发货系统
- // =============================================================================
- // 淘宝千牛自动发货系统 v3.0(精简版)
- // 仅支持无碍模式(auto),使用完整商品标题精准匹配
- // 功能:24小时监控千牛待发货列表 → 匹配商品标题 → 发送卡密/网盘链接
- // 适用:AIWROK 安卓自动化平台,ES5/Rhino 引擎
- // =============================================================================
- importClass(java.io.File);
- importClass(java.io.FileWriter);
- importClass(java.io.BufferedReader);
- importClass(java.io.InputStreamReader);
- importClass(java.io.FileInputStream);
- importClass(java.io.FileOutputStream);
- importClass(java.io.OutputStreamWriter);
- // =============================================================================
- // 第一部分:全局配置
- // =============================================================================
- var CONFIG = {
- 千牛包名: "com.taobao.qianniu",
- 轮询间隔: 30, // 检查新订单间隔(秒)
- 最大重试: 3,
- 操作延时: 2, // 每步操作后等待(秒)
- // 卡密服务器API(留空则用本地文件)
- 服务器地址: "",
- 服务器密钥: "",
- 使用服务器: false,
- 卡密目录: "/sdcard/卡密仓库/",
- 发货记录文件: "/sdcard/发货记录.txt",
- 启用保活: true,
- 保活间隔: 300,
- 启用悬浮窗: true,
- // debug=只输出节点摘要不发货; test=处理一个真实订单测试; false=正常监控
- 调试模式: "debug",
- };
- // =============================================================================
- // 第二部分:商品配置(用完整商品标题,精准匹配)
- // =============================================================================
- /**
- * 配置说明:
- * 商品标题: 淘宝商品的完整标题,用于在订单列表中精准匹配
- * 发货类型: "卡密" / "网盘" / "卡密+网盘"
- * 卡密文件: 本地卡密文件路径(每行一个卡密,用过的自动删除)
- * 网盘链接: 该商品对应的网盘链接
- * 发货话术: 发送给买家的话术,{卡密}和{网盘}自动替换
- */
- var 商品配置 = {
- "二年级数学必刷题上册人教版口算题卡天天练": {
- 发货类型: "卡密",
- 卡密文件: "/sdcard/卡密仓库/二年级数学必刷题.txt",
- 网盘链接: "",
- 发货话术: "您好,您购买的【二年级数学必刷题】已发货!\n卡密:{卡密}\n请打开APP输入卡密激活使用,有问题随时联系~"
- },
- "小学单位换算练习元角分长度单位专项训练": {
- 发货类型: "网盘",
- 卡密文件: "",
- 网盘链接: "https://pan.baidu.com/s/1aBcDeFgH 提取码: x1y2",
- 发货话术: "您好,您购买的【小学单位换算练习】已发货!\n网盘下载链接:{网盘}\n请复制链接到浏览器下载,有问题随时联系~"
- },
- "一年级加减口算练习册10以内20以内凑十法": {
- 发货类型: "卡密+网盘",
- 卡密文件: "/sdcard/卡密仓库/一年级加减口算.txt",
- 网盘链接: "https://pan.baidu.com/s/2bCdEfGhI 提取码: a3b4",
- 发货话术: "您好,您购买的【一年级加减口算】已发货!\n激活卡密:{卡密}\n网盘链接:{网盘}\n请先下载APP,输入卡密激活使用~"
- }
- // 继续添加商品...把完整的淘宝商品标题填进来即可
- };
- // =============================================================================
- // 第三部分:无碍模式操作封装(仅auto,无模式分支)
- // =============================================================================
- var ActionAdapter = {
- // ================================================================
- // 基础操作(直接调用 auto 对象)
- // ================================================================
- init: function() {
- if (typeof auto !== 'undefined') {
- logger.log("无碍模式已就绪");
- return true;
- }
- logger.err("auto对象不存在,请开启无障碍服务");
- return false;
- },
- click: function(x, y) {
- try {
- auto.clickPoint(x, y);
- return true;
- } catch (e) {
- logger.err("点击失败: " + e);
- return false;
- }
- },
- clickPercent: function(px, py) {
- try {
- auto.clickPercent(px, py);
- return true;
- } catch (e) {
- logger.err("百分比点击失败: " + e);
- return false;
- }
- },
- swipe: function(x1, y1, x2, y2, duration) {
- duration = duration || 500;
- try {
- auto.swip(x1, y1, x2, y2, duration, 0);
- return true;
- } catch (e) {
- logger.err("滑动失败: " + e);
- return false;
- }
- },
- swipePercent: function(px1, py1, px2, py2, duration) {
- duration = duration || 500;
- var w = screen.getScreenWidth();
- var h = screen.getScreenHeight();
- return this.swipe(
- Math.floor(px1 * w), Math.floor(py1 * h),
- Math.floor(px2 * w), Math.floor(py2 * h), duration
- );
- },
- back: function() {
- try { auto.back(); return true; } catch (e) { return false; }
- },
- home: function() {
- try { auto.home(); return true; } catch (e) { return false; }
- },
- // ================================================================
- // 核心:auto.getXml() 节点解析
- // 千牛很多按钮文字在 desc 属性里,必须同时匹配 text 和 desc
- // ================================================================
- getXml: function() {
- try {
- if (typeof auto !== 'undefined' && typeof auto.getXml === 'function') {
- return auto.getXml();
- }
- } catch (e) {
- logger.err("getXml失败: " + e);
- }
- return "";
- },
- /**
- * 从XML中查找包含指定文字的节点(同时匹配 text 和 desc)
- */
- findNodes: function(xml, keyword) {
- var results = [];
- if (!xml || !keyword) return results;
- var pattern = /<node\s+([^>]+?)(?:\/>|>)/g;
- var match;
- while ((match = pattern.exec(xml)) !== null) {
- var attrs = match[1];
- var textVal = "";
- var descVal = "";
- var tm = attrs.match(/text="([^"]*)"/);
- if (tm) textVal = tm[1];
- var dm = attrs.match(/desc="([^"]*)"/);
- if (dm) descVal = dm[1];
- if (textVal.indexOf(keyword) !== -1 || descVal.indexOf(keyword) !== -1) {
- results.push(this._parseNode(attrs, textVal, descVal));
- }
- }
- return results;
- },
- /**
- * 解析XML中所有节点(不过滤关键词,用于调试和父节点查找)
- */
- findAllNodes: function(xml) {
- var results = [];
- if (!xml) return results;
- var pattern = /<node\s+([^>]+?)(?:\/>|>)/g;
- var match;
- while ((match = pattern.exec(xml)) !== null) {
- var attrs = match[1];
- var tm = attrs.match(/text="([^"]*)"/);
- var dm = attrs.match(/desc="([^"]*)"/);
- results.push(this._parseNode(attrs, tm ? tm[1] : "", dm ? dm[1] : ""));
- }
- return results;
- },
- /**
- * 解析单个节点属性(内部方法)
- */
- _parseNode: function(attrs, textVal, descVal) {
- var cpm = attrs.match(/centerPercent="\[([0-9.]+),([0-9.]+)\]"/);
- var cm = attrs.match(/center="\[(\d+),(\d+)\]"/);
- var clm = attrs.match(/clickable="([^"]*)"/);
- var idm = attrs.match(/id="([^"]*)"/);
- var bm = attrs.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
- var clsm = attrs.match(/class="([^"]*)"/);
- return {
- text: textVal,
- desc: descVal,
- x: cm ? parseInt(cm[1]) : 0,
- y: cm ? parseInt(cm[2]) : 0,
- px: cpm ? parseFloat(cpm[1]) : 0,
- py: cpm ? parseFloat(cpm[2]) : 0,
- clickable: clm ? clm[1] : "false",
- id: idm ? idm[1] : "",
- bounds: bm ? [parseInt(bm[1]), parseInt(bm[2]), parseInt(bm[3]), parseInt(bm[4])] : null,
- class: clsm ? clsm[1] : ""
- };
- },
- /**
- * 查找不可点击节点的最近可点击父节点
- */
- findClickableParent: function(node, xml) {
- if (!node || !node.bounds) return null;
- var allNodes = this.findAllNodes(xml);
- var nodeArea = (node.bounds[2] - node.bounds[0]) * (node.bounds[3] - node.bounds[1]);
- var bestParent = null;
- var bestArea = 999999999;
- for (var i = 0; i < allNodes.length; i++) {
- var p = allNodes[i];
- if (p.clickable !== "true" || !p.bounds) continue;
- // 父节点必须包含目标节点
- if (p.bounds[0] <= node.bounds[0] && p.bounds[1] <= node.bounds[1] &&
- p.bounds[2] >= node.bounds[2] && p.bounds[3] >= node.bounds[3]) {
- var pArea = (p.bounds[2] - p.bounds[0]) * (p.bounds[3] - p.bounds[1]);
- // 选面积最小的可点击父节点(最近的)
- if (pArea < bestArea && pArea >= nodeArea) {
- bestArea = pArea;
- bestParent = p;
- }
- }
- }
- return bestParent;
- },
- /**
- * 找文字并点击(核心方法)
- * 1. 查找包含文字的节点
- * 2. 优先点击 clickable=true 的节点
- * 3. 不可点击的找父节点点击
- */
- findTextAndClick: function(text, timeout) {
- timeout = timeout || 3000;
- var startTime = new Date().getTime();
- while (new Date().getTime() - startTime < timeout) {
- var xml = this.getXml();
- if (!xml) { sleep.millisecond(毫秒 = 300); continue; }
- var result = this._findBestNode(xml, text);
- if (result) {
- if (result.clickable === "true") {
- this.clickPercent(result.px, result.py);
- } else {
- // 不可点击 → 找最近可点击父节点
- var parent = this.findClickableParent(result, xml);
- if (parent) {
- logger.log("节点不可点击,使用父节点: [" + result.text + "|" + result.desc + "] -> px=" + parent.px + " py=" + parent.py);
- this.clickPercent(parent.px, parent.py);
- } else {
- this.clickPercent(result.px, result.py);
- }
- }
- sleep.millisecond(毫秒 = 500);
- logger.log("找到并点击: " + text + " (text=" + result.text + " desc=" + result.desc + ")", true);
- return true;
- }
- sleep.millisecond(毫秒 = 300);
- }
- logger.warn("未找到文字: " + text);
- return false;
- },
- /**
- * 查找最佳节点(优先可点击的)
- */
- _findBestNode: function(xml, text) {
- var nodes = this.findNodes(xml, text);
- if (nodes.length === 0) return null;
- // 优先返回 clickable=true
- for (var i = 0; i < nodes.length; i++) {
- if (nodes[i].clickable === "true") return nodes[i];
- }
- return nodes[0];
- },
- /**
- * 找文字(返回节点信息,不点击)
- */
- findText: function(text) {
- var xml = this.getXml();
- if (!xml) return null;
- return this._findBestNode(xml, text);
- },
- hasText: function(text) {
- return this.findText(text) !== null;
- },
- hasAnyText: function(texts) {
- for (var i = 0; i < texts.length; i++) {
- if (this.findText(texts[i])) return true;
- }
- return false;
- },
- /**
- * 输出当前页面所有有意义节点的摘要(调试用)
- */
- dumpPageSummary: function() {
- var xml = this.getXml();
- if (!xml) { logger.warn("无法获取页面XML"); return; }
- var nodes = this.findAllNodes(xml);
- var count = 0;
- logger.log("====== 页面节点摘要 ======");
- for (var i = 0; i < nodes.length; i++) {
- var n = nodes[i];
- // 过滤掉空白节点
- if (n.text === "" && n.desc === "") continue;
- logger.log(" [" + (n.clickable === "true" ? "可点击" : " 不可") + "] text=\"" + n.text + "\" desc=\"" + n.desc + "\" px=" + n.px + " py=" + n.py);
- count++;
- }
- logger.log("====== 共 " + count + " 个有意义节点 ======");
- },
- /**
- * 输入文字(通过剪贴板粘贴,兼容中文)
- */
- inputText: function(text) {
- try {
- if (typeof Clipboard !== 'undefined' && Clipboard.copy) {
- Clipboard.copy(text);
- sleep.millisecond(毫秒 = 300);
- }
- // 尝试粘贴
- try {
- var node = auto.nodeSearch(3000).focused(true);
- if (node && typeof node.getNode === 'function') {
- var n = node.getNode(0);
- if (n && typeof n.paste === 'function') {
- n.paste();
- }
- }
- } catch (pe) {}
- sleep.second(秒 = 1);
- logger.log("已输入文字: " + text.substring(0, 20) + (text.length > 20 ? "..." : ""));
- return true;
- } catch (e) {
- logger.err("输入文字失败: " + e);
- return false;
- }
- },
- /**
- * 等待指定文字出现
- */
- backToText: function(text, timeout) {
- timeout = timeout || 10000;
- var startTime = new Date().getTime();
- while (new Date().getTime() - startTime < timeout) {
- if (this.hasText(text)) return true;
- sleep.millisecond(毫秒 = 500);
- }
- return false;
- }
- };
- // =============================================================================
- // 第四部分:日志系统(悬浮窗 + Toast + print.log/err/warn)
- // =============================================================================
- var logger = {
- 悬浮窗实例: null,
- 标题控件: null,
- 内容控件: null,
- 状态控件: null,
- 日志缓冲: [],
- 最大行数: 12,
- 统计: { 轮次: 0, 发货: 0, 错误: 0, 启动时间: null },
- init: function() {
- this.统计.启动时间 = new Date();
- if (!CONFIG.启用悬浮窗) return;
- try {
- this.悬浮窗实例 = new floatUI();
- this.悬浮窗实例.loadXML(
- '<LinearLayout orientation="vertical" w="400" h="280" gravity="top" id="log_main" background="#CC000000">' +
- ' <LinearLayout orientation="horizontal" w="400" h="30" gravity="center_vertical" background="#FF1a1a2e">' +
- ' <TextView id="log_title" textColor="#FFD700" textSize="13sp" padding="8dp" layout_weight="1" text="📦 淘宝自动发货v3" />' +
- ' </LinearLayout>' +
- ' <LinearLayout orientation="horizontal" w="400" h="24" gravity="center_vertical" background="#FF16213e">' +
- ' <TextView id="log_status" textColor="#00FFFF" textSize="10sp" padding="6dp" layout_weight="1" text="状态: 初始化中..." />' +
- ' </LinearLayout>' +
- ' <TextView id="log_content" textColor="#00FF00" textSize="10sp" padding="6dp" layout_weight="1" text="等待启动...\n" />' +
- '</LinearLayout>'
- );
- this.标题控件 = this.悬浮窗实例.findViewById('log_title');
- this.状态控件 = this.悬浮窗实例.findViewById('log_status');
- this.内容控件 = this.悬浮窗实例.findViewById('log_content');
- var self = this;
- setTimeout(function() {
- if (self.悬浮窗实例) self.悬浮窗实例.setPosition(5, 5);
- }, 200);
- } catch (e) {}
- },
- _time: function() {
- var now = new Date();
- var h = now.getHours(), m = now.getMinutes(), s = now.getSeconds();
- return (h < 10 ? "0" : "") + h + ":" + (m < 10 ? "0" : "") + m + ":" + (s < 10 ? "0" : "") + s;
- },
- _更新状态: function() {
- if (!this.状态控件) return;
- try {
- var runtime = "";
- if (this.统计.启动时间) {
- var diff = Math.floor((new Date() - this.统计.启动时间) / 1000);
- var h = Math.floor(diff / 3600);
- var m = Math.floor((diff % 3600) / 60);
- var s = diff % 60;
- runtime = " 运行" + (h > 0 ? h + "h" : "") + m + "m" + s + "s";
- }
- this.状态控件.setText("轮次:" + this.统计.轮次 + " | 发货:" + this.统计.发货 + " | 错误:" + this.统计.错误 + runtime);
- } catch (e) {}
- },
- _toast: function(msg) {
- try {
- if (typeof toast !== 'undefined' && toast.show) toast.show("" + msg);
- } catch (e) {}
- },
- _output: function(level, msg, showToast) {
- var timeStr = this._time();
- var levelTag = "";
- if (level === "信息") { levelTag = "ℹ"; }
- else if (level === "成功") { levelTag = "✅"; }
- else if (level === "警告") { levelTag = "⚠"; }
- else if (level === "错误") { levelTag = "❌"; this.统计.错误++; }
- var line = timeStr + " " + levelTag + " " + msg;
- // 按级别调用 AIWROK 平台原生方法
- try {
- if (level === "错误" && typeof print !== 'undefined' && print.err) {
- print.err(line);
- } else if (level === "警告" && typeof print !== 'undefined' && print.warn) {
- print.warn(line);
- } else if (typeof print !== 'undefined' && print.log) {
- print.log(line);
- } else if (typeof printl === 'function') {
- printl(line);
- }
- } catch (e) {}
- // 悬浮窗
- this.日志缓冲.push(line);
- if (this.日志缓冲.length > this.最大行数) this.日志缓冲.shift();
- if (this.内容控件) {
- try { this.内容控件.setText(this.日志缓冲.join("\n")); } catch (e) {}
- }
- this._更新状态();
- if (showToast) this._toast(levelTag + " " + msg);
- },
- log: function(msg, toast) { this._output("信息", msg, toast); },
- warn: function(msg, toast) { this._output("警告", msg, toast); },
- err: function(msg, toast) { this._output("错误", msg, toast); },
- success: function(msg, toast) { this._output("成功", msg, toast); },
- addShip: function() { this.统计.发货++; this._更新状态(); },
- addRound: function() { this.统计.轮次++; this._更新状态(); }
- };
- // =============================================================================
- // 第五部分:卡密管理
- // =============================================================================
- var 卡密管理 = {
- 取本地卡密: function(文件路径) {
- try {
- var file = new File(文件路径);
- if (!file.exists()) {
- logger.err("卡密文件不存在: " + 文件路径);
- return null;
- }
- var reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
- var lines = [], line;
- while ((line = reader.readLine()) !== null) {
- line = line.trim();
- if (line !== "" && line.indexOf("#") !== 0) lines.push(line);
- }
- reader.close();
- if (lines.length === 0) {
- logger.warn("卡密文件已空: " + 文件路径);
- return null;
- }
- var 卡密 = lines[0];
- logger.log("取出卡密: " + 卡密 + " (剩余 " + (lines.length - 1) + " 个)");
- var remaining = lines.slice(1);
- var writer = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
- for (var i = 0; i < remaining.length; i++) writer.write(remaining[i] + "\n");
- writer.close();
- return 卡密;
- } catch (e) {
- logger.err("读取卡密失败: " + e);
- return null;
- }
- },
- 取服务器卡密: function(商品ID) {
- try {
- var url = CONFIG.服务器地址 + "?action=get&product=" + 商品ID + "&key=" + CONFIG.服务器密钥;
- logger.log("从服务器获取卡密: " + url);
- var response = http.get(url);
- if (!response) { logger.err("服务器无响应"); return null; }
- try {
- var json = JSON.parse(response);
- if (json.code === 0 && json.data) {
- logger.log("服务器返回卡密: " + json.data);
- return json.data;
- }
- logger.err("服务器返回错误: " + (json.msg || json.message || response));
- } catch (e) {
- if (response && response.length > 0) {
- logger.log("服务器返回卡密: " + response);
- return response;
- }
- }
- } catch (e) {
- logger.err("服务器获取卡密失败: " + e);
- }
- return null;
- },
- 上报已使用: function(商品ID, 卡密, 订单号) {
- try {
- var url = CONFIG.服务器地址 + "?action=report&product=" + 商品ID + "&key=" + 卡密 + "&order=" + 订单号;
- var response = http.get(url);
- logger.log("服务器上报结果: " + response);
- } catch (e) {
- logger.err("服务器上报失败: " + e);
- }
- },
- 取卡密: function(商品) {
- if (CONFIG.使用服务器) return this.取服务器卡密(商品.名称 || "");
- return this.取本地卡密(商品.卡密文件);
- },
- 检查库存: function(商品) {
- if (CONFIG.使用服务器) return -1;
- try {
- var file = new File(商品.卡密文件);
- if (!file.exists()) return 0;
- var reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
- var count = 0, line;
- while ((line = reader.readLine()) !== null) {
- line = line.trim();
- if (line !== "" && line.indexOf("#") !== 0) count++;
- }
- reader.close();
- return count;
- } catch (e) { return -1; }
- }
- };
- // =============================================================================
- // 第六部分:发货记录
- // =============================================================================
- var 发货记录 = {
- 已发货: function(订单号) {
- try {
- var file = new File(CONFIG.发货记录文件);
- if (!file.exists()) return false;
- var reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
- var line;
- while ((line = reader.readLine()) !== null) {
- if (line.indexOf(订单号) !== -1) { reader.close(); return true; }
- }
- reader.close();
- } catch (e) {}
- return false;
- },
- 记录: function(订单号, 商品名, 发货内容) {
- try {
- var writer = new OutputStreamWriter(new FileOutputStream(CONFIG.发货记录文件, true), "UTF-8");
- var now = new Date();
- var dateStr = now.getFullYear() + "-" + (now.getMonth() + 1) + "-" + now.getDate() + " " + logger._time();
- writer.write(dateStr + " | " + 订单号 + " | " + 商品名 + " | " + 发货内容 + "\n");
- writer.close();
- logger.log("已记录发货: " + 订单号 + " -> " + 商品名);
- } catch (e) {
- logger.err("记录发货失败: " + e);
- }
- },
- 今日数量: function() {
- try {
- var file = new File(CONFIG.发货记录文件);
- if (!file.exists()) return 0;
- var reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
- var now = new Date();
- var today = now.getFullYear() + "-" + (now.getMonth() + 1) + "-" + now.getDate();
- var count = 0, line;
- while ((line = reader.readLine()) !== null) {
- if (line.indexOf(today) !== -1) count++;
- }
- reader.close();
- return count;
- } catch (e) { return 0; }
- }
- };
- // =============================================================================
- // 第七部分:千牛自动化操作
- // =============================================================================
- var 千牛操作 = {
- 启动千牛: function() {
- logger.log("启动千牛...", true);
- try {
- app.openApp(CONFIG.千牛包名);
- for (var i = 8; i > 0; i--) {
- sleep.second(秒 = 1);
- }
- logger.success("千牛已启动");
- return true;
- } catch (e) {
- logger.err("启动千牛失败: " + e);
- return false;
- }
- },
- 确保运行: function() {
- // 无碍模式没有shell,直接用app.openApp确保运行
- app.openApp(CONFIG.千牛包名);
- sleep.second(秒 = 3);
- },
- /**
- * 检查是否在订单管理页面
- */
- 在订单管理页: function() {
- return ActionAdapter.hasText("待发货") || ActionAdapter.hasText("待付款") ||
- ActionAdapter.hasText("已发货") || ActionAdapter.hasText("催发货");
- },
- /**
- * 检查待发货标签是否已选中
- */
- 待发货已选中: function() {
- var xml = ActionAdapter.getXml();
- if (!xml) return false;
- var nodes = ActionAdapter.findNodes(xml, "待发货");
- for (var i = 0; i < nodes.length; i++) {
- // selected=true 表示已选中
- var pattern = /<node\s+([^>]+?)(?:\/>|>)/g;
- var match;
- while ((match = pattern.exec(xml)) !== null) {
- var attrs = match[1];
- if ((attrs.indexOf("待发货") !== -1) && attrs.indexOf('selected="true"') !== -1) {
- return true;
- }
- }
- }
- return false;
- },
- /**
- * 进入待发货订单列表
- * 多条路径:直接找"待发货"标签 → 找"订单管理"入口 → 首页重试
- */
- 进入待发货列表: function() {
- logger.log("进入待发货订单列表...", true);
- // 路径1:直接点击"待发货"标签
- if (ActionAdapter.findTextAndClick("待发货", 3000)) {
- sleep.second(秒 = CONFIG.操作延时);
- if (this.在订单管理页()) {
- logger.log("已在待发货列表页面");
- return true;
- }
- }
- // 路径2:找"订单管理"入口
- if (ActionAdapter.findTextAndClick("订单管理", 2000)) {
- sleep.second(秒 = CONFIG.操作延时);
- if (ActionAdapter.findTextAndClick("待发货", 3000)) {
- sleep.second(秒 = CONFIG.操作延时);
- if (this.在订单管理页()) return true;
- }
- }
- // 路径3:从首页找订单入口
- ActionAdapter.home();
- sleep.second(秒 = 2);
- if (ActionAdapter.findTextAndClick("订单", 3000)) {
- sleep.second(秒 = CONFIG.操作延时);
- ActionAdapter.findTextAndClick("待发货", 3000);
- sleep.second(秒 = CONFIG.操作延时);
- if (this.在订单管理页()) return true;
- }
- logger.warn("未能进入待发货列表");
- return false;
- },
- /**
- * 检查是否有新订单(空状态消失 = 有订单)
- */
- 有新订单: function() {
- // 空状态提示
- if (ActionAdapter.hasText("还没有相关订单") || ActionAdapter.hasText("暂无")) {
- return false;
- }
- // 有订单时的特征文字
- var 订单特征 = ["立即发货", "去发货", "买家已付款", "联系买家"];
- for (var i = 0; i < 订单特征.length; i++) {
- if (ActionAdapter.hasText(订单特征[i])) {
- logger.log("检测到待发货订单: " + 订单特征[i], true);
- return true;
- }
- }
- // 检查是否有订单号格式的数字
- var xml = ActionAdapter.getXml();
- if (xml) {
- var allNodes = ActionAdapter.findAllNodes(xml);
- for (var j = 0; j < allNodes.length; j++) {
- if (allNodes[j].text && /^\d{15,20}$/.test(allNodes[j].text)) {
- logger.log("检测到订单号: " + allNodes[j].text, true);
- return true;
- }
- }
- }
- return false;
- },
- /**
- * 匹配商品(用完整商品标题精准匹配)
- * 遍历商品配置,在页面XML中查找哪个商品标题出现了
- * @return {object|null} {名称, 配置, 节点}
- */
- 匹配商品: function() {
- var xml = ActionAdapter.getXml();
- if (!xml) xml = "";
- for (var 商品标题 in 商品配置) {
- // 直接用完整标题去XML里找
- var nodes = ActionAdapter.findNodes(xml, 商品标题);
- if (nodes.length > 0) {
- logger.success("精准匹配到商品: " + 商品标题, true);
- logger.log(" 节点: text=[" + nodes[0].text + "] desc=[" + nodes[0].desc + "] px=" + nodes[0].px + " py=" + nodes[0].py);
- return {
- 名称: 商品标题,
- 配置: 商品配置[商品标题],
- 节点: nodes[0]
- };
- }
- }
- // 如果完整标题没匹配到,尝试模糊匹配(标题前6个字)
- for (var 标题 in 商品配置) {
- var 短标题 = 标题.substring(0, 6);
- var nodes2 = ActionAdapter.findNodes(xml, 短标题);
- if (nodes2.length > 0) {
- logger.log("模糊匹配到商品: " + 标题 + " (关键词: " + 短标题 + ")", true);
- return {
- 名称: 标题,
- 配置: 商品配置[标题],
- 节点: nodes2[0]
- };
- }
- }
- logger.warn("未匹配到任何商品");
- return null;
- },
- /**
- * 获取当前订单号
- */
- 获取订单号: function() {
- var xml = ActionAdapter.getXml();
- if (xml) {
- var patterns = [
- /订单号[::]\s*(\d{10,20})/,
- /订单编号[::]\s*(\d{10,20})/,
- /"(\d{15,20})"/
- ];
- for (var i = 0; i < patterns.length; i++) {
- var m = xml.match(patterns[i]);
- if (m && m[1]) {
- logger.log("提取到订单号: " + m[1]);
- return m[1];
- }
- }
- }
- var 临时号 = "TEMP" + new Date().getTime();
- logger.warn("未能提取订单号,使用临时号: " + 临时号);
- return 临时号;
- },
- /**
- * 点击订单进入详情页
- */
- 点击进入订单: function(商品节点) {
- if (!商品节点) {
- ActionAdapter.clickPercent(0.5, 0.4);
- sleep.second(秒 = CONFIG.操作延时);
- } else {
- var node = 商品节点.节点 || 商品节点;
- if (node.px > 0 && node.py > 0) {
- ActionAdapter.clickPercent(node.px, node.py);
- } else {
- ActionAdapter.clickPercent(0.5, 0.4);
- }
- sleep.second(秒 = CONFIG.操作延时);
- }
- // 验证是否进入订单详情
- if (ActionAdapter.hasText("订单详情") || ActionAdapter.hasText("订单号") ||
- ActionAdapter.hasText("联系买家") || ActionAdapter.hasText("发货") ||
- ActionAdapter.hasText("阿里旺旺") || ActionAdapter.hasText("旺旺")) {
- logger.success("已进入订单详情");
- return true;
- }
- if (ActionAdapter.hasText("确认发货") || ActionAdapter.hasText("物流")) {
- logger.success("已进入发货页面");
- return true;
- }
- logger.warn("未能进入订单详情");
- return false;
- },
- /**
- * 进入买家聊天界面(点阿里旺旺)
- */
- 进入聊天: function() {
- logger.log("进入买家聊天界面...");
- var 按钮文字 = ["阿里旺旺", "旺旺", "联系买家", "发消息", "联系", "留言"];
- for (var i = 0; i < 按钮文字.length; i++) {
- if (ActionAdapter.findTextAndClick(按钮文字[i], 2000)) {
- sleep.second(秒 = CONFIG.操作延时);
- logger.success("已进入聊天界面: " + 按钮文字[i]);
- return true;
- }
- }
- logger.warn("未能进入聊天界面");
- return false;
- },
- /**
- * 在聊天界面发送消息
- */
- 发送消息: function(消息内容) {
- logger.log("准备发送消息...", true);
- // 步骤1:点击聊天输入框
- ActionAdapter.clickPercent(0.5, 0.92);
- sleep.second(秒 = 1);
- // 步骤2:通过剪贴板粘贴
- ActionAdapter.inputText(消息内容);
- sleep.second(秒 = 1);
- // 步骤3:点击发送按钮
- var 发送按钮文字 = ["发送", "Send", "发送消息"];
- var 发送成功 = false;
- for (var i = 0; i < 发送按钮文字.length; i++) {
- if (ActionAdapter.findTextAndClick(发送按钮文字[i], 2000)) {
- sleep.second(秒 = 1);
- 发送成功 = true;
- break;
- }
- }
- // 百分比点击发送按钮(通常在右下角)
- if (!发送成功) {
- ActionAdapter.clickPercent(0.95, 0.92);
- sleep.second(秒 = 1);
- 发送成功 = true;
- }
- if (发送成功) logger.success("消息已发送", true);
- return 发送成功;
- },
- /**
- * 返回待发货订单列表
- */
- 返回列表: function() {
- logger.log("返回订单列表...");
- ActionAdapter.back();
- sleep.second(秒 = CONFIG.操作延时);
- ActionAdapter.back();
- sleep.second(秒 = CONFIG.操作延时);
- if (!this.在订单管理页()) {
- sleep.second(秒 = 1);
- if (!this.在订单管理页()) {
- logger.warn("返回后不在订单管理页,重新进入");
- this.进入待发货列表();
- }
- }
- },
- /**
- * 执行发货(完整流程)
- * 在订单详情页点击"发货" → 进入发货页面 → 选"无需物流" → 填写发货留言 → 确认发货
- * @param {object} 发货内容 - {消息, 摘要, 卡密}
- * @return {boolean} 是否发货成功
- */
- 执行发货: function(发货内容) {
- logger.log("===== 开始执行发货操作 =====", true);
- // 步骤1:在订单详情页点击"发货"按钮
- var 发货按钮 = ["立即发货", "去发货", "发货"];
- var 点击成功 = false;
- for (var i = 0; i < 发货按钮.length; i++) {
- if (ActionAdapter.findTextAndClick(发货按钮[i], 3000)) {
- sleep.second(秒 = CONFIG.操作延时);
- logger.log("已点击发货按钮: " + 发货按钮[i]);
- 点击成功 = true;
- break;
- }
- }
- if (!点击成功) {
- // 可能已经在发货页面了
- if (ActionAdapter.hasText("确认发货") || ActionAdapter.hasText("无需物流")) {
- logger.log("已在发货页面");
- 点击成功 = true;
- } else {
- logger.err("未找到发货按钮");
- return false;
- }
- }
- // 步骤2:等待发货页面加载,检查页面特征
- sleep.second(秒 = 2);
- var 在发货页 = ActionAdapter.hasText("确认发货") ||
- ActionAdapter.hasText("无需物流") ||
- ActionAdapter.hasText("物流公司") ||
- ActionAdapter.hasText("发货方式");
- if (!在发货页) {
- logger.log("发货页面未加载,再等一下...");
- sleep.second(秒 = 2);
- 在发货页 = ActionAdapter.hasText("确认发货") ||
- ActionAdapter.hasText("无需物流") ||
- ActionAdapter.hasText("物流公司");
- }
- if (在发货页) {
- logger.log("已进入发货页面");
- ActionAdapter.dumpPageSummary();
- }
- // 步骤3:选择"无需物流"(虚拟商品不需要快递)
- var 物流选项 = ["无需物流", "自己联系物流", "虚拟商品", "电子凭证", "不需要物流"];
- var 选了物流 = false;
- for (var j = 0; j < 物流选项.length; j++) {
- if (ActionAdapter.findTextAndClick(物流选项[j], 2000)) {
- sleep.second(秒 = 1);
- logger.log("已选择: " + 物流选项[j]);
- 选了物流 = true;
- break;
- }
- }
- if (!选了物流) {
- logger.log("未找到'无需物流'选项,可能默认已选或页面不同");
- // 输出页面节点帮助排查
- ActionAdapter.dumpPageSummary();
- }
- // 步骤4:填写发货留言/备注(把卡密/网盘信息填进去)
- var 留言字段 = ["发货留言", "卖家发货备注", "发货备注", "留言", "备注", "给买家留言"];
- var 填了留言 = false;
- for (var k = 0; k < 留言字段.length; k++) {
- if (ActionAdapter.findTextAndClick(留言字段[k], 1500)) {
- sleep.second(秒 = 1);
- // 点击后可能出现输入框,填入发货内容
- ActionAdapter.inputText(发货内容.消息);
- sleep.second(秒 = 1);
- logger.log("已填写发货留言: " + 留言字段[k]);
- 填了留言 = true;
- break;
- }
- }
- if (!填了留言) {
- // 尝试找输入框直接填写
- logger.log("未找到留言字段,尝试直接找输入框...");
- // 发货页面的输入框通常在中部
- ActionAdapter.clickPercent(0.5, 0.5);
- sleep.second(秒 = 1);
- ActionAdapter.inputText(发货内容.消息);
- sleep.second(秒 = 1);
- }
- // 步骤5:点击"确认发货"按钮
- logger.log("寻找确认发货按钮...");
- var 确认按钮 = ["确认发货", "确定发货", "提交", "确定"];
- var 确认成功 = false;
- for (var m = 0; m < 确认按钮.length; m++) {
- if (ActionAdapter.findTextAndClick(确认按钮[m], 3000)) {
- sleep.second(秒 = CONFIG.操作延时);
- logger.log("已点击: " + 确认按钮[m]);
- 确认成功 = true;
- break;
- }
- }
- if (!确认成功) {
- // 尝试点击右下角的确认按钮(常见位置)
- logger.log("未找到确认按钮文字,尝试点击右下角...");
- ActionAdapter.clickPercent(0.85, 0.9);
- sleep.second(秒 = CONFIG.操作延时);
- }
- // 步骤6:验证发货是否成功
- sleep.second(秒 = 2);
- var 发货成功 = ActionAdapter.hasText("发货成功") ||
- ActionAdapter.hasText("已发货") ||
- ActionAdapter.hasText("发货完成") ||
- ActionAdapter.hasText("提交成功");
- // 如果没有明确的成功提示,检查是否回到了订单详情或列表
- if (!发货成功) {
- if (ActionAdapter.hasText("订单详情") || ActionAdapter.hasText("待发货") ||
- ActionAdapter.hasText("已发货") || ActionAdapter.hasText("催发货")) {
- logger.log("已返回订单页面,推断发货成功");
- 发货成功 = true;
- }
- }
- // 处理可能的二次确认弹窗
- if (ActionAdapter.hasText("确定") || ActionAdapter.hasText("确认")) {
- var 弹窗按钮 = ["确定", "确认", "是"];
- for (var n = 0; n < 弹窗按钮.length; n++) {
- if (ActionAdapter.findTextAndClick(弹窗按钮[n], 1500)) {
- sleep.second(秒 = 1);
- logger.log("已确认弹窗: " + 弹窗按钮[n]);
- break;
- }
- }
- sleep.second(秒 = 2);
- // 再次检查
- if (!发货成功) {
- 发货成功 = ActionAdapter.hasText("发货成功") ||
- ActionAdapter.hasText("已发货") ||
- ActionAdapter.hasText("发货完成");
- }
- }
- if (发货成功) {
- logger.success("发货操作完成!", true);
- } else {
- logger.warn("未能确认发货是否成功,请手动检查");
- ActionAdapter.dumpPageSummary();
- }
- return 发货成功;
- },
- /**
- * 标记发货(旧方法保留,仅作为后备)
- */
- 标记发货: function() {
- logger.log("尝试标记订单发货...");
- var 发货按钮 = ["发货", "确认发货", "一键发货"];
- for (var i = 0; i < 发货按钮.length; i++) {
- if (ActionAdapter.findTextAndClick(发货按钮[i], 2000)) {
- sleep.second(秒 = CONFIG.操作延时);
- logger.success("已标记发货");
- return true;
- }
- }
- logger.warn("未找到发货按钮");
- return false;
- }
- };
- // =============================================================================
- // 第八部分:发货核心流程
- // =============================================================================
- var 发货流程 = {
- /**
- * 处理一个订单的完整流程
- * 匹配商品 → 进详情 → 执行发货(选无需物流+确认) → 点旺旺 → 发消息 → 返回列表
- */
- 处理订单: function() {
- logger.log("========== 开始处理订单 ==========");
- // 步骤1:匹配商品
- var 商品 = 千牛操作.匹配商品();
- if (!商品) {
- logger.warn("未匹配到商品,跳过");
- return false;
- }
- // 步骤2:获取订单号
- var 订单号 = 千牛操作.获取订单号();
- // 步骤3:检查是否已发货
- if (发货记录.已发货(订单号)) {
- logger.log("订单 " + 订单号 + " 已发过货,跳过");
- ActionAdapter.swipePercent(0.5, 0.6, 0.5, 0.3, 500);
- sleep.second(秒 = 1);
- return false;
- }
- // 步骤4:准备发货内容
- var 发货内容 = this.准备发货内容(商品);
- if (!发货内容) {
- logger.err("准备发货内容失败");
- return false;
- }
- // 步骤5:点击订单进入详情
- if (!千牛操作.点击进入订单(商品.节点)) {
- logger.err("进入订单详情失败");
- return false;
- }
- // 步骤6:执行发货(点发货 → 选无需物流 → 填留言 → 确认发货)
- var 发货结果 = 千牛操作.执行发货(发货内容);
- if (发货结果) {
- logger.success("订单已发货: " + 订单号, true);
- } else {
- logger.warn("发货操作未能确认成功,继续发旺旺消息");
- }
- // 步骤7:进入聊天界面(点阿里旺旺,给买家发卡密/网盘信息)
- // 此时可能需要返回到订单详情页
- if (!ActionAdapter.hasText("订单详情") && !ActionAdapter.hasText("阿里旺旺")) {
- // 如果发货后不在订单详情,尝试返回
- ActionAdapter.back();
- sleep.second(秒 = CONFIG.操作延时);
- }
- if (千牛操作.进入聊天()) {
- // 步骤8:发送消息
- if (千牛操作.发送消息(发货内容.消息)) {
- logger.success("旺旺消息已发送", true);
- // 步骤9:返回订单详情
- ActionAdapter.back();
- sleep.second(秒 = CONFIG.操作延时);
- } else {
- logger.err("发送旺旺消息失败");
- }
- } else {
- logger.warn("未能进入旺旺聊天(发货已完成,消息发送失败)");
- }
- // 步骤10:记录发货
- 发货记录.记录(订单号, 商品.名称, 发货内容.摘要);
- // 步骤11:上报服务器
- if (CONFIG.使用服务器 && 发货内容.卡密) {
- 卡密管理.上报已使用(商品.名称, 发货内容.卡密, 订单号);
- }
- logger.success("订单 " + 订单号 + " 处理完成: " + 商品.名称, true);
- logger.success("今日已发货: " + 发货记录.今日数量() + " 单");
- // 步骤12:返回列表
- 千牛操作.返回列表();
- return true;
- },
- 准备发货内容: function(商品) {
- var 配置 = 商品.配置;
- var 话术 = 配置.发货话术;
- var 卡密 = "";
- var 网盘 = 配置.网盘链接 || "";
- var 摘要 = "";
- if (配置.发货类型 === "卡密") {
- 卡密 = 卡密管理.取卡密(配置);
- if (!卡密) { logger.err("无可用卡密!商品: " + 商品.名称); return null; }
- 话术 = 话术.replace("{卡密}", 卡密).replace("{网盘}", 网盘);
- 摘要 = "卡密:" + 卡密;
- } else if (配置.发货类型 === "网盘") {
- 话术 = 话术.replace("{网盘}", 网盘).replace("{卡密}", "");
- 摘要 = "网盘:" + 网盘.substring(0, 20);
- } else if (配置.发货类型 === "卡密+网盘") {
- 卡密 = 卡密管理.取卡密(配置);
- if (!卡密) { logger.err("无可用卡密!商品: " + 商品.名称); return null; }
- 话术 = 话术.replace("{卡密}", 卡密).replace("{网盘}", 网盘);
- 摘要 = "卡密:" + 卡密 + " 网盘:" + 网盘.substring(0, 15);
- }
- return { 消息: 话术, 摘要: 摘要, 卡密: 卡密 };
- },
- /**
- * 处理当前所有可见的待发货订单
- */
- 处理当前订单: function() {
- var 处理数量 = 0;
- var 连续无订单 = 0;
- for (var i = 0; i < 50; i++) {
- if (!千牛操作.有新订单()) {
- 连续无订单++;
- if (连续无订单 >= 2) {
- logger.log("没有更多待发货订单");
- break;
- }
- ActionAdapter.swipePercent(0.5, 0.6, 0.5, 0.3, 500);
- sleep.second(秒 = 1);
- continue;
- }
- 连续无订单 = 0;
- logger.log("--- 处理第 " + (i + 1) + " 个订单 ---");
- if (this.处理订单()) 处理数量++;
- sleep.second(秒 = 1);
- }
- logger.success("本轮处理完成,共发货 " + 处理数量 + " 单", true);
- return 处理数量;
- }
- };
- // =============================================================================
- // 第九部分:保活心跳(无碍版,不用shell)
- // =============================================================================
- var 保活系统 = {
- 线程: null,
- 运行中: false,
- start: function() {
- if (!CONFIG.启用保活) return;
- this.运行中 = true;
- this.线程 = new thread().runJsCode(function() {
- while (保活系统.运行中) {
- try {
- // 无碍模式没有shell,用getXml检查千牛是否在前台
- var xml = ActionAdapter.getXml();
- if (!xml || xml === "") {
- logger.warn("千牛可能未运行,尝试重启...");
- app.openApp(CONFIG.千牛包名);
- sleep.second(秒 = 8);
- }
- logger.log("保活心跳: 今日发货 " + 发货记录.今日数量() + " 单");
- } catch (e) {}
- sleep.second(秒 = CONFIG.保活间隔);
- }
- });
- logger.log("保活线程已启动");
- },
- stop: function() {
- this.运行中 = false;
- logger.log("保活线程已停止");
- }
- };
- // =============================================================================
- // 第十部分:库存预警
- // =============================================================================
- var 库存预警 = {
- 检查全部: function() {
- var 预警商品 = [];
- for (var 商品名 in 商品配置) {
- var 配置 = 商品配置[商品名];
- if (配置.发货类型 === "卡密" || 配置.发货类型 === "卡密+网盘") {
- var 库存 = 卡密管理.检查库存(配置);
- if (库存 >= 0 && 库存 < 5) {
- 预警商品.push(商品名 + " (剩余" + 库存 + "个)");
- logger.warn("库存预警: " + 商品名 + " 仅剩 " + 库存 + " 个卡密!");
- }
- }
- }
- return 预警商品;
- }
- };
- // =============================================================================
- // 第十一部分:主控制流程(持续监控模式)
- // =============================================================================
- var 主控 = {
- 运行中: false,
- 统计: { 总轮次: 0, 总发货: 0, 开始时间: null, 最后检查: null },
- 初始化: function() {
- logger.log("========================================");
- logger.log(" 淘宝千牛自动发货系统 v3.0");
- logger.log(" 精简版 · 仅无碍模式 · 完整标题匹配");
- logger.log("========================================");
- logger.init();
- if (!ActionAdapter.init()) {
- logger.err("无碍模式初始化失败,系统退出");
- return false;
- }
- try {
- var dir = new File(CONFIG.卡密目录);
- if (!dir.exists()) dir.mkdirs();
- } catch (e) {}
- var 预警 = 库存预警.检查全部();
- if (预警.length > 0) logger.warn("库存预警: " + 预警.join(", "));
- logger.log("商品数量: " + Object.keys(商品配置).length);
- logger.log("监控间隔: " + CONFIG.轮询间隔 + "秒");
- this.统计.开始时间 = new Date();
- logger.success("系统初始化完成", true);
- return true;
- },
- 进入监控状态: function() {
- 千牛操作.确保运行();
- sleep.second(秒 = 2);
- if (!千牛操作.进入待发货列表()) {
- logger.err("进入待发货列表失败!", true);
- return false;
- }
- if (!千牛操作.待发货已选中()) {
- ActionAdapter.findTextAndClick("待发货", 3000);
- sleep.second(秒 = CONFIG.操作延时);
- }
- logger.success("已进入待发货列表,开始持续监控", true);
- return true;
- },
- 监控一轮: function() {
- this.统计.总轮次++;
- logger.addRound();
- try {
- if (!千牛操作.在订单管理页()) {
- logger.warn("不在订单管理页面,重新进入");
- this.进入监控状态();
- return;
- }
- if (千牛操作.有新订单()) {
- logger.success("检测到新订单!开始处理...", true);
- var 发货数 = 发货流程.处理当前订单();
- this.统计.总发货 += 发货数;
- for (var i = 0; i < 发货数; i++) logger.addShip();
- if (发货数 > 0) {
- logger.success("本轮发货 " + 发货数 + " 单,累计 " + this.统计.总发货 + " 单", true);
- }
- if (!千牛操作.在订单管理页()) this.进入监控状态();
- } else {
- if (this.统计.总轮次 % 10 === 0) {
- logger.log("监控中... 暂无新订单 (轮次:" + this.统计.总轮次 + ")");
- }
- if (this.统计.总轮次 % 20 === 0) 库存预警.检查全部();
- }
- } catch (e) {
- logger.err("监控异常: " + e, true);
- logger.log("异常恢复:重新进入待发货列表...", true);
- this.进入监控状态();
- }
- this.统计.最后检查 = new Date();
- },
- 主循环: function() {
- this.运行中 = true;
- logger.success("系统进入持续监控模式", true);
- logger.log("停在待发货列表页面,有人下单自动发货", true);
- while (this.运行中) {
- try { this.监控一轮(); } catch (e) { logger.err("主循环异常: " + e, true); }
- var 等待秒数 = CONFIG.轮询间隔;
- for (var i = 等待秒数; i > 0; i--) {
- if (!this.运行中) break;
- if (i % 10 === 0 || i <= 3) { try { logger._更新状态(); } catch (e2) {} }
- sleep.second(秒 = 1);
- }
- }
- },
- start: function() {
- if (!this.初始化()) return;
- 保活系统.start();
- if (!this.进入监控状态()) {
- logger.err("无法进入监控状态,5秒后重试...", true);
- sleep.second(秒 = 5);
- if (!this.进入监控状态()) {
- logger.err("重试失败,系统退出", true);
- return;
- }
- }
- this.主循环();
- },
- stop: function() {
- this.运行中 = false;
- 保活系统.stop();
- logger.log("系统已停止");
- }
- };
- // =============================================================================
- // 第十二部分:启动入口
- // =============================================================================
- function 确保目录存在(路径) {
- try {
- var dir = new File(路径);
- if (!dir.exists()) dir.mkdirs();
- } catch (e) {}
- }
- function 创建卡密示例() {
- try {
- for (var 商品名 in 商品配置) {
- var 配置 = 商品配置[商品名];
- if (配置.卡密文件 && 配置.卡密文件 !== "") {
- var file = new File(配置.卡密文件);
- if (!file.exists()) {
- var writer = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
- writer.write("# " + 商品名 + " 卡密文件\n");
- writer.write("# 每行一个卡密,用过的自动删除\n\n");
- writer.write("Q5J3HHT8-NJMF\n");
- writer.write("KA8CPLJP-DNCM\n");
- writer.write("FYYEGC6D-KPCA\n");
- writer.close();
- logger.log("已创建卡密示例文件: " + 配置.卡密文件);
- }
- }
- }
- } catch (e) {
- logger.err("创建卡密示例失败: " + e);
- }
- }
- // =============================================================================
- // 启动
- // =============================================================================
- 确保目录存在(CONFIG.卡密目录);
- 确保目录存在("/sdcard/");
- 创建卡密示例();
- if (CONFIG.调试模式 === "debug") {
- // ===== 调试模式:输出页面节点,验证XML解析 =====
- logger.init();
- logger.log("========================================");
- logger.log(" 调试模式:验证XML解析");
- logger.log("========================================");
- if (!ActionAdapter.init()) {
- logger.err("无碍模式初始化失败");
- } else {
- 千牛操作.启动千牛();
- sleep.second(秒 = 3);
- logger.log("===== 当前页面节点摘要 =====");
- ActionAdapter.dumpPageSummary();
- logger.log("===== 测试查找'待发货'节点 =====");
- var xml = ActionAdapter.getXml();
- var nodes = ActionAdapter.findNodes(xml, "待发货");
- logger.log("查找'待发货'结果: " + nodes.length + " 个节点");
- for (var i = 0; i < nodes.length; i++) {
- logger.log(" 节点" + i + ": text=[" + nodes[i].text + "] desc=[" + nodes[i].desc + "] px=" + nodes[i].px + " py=" + nodes[i].py + " clickable=" + nodes[i].clickable);
- if (nodes[i].clickable === "false") {
- var parent = ActionAdapter.findClickableParent(nodes[i], xml);
- if (parent) {
- logger.log(" -> 父节点: px=" + parent.px + " py=" + parent.py + " clickable=" + parent.clickable);
- }
- }
- }
- logger.log("===== 测试商品标题匹配 =====");
- var 商品结果 = 千牛操作.匹配商品();
- if (商品结果) {
- logger.success("匹配到商品: " + 商品结果.名称);
- } else {
- logger.log("当前页面未匹配到商品(正常,列表为空时无商品)");
- }
- logger.success("调试完成!", true);
- logger.log("如需测试真实发货,请将 CONFIG.调试模式 设为 'test'");
- logger.log("如需正常监控,请将 CONFIG.调试模式 设为 false");
- }
- } else if (CONFIG.调试模式 === "test") {
- // ===== 测试模式:处理一个真实订单,验证完整发货流程 =====
- logger.init();
- logger.log("========================================");
- logger.log(" 测试模式:处理一个真实订单");
- logger.log("========================================");
- if (!ActionAdapter.init()) {
- logger.err("无碍模式初始化失败");
- } else {
- // 启动千牛
- 千牛操作.启动千牛();
- sleep.second(秒 = 3);
- // 进入待发货列表
- if (!千牛操作.进入待发货列表()) {
- logger.err("进入待发货列表失败!", true);
- } else {
- logger.log("===== 检查是否有待发货订单 =====");
- if (千牛操作.有新订单()) {
- logger.success("检测到订单!开始处理...", true);
- // 输出页面节点,看看订单列表长什么样
- logger.log("===== 订单列表节点摘要 =====");
- ActionAdapter.dumpPageSummary();
- // 尝试匹配商品
- logger.log("===== 尝试匹配商品 =====");
- var 商品 = 千牛操作.匹配商品();
- if (商品) {
- logger.success("匹配到商品: " + 商品.名称, true);
- // 处理这个订单
- logger.log("===== 开始处理订单 =====");
- var 结果 = 发货流程.处理订单();
- if (结果) {
- logger.success("测试发货成功!", true);
- } else {
- logger.err("测试发货失败,请检查日志", true);
- }
- } else {
- logger.warn("未匹配到商品");
- logger.log("请检查商品配置中的标题是否与淘宝订单中的商品标题一致");
- logger.log("===== 当前页面所有节点 =====");
- ActionAdapter.dumpPageSummary();
- }
- } else {
- logger.log("当前没有待发货订单,请先在淘宝下一单测试");
- logger.log("===== 当前页面节点 =====");
- ActionAdapter.dumpPageSummary();
- }
- }
- logger.log("========================================");
- logger.log("测试完成!");
- logger.log("如需正常监控,请将 CONFIG.调试模式 设为 false");
- logger.log("========================================");
- }
- } else {
- // ===== 正常模式:24小时持续监控 =====
- 主控.start();
- }
复制代码
| |  | |  |
|
|