信息发布软件,b2b软件,广告发布软件

 找回密码
 立即注册
搜索
查看: 1|回复: 0

[宣传软件网站动态] ⛓️WebSocket带截图/点击/滑屏/按键/跑脚本/状态查询

[复制链接]

1962

主题

1972

帖子

1万

积分

积分
11500
发表于 半小时前 | 显示全部楼层 |阅读模式

资讯缩略图:

资讯发布日期:2026-08-25

资讯发布简介:⛓️WebSocket带截图/点击/滑屏/按键/跑脚本/状态查询

资讯关键词:⛓️WebSocket带截图/点击/滑屏/按键/跑脚本/状态查询

资讯所属分类:IT资讯 SEO资讯 

联系:

① 本信息收集于网络,如有不对的地方欢迎联系我纠正!
② 本信息免费收录,不存在价格的问题!
③ 如果您的网站也想这样出现在这里,请您加好友情链接,我当天会审核通过!

④友情链接关键字:软件网站分类目录 网址:http://www.postbbs.com/

资讯详细描述
⛓️WebSocket带截图/点击/滑屏/按键/跑脚本/状态查询


QQ截图20260824132547.png

  1. // =====================================================================
  2. //  AIWROK WebSocket 客户端(实用版 — 带截图/点击/滑屏/按键/跑脚本/状态查询)
  3. //
  4. //  服务端可下发的指令(type):
  5. //    CMD_SCREENSHOT           截图(base64 返回,可调 quality 0.3~0.95)
  6. //    CMD_CLICK                点击 {x, y}
  7. //    CMD_SWIPE                滑屏 {x1,y1,x2,y2,duration}
  8. //    CMD_KEY                  按键 {key: "BACK"|"HOME"|"RECENT"|"POWER"|"NOTIFICATION"|数字keyCode}
  9. //    CMD_RUN_SCRIPT           远程启动脚本 {path: "/sdcard/AIWROK/xxx.js", timeoutSec, args:{...}}
  10. //    CMD_STATUS               查设备状态(包名/内存/运行时长/分辨率)
  11. //    CMD_TOAST                弹提示 {msg}
  12. //    CMD_CLEAR_CACHE          清除本地缓存
  13. //    MANAGE_TASK_NEW          下发任务(兼容原格式,自动转成 CMD_RUN_SCRIPT 或回执)
  14. //    MANAGE_CMD_STOP_ALL      停止一切(写标志 + 设 stopAll + 回 CLIENT_STOPPING + 关 WS)
  15. //    SERVER_PING              心跳回包
  16. //
  17. //  客户端上报:
  18. //    CLIENT_REGISTER / CLIENT_HEARTBEAT / CLIENT_TASK_ACK
  19. //    CMD_RESP  (每条 CMD_* 指令执行完都回,格式统一,含 ok/msg/data/ts/cmdId)
  20. //    CLIENT_STOPPING  (停止前最后一条广播给服务端)
  21. //
  22. //  用法:CFG.serverUrl 改成 ws://ip:port/ws,直接跑
  23. // =====================================================================

  24. // ---------- 全局变量(必须放在文件顶部,避免 Rhino ES5 ReferenceError)----------
  25. var G = {
  26.     ws: null,
  27.     clientId: null,
  28.     isAllowReConnect: true,
  29.     isInfiniteReConnect: true,
  30.     reConnectMaxTimes: 10,
  31.     reConnectTimes: 0,
  32.     connectDelayMs: 3000,
  33.     multiplier: 2,
  34.     maxConnectDelayMs: 30000,
  35.     heartbeatIntervalMs: 50000,
  36.     heartbeatTimer: null,
  37.     lastTs: 0,
  38.     dispatcher: {},
  39.     running: true,
  40.     stopAll: false,            // STOP_ALL 全局标志(业务脚本都可以读这个退出)
  41.     startTime: nowSec(),       // 启动时间戳
  42.     activeThreads: {},         // CMD_RUN_SCRIPT 启动的线程 id -> thread 对象
  43.     STOP_FLAG: "/sdcard/AIWROK/STOP_ALL"
  44. };

  45. var CFG = {
  46.     serverUrl: "ws://192.168.10.103:32101/ws", // 主群控服务端
  47.     platform:  "AIWROK",
  48.     appName:   "AIWROK_WebSocket客户端",
  49.     debug:     true,
  50.     // 截图默认参数(可以被 CMD_SCREENSHOT.data 覆盖)
  51.     defaultQuality: 0.55,
  52.     maxWidth:   1080,          // 超过就等比缩放,减少带宽
  53.     stopReceiverIntervalMs: 1500
  54. };

  55. // ---------- 工具函数 ----------
  56. function nowSec() {
  57.     return Math.floor((new Date()).getTime() / 1000);
  58. }
  59. function nowMs() {
  60.     return (new Date()).getTime();
  61. }
  62. function jitter(ms) {
  63.     return Math.floor(ms * (0.85 + Math.random() * 0.3));
  64. }
  65. function readTextFile(path) {
  66.     try {
  67.         var br = new java.io.BufferedReader(
  68.             new java.io.InputStreamReader(new java.io.FileInputStream(path), "utf-8"));
  69.         var sb = new java.lang.StringBuilder();
  70.         var line;
  71.         while ((line = br.readLine()) != null) sb.append(line).append("\n");
  72.         br.close();
  73.         return "" + sb.toString();
  74.     } catch (e) { return null; }
  75. }
  76. function deleteFileOrDir(p) {
  77.     try {
  78.         var f = new java.io.File(p);
  79.         if (!f.exists()) return true;
  80.         if (f.isDirectory()) {
  81.             var list = f.listFiles();
  82.             for (var i = 0; i < list.length; i++) deleteFileOrDir(list[i].getAbsolutePath());
  83.         }
  84.         return f["delete"]();
  85.     } catch (e) { return false; }
  86. }
  87. function humanSize(bytes) {
  88.     if (bytes < 1024) return bytes + "B";
  89.     if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + "KB";
  90.     return (bytes / 1024 / 1024).toFixed(2) + "MB";
  91. }

  92. // ---------- 本地缓存封装 ----------
  93. var Cache = {
  94.     BASE: "/sdcard/AIWROK/wscache",
  95.     ensure: function () {
  96.         try {
  97.             var f = new java.io.File(this.BASE);
  98.             if (!f.exists()) f.mkdirs();
  99.         } catch (e) {}
  100.     },
  101.     read: function (key) {
  102.         this.ensure();
  103.         try {
  104.             var p = this.BASE + "/" + encodeURIComponent(key) + ".json";
  105.             return JSON.parse(readTextFile(p));
  106.         } catch (e) { return null; }
  107.     },
  108.     write: function (key, val) {
  109.         this.ensure();
  110.         try {
  111.             var p = this.BASE + "/" + encodeURIComponent(key) + ".json";
  112.             var fw = new java.io.FileWriter(p);
  113.             fw.write(JSON.stringify(val));
  114.             fw.close();
  115.             return true;
  116.         } catch (e) { return false; }
  117.     },
  118.     clear: function (key) {
  119.         try {
  120.             var p = this.BASE + "/" + encodeURIComponent(key) + ".json";
  121.             var f = new java.io.File(p);
  122.             if (f.exists()) f["delete"]();
  123.         } catch (e) {}
  124.     },
  125.     clearAll: function () {
  126.         return deleteFileOrDir(this.BASE);
  127.     }
  128. };

  129. // 尝试恢复 client_id
  130. G.clientId = (Cache.read("wsc_client_id") || {}).id || null;

  131. // ---------- 统一 CMD_RESP ----------
  132. function cmdResp(cmdName, cmdId, ok, msg, data) {
  133.     var obj = {
  134.         type: "CMD_RESP",
  135.         cmd: cmdName,
  136.         cmdId: cmdId || ("cid_" + nowMs() + "_" + Math.floor(Math.random() * 1000)),
  137.         ok: !!ok,
  138.         msg: msg || (ok ? "ok" : "failed"),
  139.         ts: nowSec()
  140.     };
  141.     if (typeof data !== "undefined") obj.data = data;
  142.     if (G.clientId) obj.client_id = G.clientId;
  143.     WSC.send(obj);
  144.     return obj;
  145. }

  146. // ---------- 消息派发器 ----------
  147. var WSC = {
  148.     on: function (type, fn) {
  149.         if (!G.dispatcher[type]) G.dispatcher[type] = [];
  150.         G.dispatcher[type].push(fn);
  151.     },
  152.     off: function (type, fn) {
  153.         var arr = G.dispatcher[type] || [];
  154.         for (var i = arr.length - 1; i >= 0; i--) if (arr[i] === fn) arr.splice(i, 1);
  155.     },
  156.     fire: function (type, msgObj) {
  157.         var arr = G.dispatcher[type] || [];
  158.         for (var i = 0; i < arr.length; i++) {
  159.             try {
  160.                 arr[i](msgObj);
  161.             } catch (e) {
  162.                 printl("派发[" + type + "]异常: " + (e.message || e));
  163.             }
  164.         }
  165.     },
  166.     send: function (obj) {
  167.         if (!G.ws) return false;
  168.         if (G.clientId && obj.clientId == null) obj.client_id = G.clientId;
  169.         if (obj.timestamp == null) obj.timestamp = nowSec();
  170.         if (obj.platform == null) obj.platform = CFG.platform;
  171.         var txt = JSON.stringify(obj);
  172.         try {
  173.             G.ws.send(txt);
  174.             if (CFG.debug) {
  175.                 var head = (obj.type || "?") + " ";
  176.                 // 截图回包 base64 太长,只打摘要
  177.                 if (obj.type === "CMD_RESP" && obj.data && obj.data.base64) {
  178.                     printl("SEND -> " + head + "[" + obj.cmd + "] base64_len=" + obj.data.base64.length + " (" + obj.data.w + "x" + obj.data.h + " q=" + obj.data.quality + ")");
  179.                 } else {
  180.                     printl("SEND -> " + head + txt.slice(0, 160));
  181.                 }
  182.             }
  183.             return true;
  184.         } catch (e) {
  185.             printl("发送失败: " + (e.message || e));
  186.             return false;
  187.         }
  188.     },
  189.     stop: function () {
  190.         G.running = false;
  191.         G.isAllowReConnect = false;
  192.         if (G.heartbeatTimer) {
  193.             try { clearTimeout(G.heartbeatTimer); } catch (e) {}
  194.             G.heartbeatTimer = null;
  195.         }
  196.         try { if (G.ws) G.ws.close(); } catch (e) {}
  197.     }
  198. };

  199. // ---------- 心跳(安全版:链式 setTimeout 全包 try-catch,避免 AIWROK 未捕获异常杀脚本)----------
  200. function startHeartbeat() {
  201.     if (G.heartbeatTimer) {
  202.         try { clearTimeout(G.heartbeatTimer); } catch (e) {}
  203.     }
  204.     (function tick() {
  205.         try {
  206.             if (!G.running) return;
  207.             var imei = "";
  208.             try { if (typeof device !== "undefined") imei = device.getIMEI() || ""; } catch (e) {}
  209.             WSC.send({
  210.                 type: "CLIENT_HEARTBEAT",
  211.                 imei: imei,
  212.                 platform: CFG.platform,
  213.                 app: CFG.appName,
  214.                 runtimeSec: nowSec() - G.startTime,
  215.                 stopAll: !!G.stopAll
  216.             });
  217.         } catch (eOuter) {
  218.             try { printl("心跳异常(已兜住): " + (eOuter.message || eOuter)); } catch (eL) {}
  219.         }
  220.         try {
  221.             G.heartbeatTimer = setTimeout(tick, G.heartbeatIntervalMs);
  222.         } catch (eTimer) {
  223.             try { printl("心跳下一次调度失败: " + (eTimer.message || eTimer)); } catch (eL) {}
  224.         }
  225.     })();
  226. }

  227. // ---------- 设备信息采集(连接时用,也被 CMD_STATUS 复用)----------
  228. function collectDeviceInfo() {
  229.     var info = {
  230.         platform: CFG.platform,
  231.         app: CFG.appName,
  232.         imei: "",
  233.         android: "",
  234.         model: "",
  235.         brand: "",
  236.         w: 1080,
  237.         h: 2400,
  238.         dpi: 0,
  239.         frontPkg: "",
  240.         internalMB: 0,
  241.         sdMB: 0,
  242.         clientId: G.clientId,
  243.         runtimeSec: nowSec() - G.startTime,
  244.         stopAll: !!G.stopAll,
  245.         activeScriptCount: 0
  246.     };
  247.     try { if (typeof device !== "undefined") info.imei = device.getIMEI() || ""; } catch (e) {}
  248.     try { info.android = java.lang.System.getProperty("os.version") + ""; } catch (e) {}
  249.     try { info.model = android.os.Build.MODEL + ""; } catch (e) {}
  250.     try { info.brand = android.os.Build.BRAND + ""; } catch (e) {}
  251.     try {
  252.         if (typeof screen !== "undefined") {
  253.             info.w = screen.getScreenWidth();
  254.             info.h = screen.getScreenHeight();
  255.             try { info.dpi = screen.getDensityDpi(); } catch (e2) {}
  256.         }
  257.     } catch (e) {}
  258.     try {
  259.         var am = context.getSystemService(android.content.Context.ACTIVITY_SERVICE);
  260.         info.frontPkg = am.getRunningTasks(1).get(0).topActivity.getPackageName() + "";
  261.     } catch (e) {}
  262.     try {
  263.         var stat = new android.os.StatFs(android.os.Environment.getDataDirectory().getAbsolutePath());
  264.         info.internalMB = Math.floor((stat.getAvailableBytes() * 1.0) / 1024 / 1024);
  265.     } catch (e) {}
  266.     try {
  267.         var sd = android.os.Environment.getExternalStorageDirectory();
  268.         var s2 = new android.os.StatFs(sd.getAbsolutePath());
  269.         info.sdMB = Math.floor((s2.getAvailableBytes() * 1.0) / 1024 / 1024);
  270.     } catch (e) {}
  271.     var cnt = 0;
  272.     try { for (var k in G.activeThreads) if (G.activeThreads.hasOwnProperty(k)) cnt++; } catch (e) {}
  273.     info.activeScriptCount = cnt;
  274.     return info;
  275. }

  276. // ---------- 事件回调:onConnected ----------
  277. function wsOnConnected() {
  278.     G.reConnectTimes = 0;
  279.     G.connectDelayMs = 3000;
  280.     printl("✓ 连接成功 — 握手完成");

  281.     var info = collectDeviceInfo();
  282.     WSC.send({
  283.         type: "CLIENT_REGISTER",
  284.         platform: info.platform,
  285.         imei: info.imei,
  286.         app: info.app,
  287.         lastClientId: G.clientId,
  288.         screen: info.w + "x" + info.h,
  289.         dpi: info.dpi,
  290.         android: info.android,
  291.         model: info.model,
  292.         brand: info.brand,
  293.         internalMB: info.internalMB,
  294.         sdMB: info.sdMB
  295.     });
  296.     startHeartbeat();
  297.     WSC.fire("_connected", info);
  298. }

  299. // ---------- 事件回调:onTextMessage ----------
  300. function wsOnTextMessage(msg) {
  301.     var s = "" + msg;
  302.     if (CFG.debug) printl("RECV <- " + s.slice(0, 200));
  303.     var obj = null;
  304.     try { obj = JSON.parse(s); } catch (e) {
  305.         printl("非JSON消息,忽略: " + s.slice(0, 120));
  306.         WSC.fire("_text", { raw: s });
  307.         return;
  308.     }
  309.     if (obj && obj.type === "SERVER_INIT") {
  310.         if (obj.client_id) {
  311.             G.clientId = obj.client_id;
  312.             Cache.write("wsc_client_id", { id: G.clientId });
  313.             printl("服务端分配 client_id: " + G.clientId + "(已本地保存)");
  314.         }
  315.         if (obj.msg) printl("服务端消息: " + obj.msg);
  316.     }
  317.     if (obj && obj.type) {
  318.         WSC.fire(obj.type, obj);
  319.         WSC.fire("_any", obj);
  320.     }
  321. }

  322. // ---------- 事件回调:onConnectError ----------
  323. function wsOnConnectError() {
  324.     printl("✗ 连接失败(通常服务端未开启或端口不通)");
  325.     scheduleReconnect();
  326. }

  327. // ---------- 事件回调:onDisconnected ----------
  328. function wsOnDisconnected() {
  329.     printl("✗ 连接已断开");
  330.     if (G.heartbeatTimer) {
  331.         try { clearTimeout(G.heartbeatTimer); } catch (e) {}
  332.         G.heartbeatTimer = null;
  333.     }
  334.     scheduleReconnect();
  335. }

  336. // ---------- 连接 + 事件重挂 ----------
  337. function connectWebSocket() {
  338.     if (G.ws) {
  339.         try { G.ws.close(); } catch (e) {}
  340.         G.ws = null;
  341.     }
  342.     printl("开始连接: " + CFG.serverUrl + "  (重试计数 " + G.reConnectTimes + "/" + G.reConnectMaxTimes + ")");

  343.     var ws = null;
  344.     try { ws = new websocket(); }
  345.     catch (e) {
  346.         printl("new websocket() 失败: " + (e.message || e));
  347.         scheduleReconnect();
  348.         return;
  349.     }

  350.     G.ws = ws;

  351.     // 挂事件:AIWROK 新版 ws.event() 优先,失败兜底属性赋值
  352.     var eventOk = false;
  353.     try {
  354.         ws.event(wsOnConnected, wsOnTextMessage, wsOnConnectError, wsOnDisconnected);
  355.         eventOk = true;
  356.         if (CFG.debug) printl("ws.event() 挂事件成功");
  357.     } catch (e1) {
  358.         if (CFG.debug) printl("ws.event() 失败: " + (e1.message || e1) + " — 尝试属性赋值");
  359.     }
  360.     if (!eventOk) {
  361.         try {
  362.             ws.onConnected = wsOnConnected;
  363.             ws.onTextMessage = wsOnTextMessage;
  364.             ws.onConnectError = wsOnConnectError;
  365.             ws.onDisconnected = wsOnDisconnected;
  366.             if (CFG.debug) printl("属性赋值挂事件成功");
  367.         } catch (e2) {
  368.             printl("两种事件挂法都失败: " + (e2.message || e2));
  369.         }
  370.     }

  371.     // 发起连接:AIWROK connet(双n) 优先,失败回退 connect
  372.     try {
  373.         var ret = null;
  374.         try { ret = ws.connet(CFG.serverUrl); }
  375.         catch (eA) {
  376.             printl("connet() 异常: " + (eA.message || eA) + " — 回退 connect()");
  377.             ret = ws.connect(CFG.serverUrl);
  378.         }
  379.         if (CFG.debug) printl("连接发起返回: " + (ret == null ? "null(表示已发起)" : ret));
  380.     } catch (eB) {
  381.         printl("两种连接方式都失败: " + (eB.message || eB));
  382.         scheduleReconnect();
  383.     }
  384. }

  385. // ---------- 指数退避 + 抖动重连(安全版:setTimeout 最外层包 try-catch)----------
  386. function scheduleReconnect() {
  387.     try {
  388.         if (!G.running) return;
  389.         if (!G.isAllowReConnect) return;
  390.         G.reConnectTimes += 1;
  391.         if (!G.isInfiniteReConnect && G.reConnectTimes > G.reConnectMaxTimes) {
  392.             printl("⚠️  超过最大重试次数(" + G.reConnectMaxTimes + "),停止重连");
  393.             return;
  394.         }
  395.         G.connectDelayMs = Math.min(G.connectDelayMs * G.multiplier, G.maxConnectDelayMs);
  396.         var waitMs = jitter(G.connectDelayMs);
  397.         printl("下次重连等待 " + (waitMs / 1000).toFixed(2) + "s  (封顶" + (G.maxConnectDelayMs / 1000) + "s)");
  398.         setTimeout(function () {
  399.             try { connectWebSocket(); } catch (e) {
  400.                 printl("重连回调异常(已兜住): " + (e.message || e));
  401.                 scheduleReconnect();
  402.             }
  403.         }, waitMs);
  404.     } catch (eOuter) {
  405.         printl("scheduleReconnect 外层异常: " + (eOuter.message || eOuter));
  406.     }
  407. }

  408. // =====================================================================
  409. //  业务指令实现(真正能用的功能,不是空壳)
  410. // =====================================================================

  411. // -------- CMD_SCREENSHOT:截图,base64 返回 -------
  412. // 支持: o.data = { quality:0.3~0.95, maxWidth:1080, full:true/false }
  413. WSC.on("CMD_SCREENSHOT", function (o) {
  414.     var t0 = nowMs();
  415.     var cmdId = o.cmdId;
  416.     try {
  417.         var quality = CFG.defaultQuality;
  418.         if (o && o.data && o.data.quality && o.data.quality > 0 && o.data.quality <= 1) {
  419.             quality = o.data.quality;
  420.         }
  421.         var maxW = CFG.maxWidth;
  422.         if (o && o.data && o.data.maxWidth && o.data.maxWidth > 0) maxW = o.data.maxWidth;

  423.         // 1) fastCapture 截图
  424.         var bmp = null;
  425.         try {
  426.             if (typeof image !== "undefined") {
  427.                 bmp = image.fastCapture();
  428.             } else {
  429.                 cmdResp("CMD_SCREENSHOT", cmdId, false, "image 对象不可用");
  430.                 return;
  431.             }
  432.         } catch (e) {
  433.             cmdResp("CMD_SCREENSHOT", cmdId, false, "fastCapture 异常: " + (e.message || e));
  434.             return;
  435.         }
  436.         if (!bmp) { cmdResp("CMD_SCREENSHOT", cmdId, false, "截图返回空"); return; }

  437.         var w = bmp.getWidth();
  438.         var h = bmp.getHeight();

  439.         // 2) 超宽就等比缩放
  440.         if (w > maxW) {
  441.             var ratio = maxW * 1.0 / w;
  442.             var newH = Math.floor(h * ratio);
  443.             try {
  444.                 var scaled = android.graphics.Bitmap.createScaledBitmap(bmp, maxW, newH, true);
  445.                 try { bmp.recycle(); } catch (e) {}
  446.                 bmp = scaled;
  447.                 w = maxW;
  448.                 h = newH;
  449.             } catch (e2) {}
  450.         }

  451.         // 3) JPEG 压缩转 base64
  452.         var baos = new java.io.ByteArrayOutputStream();
  453.         bmp.compress(android.graphics.Bitmap.CompressFormat.JPEG, Math.floor(quality * 100), baos);
  454.         try { bmp.recycle(); } catch (e) {}
  455.         var bytes = baos.toByteArray();
  456.         var b64 = android.util.Base64.encodeToString(bytes, android.util.Base64.NO_WRAP);
  457.         try { baos.close(); } catch (e) {}

  458.         cmdResp("CMD_SCREENSHOT", cmdId, true, "ok", {
  459.             base64: "" + b64,
  460.             w: w,
  461.             h: h,
  462.             quality: quality,
  463.             sizeKb: Math.floor(bytes.length / 1024),
  464.             costMs: nowMs() - t0
  465.         });
  466.     } catch (eBig) {
  467.         cmdResp("CMD_SCREENSHOT", cmdId, false, "截图总异常: " + (eBig.message || eBig));
  468.     }
  469. });

  470. // -------- CMD_CLICK:点击 {x, y} -------
  471. WSC.on("CMD_CLICK", function (o) {
  472.     var cmdId = o.cmdId;
  473.     var x = (o && o.data && o.data.x != null) ? parseInt(o.data.x, 10) : NaN;
  474.     var y = (o && o.data && o.data.y != null) ? parseInt(o.data.y, 10) : NaN;
  475.     if (isNaN(x) || isNaN(y) || x < 0 || y < 0) {
  476.         cmdResp("CMD_CLICK", cmdId, false, "缺少有效 x/y 坐标");
  477.         return;
  478.     }
  479.     try {
  480.         if (typeof action !== "undefined") {
  481.             action.click(x, y);
  482.             cmdResp("CMD_CLICK", cmdId, true, "ok", { x: x, y: y });
  483.         } else {
  484.             cmdResp("CMD_CLICK", cmdId, false, "action 对象不可用");
  485.         }
  486.     } catch (e) {
  487.         cmdResp("CMD_CLICK", cmdId, false, "点击异常: " + (e.message || e));
  488.     }
  489. });

  490. // -------- CMD_SWIPE:滑屏 {x1,y1,x2,y2,duration} -------
  491. WSC.on("CMD_SWIPE", function (o) {
  492.     var cmdId = o.cmdId;
  493.     var d = o && o.data ? o.data : {};
  494.     var x1 = parseInt(d.x1, 10), y1 = parseInt(d.y1, 10);
  495.     var x2 = parseInt(d.x2, 10), y2 = parseInt(d.y2, 10);
  496.     var dur = d.duration ? parseInt(d.duration, 10) : 300;
  497.     if ([x1, y1, x2, y2].some(function (n) { return isNaN(n) || n < 0; })) {
  498.         cmdResp("CMD_SWIPE", cmdId, false, "坐标缺失");
  499.         return;
  500.     }
  501.     try {
  502.         if (typeof action !== "undefined") {
  503.             action.swipe(x1, y1, x2, y2, dur);
  504.             cmdResp("CMD_SWIPE", cmdId, true, "ok", { x1: x1, y1: y1, x2: x2, y2: y2, duration: dur });
  505.         } else {
  506.             cmdResp("CMD_SWIPE", cmdId, false, "action 对象不可用");
  507.         }
  508.     } catch (e) {
  509.         cmdResp("CMD_SWIPE", cmdId, false, "滑屏异常: " + (e.message || e));
  510.     }
  511. });

  512. // -------- CMD_KEY:按键 -------
  513. // data.key 支持: "BACK" / "HOME" / "RECENT" / "POWER" / "NOTIFICATION" / 数字 keyCode
  514. WSC.on("CMD_KEY", function (o) {
  515.     var cmdId = o.cmdId;
  516.     var key = (o && o.data && o.data.key != null) ? ("" + o.data.key) : "";
  517.     if (!key) { cmdResp("CMD_KEY", cmdId, false, "缺少 key"); return; }
  518.     try {
  519.         // 方式1:action 快捷方法
  520.         if (typeof action !== "undefined") {
  521.             var k = key.toUpperCase();
  522.             if (k === "BACK") { action.back(); cmdResp("CMD_KEY", cmdId, true, "BACK"); return; }
  523.             if (k === "HOME") { action.home(); cmdResp("CMD_KEY", cmdId, true, "HOME"); return; }
  524.             if (k === "RECENT" || k === "RECENTS" || k === "APP_SWITCH") {
  525.                 action.recents(); cmdResp("CMD_KEY", cmdId, true, "RECENT"); return;
  526.             }
  527.         }
  528.         // 方式2:systempress 数字 keyCode
  529.         var code = parseInt(key, 10);
  530.         if (!isNaN(code) && code > 0) {
  531.             if (typeof systempress !== "undefined") {
  532.                 systempress(code);
  533.                 cmdResp("CMD_KEY", cmdId, true, "keyCode " + code);
  534.                 return;
  535.             }
  536.         }
  537.         // 方式3:shell input keyevent(兜底)
  538.         var shellKey = null;
  539.         var k2 = key.toUpperCase();
  540.         if (k2 === "BACK") shellKey = "4";
  541.         else if (k2 === "HOME") shellKey = "3";
  542.         else if (k2 === "RECENT" || k2 === "RECENTS") shellKey = "187";
  543.         else if (k2 === "POWER") shellKey = "26";
  544.         else if (k2 === "NOTIFICATION") shellKey = "83";
  545.         else if (!isNaN(code) && code > 0) shellKey = "" + code;
  546.         if (shellKey) {
  547.             try {
  548.                 java.lang.Runtime.getRuntime().exec(["su", "-c", "input keyevent " + shellKey]).waitFor();
  549.                 cmdResp("CMD_KEY", cmdId, true, "shell key " + key);
  550.                 return;
  551.             } catch (eS) {}
  552.             try {
  553.                 java.lang.Runtime.getRuntime().exec(["input", "keyevent", shellKey]).waitFor();
  554.                 cmdResp("CMD_KEY", cmdId, true, "shell key(no su) " + key);
  555.                 return;
  556.             } catch (eS2) {}
  557.         }
  558.         cmdResp("CMD_KEY", cmdId, false, "不支持的 key: " + key);
  559.     } catch (e) {
  560.         cmdResp("CMD_KEY", cmdId, false, "按键异常: " + (e.message || e));
  561.     }
  562. });

  563. // -------- CMD_RUN_SCRIPT:远程跑脚本 -------
  564. // data = { path, timeoutSec, args }
  565. // 返回 { threadId, pid, ok },后续可查线程状态
  566. WSC.on("CMD_RUN_SCRIPT", function (o) {
  567.     var cmdId = o.cmdId;
  568.     var d = o && o.data ? o.data : {};
  569.     var path = (d.path || "").trim();
  570.     var timeoutSec = d.timeoutSec ? parseInt(d.timeoutSec, 10) : 600;
  571.     if (!path) { cmdResp("CMD_RUN_SCRIPT", cmdId, false, "缺少脚本 path"); return; }

  572.     // 路径支持两种:绝对路径,或相对于 /sdcard/AIWROK/
  573.     var fullPath = path;
  574.     if (!new java.io.File(path).isAbsolute()) {
  575.         fullPath = "/sdcard/AIWROK/" + path;
  576.     }
  577.     if (!new java.io.File(fullPath).exists()) {
  578.         cmdResp("CMD_RUN_SCRIPT", cmdId, false, "脚本不存在: " + fullPath);
  579.         return;
  580.     }

  581.     try {
  582.         // AIWROK 约束:用 thread.runJsFile() 加载外部脚本
  583.         if (typeof thread === "undefined") {
  584.             cmdResp("CMD_RUN_SCRIPT", cmdId, false, "thread 对象不可用");
  585.             return;
  586.         }
  587.         var tid = "T" + nowMs() + "_" + Math.floor(Math.random() * 10000);
  588.         // 构造成员:把 client_id、CMD_RUN_SCRIPT 的 args、STOP_ALL 标志路径放进脚本共享变量
  589.         try { scriptShared = { CMD_ARGS: d.args || {}, STOP_FLAG: G.STOP_FLAG, CLIENT_ID: G.clientId, CMD_ID: cmdId }; } catch (eSh) {}

  590.         var th = thread.runJsFile(fullPath);
  591.         G.activeThreads[tid] = { thread: th, path: fullPath, startAt: nowSec(), timeoutSec: timeoutSec, cmdId: cmdId };

  592.         // 守护协程:非阻塞链式 setTimeout 轮询(不占用派发线程,避免 AIWROK 杀脚本)
  593.         (function watcherStep() {
  594.             try {
  595.                 var rec = G.activeThreads[tid];
  596.                 if (!rec) return; // 已经被 STOP_ALL 清理掉了
  597.                 var stillAlive = true;
  598.                 try {
  599.                     // isAlive 在部分 AIWROK 版本里是字段不是方法,两种都兜住
  600.                     if (th && typeof th.isAlive === "function") stillAlive = th.isAlive();
  601.                     else if (th && typeof th.isAlive === "boolean") stillAlive = th.isAlive;
  602.                     else stillAlive = true;
  603.                 } catch (eChk) { stillAlive = false; }

  604.                 var runSec = nowSec() - rec.startAt;
  605.                 var timeIsUp = runSec >= (rec.timeoutSec + 1);
  606.                 var shouldStop = !stillAlive || timeIsUp || G.stopAll;

  607.                 if (!shouldStop) {
  608.                     setTimeout(watcherStep, 1000);
  609.                     return;
  610.                 }

  611.                 // 超时或 STOP_ALL -> 停线程
  612.                 if (stillAlive && timeIsUp) {
  613.                     try { if (th && th.stop) th.stop(); } catch (eStop) {}
  614.                 }
  615.                 var cost = nowSec() - rec.startAt;
  616.                 try { delete G.activeThreads[tid]; } catch (eDel) {}
  617.                 WSC.send({
  618.                     type: "CLIENT_SCRIPT_RESULT",
  619.                     cmdId: cmdId,
  620.                     threadId: tid,
  621.                     path: fullPath,
  622.                     timeout: timeIsUp && stillAlive,
  623.                     runtimeSec: cost,
  624.                     stopAll: !!G.stopAll,
  625.                     ts: nowSec()
  626.                 });
  627.             } catch (eOuter) {
  628.                 printl("CMD_RUN_SCRIPT 守护协程异常(已兜住): " + (eOuter.message || eOuter));
  629.             }
  630.         })();

  631.         cmdResp("CMD_RUN_SCRIPT", cmdId, true, "已启动", { threadId: tid, path: fullPath, timeoutSec: timeoutSec });
  632.     } catch (e) {
  633.         cmdResp("CMD_RUN_SCRIPT", cmdId, false, "启动异常: " + (e.message || e));
  634.     }
  635. });

  636. // -------- CMD_STATUS:查状态 -------
  637. WSC.on("CMD_STATUS", function (o) {
  638.     try {
  639.         var info = collectDeviceInfo();
  640.         // 列活跃线程
  641.         var threads = [];
  642.         try {
  643.             for (var k in G.activeThreads) if (G.activeThreads.hasOwnProperty(k)) {
  644.                 var t = G.activeThreads[k];
  645.                 threads.push({
  646.                     id: k,
  647.                     path: t.path,
  648.                     runSec: nowSec() - t.startAt,
  649.                     timeoutSec: t.timeoutSec
  650.                 });
  651.             }
  652.         } catch (e) {}
  653.         info.threads = threads;
  654.         cmdResp("CMD_STATUS", o && o.cmdId, true, "ok", info);
  655.     } catch (e) {
  656.         cmdResp("CMD_STATUS", o && o.cmdId, false, "状态异常: " + (e.message || e));
  657.     }
  658. });

  659. // -------- CMD_TOAST:弹个消息 -------
  660. WSC.on("CMD_TOAST", function (o) {
  661.     var cmdId = o && o.cmdId;
  662.     var msg = (o && o.data && o.data.msg != null) ? ("" + o.data.msg) : "";
  663.     if (!msg) { cmdResp("CMD_TOAST", cmdId, false, "缺少 msg"); return; }
  664.     try {
  665.         if (typeof toast !== "undefined") { toast(msg); }
  666.         cmdResp("CMD_TOAST", cmdId, true, "ok", { msg: msg });
  667.     } catch (e) {
  668.         cmdResp("CMD_TOAST", cmdId, false, "toast 异常: " + (e.message || e));
  669.     }
  670. });

  671. // -------- CMD_CLEAR_CACHE:清除本地缓存 -------
  672. WSC.on("CMD_CLEAR_CACHE", function (o) {
  673.     var cmdId = o && o.cmdId;
  674.     try {
  675.         Cache.clearAll();
  676.         // 顺便删 STOP_ALL 标志
  677.         try {
  678.             var f2 = new java.io.File(G.STOP_FLAG);
  679.             if (f2.exists()) f2["delete"]();
  680.         } catch (e2) {}
  681.         G.stopAll = false;
  682.         cmdResp("CMD_CLEAR_CACHE", cmdId, true, "ok");
  683.     } catch (e) {
  684.         cmdResp("CMD_CLEAR_CACHE", cmdId, false, "清除异常: " + (e.message || e));
  685.     }
  686. });

  687. // -------- MANAGE_TASK_NEW:兼容原任务下发格式 -------
  688. // 如果 data.action === "run_script" 就自动走 CMD_RUN_SCRIPT 逻辑;否则打印并回 ACK
  689. WSC.on("MANAGE_TASK_NEW", function (o) {
  690.     printl("[任务] 收到: " + JSON.stringify(o.data || o).slice(0, 160));
  691.     try {
  692.         var d = o.data || o;
  693.         if (d && d.action === "run_script" && d.path) {
  694.             WSC.fire("CMD_RUN_SCRIPT", { type: "CMD_RUN_SCRIPT", cmdId: (o.taskId || ("task_" + nowMs())), data: d });
  695.             WSC.send({ type: "CLIENT_TASK_ACK", taskId: o.taskId || (d.taskId || ""), accepted: true, mode: "auto_run_script", data: d });
  696.         } else {
  697.             WSC.send({ type: "CLIENT_TASK_ACK", taskId: o.taskId || (d && d.taskId || ""), accepted: true });
  698.         }
  699.     } catch (e) {
  700.         WSC.send({ type: "CLIENT_TASK_ACK", taskId: o.taskId || "", accepted: false, error: (e.message || e) });
  701.     }
  702. });

  703. // -------- MANAGE_CMD_STOP_ALL:真停止逻辑(不是只写个文件就完事)-------
  704. // 动作:1) 设标志 + 写文件 2) 停止所有活跃线程 3) 心跳停 4) 上报 CLIENT_STOPPING 5) 2 秒后 WSC.stop()
  705. WSC.on("MANAGE_CMD_STOP_ALL", function (o) {
  706.     printl("[指令] STOP_ALL 广播,开始完整清理");
  707.     try { G.stopAll = true; } catch (e) {}
  708.     try { G.isAllowReConnect = false; } catch (e) {}

  709.     // 1) 写标志文件(供其他脚本读)
  710.     try {
  711.         var f = new java.io.File(G.STOP_FLAG);
  712.         if (!f.exists()) f.createNewFile();
  713.     } catch (e) {}

  714.     // 2) 停止所有 CMD_RUN_SCRIPT 启动的活跃线程
  715.     var stoppedCnt = 0;
  716.     try {
  717.         for (var k in G.activeThreads) if (G.activeThreads.hasOwnProperty(k)) {
  718.             try {
  719.                 var tObj = G.activeThreads[k];
  720.                 if (tObj && tObj.thread && tObj.thread.stop) tObj.thread.stop();
  721.                 stoppedCnt++;
  722.             } catch (eSt) {}
  723.         }
  724.     } catch (e) {}
  725.     try { G.activeThreads = {}; } catch (e) {}

  726.     // 3) 立即发 CLIENT_STOPPING
  727.     try {
  728.         WSC.send({
  729.             type: "CLIENT_STOPPING",
  730.             reason: "MANAGE_CMD_STOP_ALL",
  731.             stoppedThreads: stoppedCnt,
  732.             runtimeSec: nowSec() - G.startTime,
  733.             ts: nowSec()
  734.         });
  735.     } catch (eS) {}

  736.     // 4) 2 秒后彻底关 WS 与保活(链式 setTimeout 全部兜 try-catch,避免 AIWROK 判"异常未捕获"直接杀)
  737.     try {
  738.         setTimeout(function () {
  739.             try {
  740.                 if (G.heartbeatTimer) { clearTimeout(G.heartbeatTimer); G.heartbeatTimer = null; }
  741.             } catch (e1) {}
  742.             try { WSC.stop(); } catch (e2) {}
  743.             try { printl("[指令] STOP_ALL 清理完成,已断开 WS"); } catch (e3) {}
  744.         }, 2000);
  745.     } catch (eOuter) {}
  746. });

  747. // -------- SERVER_PING:心跳回包 -------
  748. WSC.on("SERVER_PING", function (o) {
  749.     WSC.send({
  750.         type: "CLIENT_PONG",
  751.         echo: o,
  752.         runtimeSec: nowSec() - G.startTime,
  753.         ts: nowSec()
  754.     });
  755. });

  756. // =====================================================================
  757. //  远程停止接收专用线程(硬约束要求)
  758. //  单独轮询 STOP_ALL 标志文件;一旦存在就触发完整停止逻辑
  759. // =====================================================================
  760. function startStopReceiver() {
  761.     (function loop() {
  762.         try {
  763.             if (!G.running) return;
  764.             var f = new java.io.File(G.STOP_FLAG);
  765.             if (f.exists() && !G.stopAll) {
  766.                 printl("[接收器] 检测到 STOP_ALL 标志文件,触发停止");
  767.                 try { f["delete"](); } catch (eDel) {}
  768.                 // 模拟收到一条 MANAGE_CMD_STOP_ALL 消息,走同一套清理逻辑
  769.                 WSC.fire("MANAGE_CMD_STOP_ALL", { type: "MANAGE_CMD_STOP_ALL", from: "local_flag" });
  770.             }
  771.         } catch (eOuter) {
  772.             try { printl("[接收器] 轮询异常(已兜住): " + (eOuter.message || eOuter)); } catch (eL) {}
  773.         }
  774.         try { setTimeout(loop, CFG.stopReceiverIntervalMs); }
  775.         catch (eTimer) {
  776.             // runTime.setTimeout 兜底(仅当 setTimeout 不存在时用)
  777.             try { runTime.setTimeout(loop, CFG.stopReceiverIntervalMs); } catch (eRT) {}
  778.         }
  779.     })();
  780. }

  781. // ---------- 启动入口(全局 try-catch + setTimeout 不阻塞,杜绝 AIWROK 随便『正在停止』)----------
  782. function main() {
  783.     try {
  784.         // 启动先清一次 STOP_ALL 标志,避免上一次的残余
  785.         try {
  786.             var _f = new java.io.File(G.STOP_FLAG);
  787.             if (_f.exists()) _f["delete"]();
  788.         } catch (e) {}
  789.         G.stopAll = false;

  790.         printl("==========================================");
  791.         printl("  AIWROK WebSocket 客户端 启动");
  792.         printl("  平台: " + CFG.platform + "   App: " + CFG.appName);
  793.         printl("  服务端: " + CFG.serverUrl);
  794.         printl("  恢复 client_id: " + (G.clientId || "新注册"));
  795.         printl("  可用指令: CMD_SCREENSHOT / CMD_CLICK / CMD_SWIPE / CMD_KEY");
  796.         printl("           CMD_RUN_SCRIPT / CMD_STATUS / CMD_TOAST / CMD_CLEAR_CACHE");
  797.         printl("==========================================");
  798.         connectWebSocket();
  799.         startStopReceiver();

  800.         // 主线程保活(链式 setTimeout,非阻塞,全包 try-catch)
  801.         var keepAliveTick = 0;
  802.         (function keepAlive() {
  803.             try {
  804.                 if (!G.running) { printl("保活线程退出"); return; }
  805.                 keepAliveTick++;
  806.                 if (keepAliveTick % 20 === 0) {
  807.                     printl("[保活] client_id=" + (G.clientId || "N/A") + " 重试=" + G.reConnectTimes + " 运行=" + (nowSec() - G.startTime) + "s stopAll=" + G.stopAll);
  808.                 }
  809.             } catch (eInner) {
  810.                 try { printl("[保活] 一次循环异常(已兜住): " + (eInner.message || eInner)); } catch (eL) {}
  811.             }
  812.             try { setTimeout(keepAlive, 1000); }
  813.             catch (eTimer) {
  814.                 try { runTime.setTimeout(keepAlive, 1000); } catch (eRT) {}
  815.             }
  816.         })();
  817.     } catch (eTop) {
  818.         // 任何 main 里未捕获异常都兜住,不让 AIWROK 直接给『正在停止』
  819.         try { printl("⚠️  main() 顶层异常(已兜住): " + (eTop.message || eTop)); } catch (eL) {}
  820.         // 尝试继续重连
  821.         try { setTimeout(function () { try { connectWebSocket(); } catch (e) {} }, 5000); } catch (e) {}
  822.     }
  823. }

  824. main();
复制代码



回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

相关导读
群发软件⛓️WebSocket带截图/点击/滑屏/按键/跑脚本/状态查询
⛓️WebSocket带截图/点击/滑屏/按键/跑脚本/状态查询
群发软件七猫阅读赚钱自动刷框架
七猫阅读赚钱自动刷框架
群发软件websocket远程停止手机接收器
websocket远程停止手机接收器
群发软件AIWROK软件本地搭建OCR找字服务器
AIWROK软件本地搭建OCR找字服务器
群发软件本地调试版原生websocket版
本地调试版原生websocket版
群发软件无障碍方式双指向中间滑动放大缩小完整示例
无障碍方式双指向中间滑动放大缩小完整示例
群发软件AIWROK双指向中间滑动捏合手势完整示例
AIWROK双指向中间滑动捏合手势完整示例
群发软件实战使用桌面控制WebSocket
实战使用桌面控制WebSocket
群发软件WebSocket调试okhttp版
WebSocket调试okhttp版
群发软件快手极速版自动刷视频
快手极速版自动刷视频
群发软件极速视频APP刷金币
极速视频APP刷金币
群发软件语音合成核心模块AIWROK 单文件版
语音合成核心模块AIWROK 单文件版
群发软件AIWROK软件自动文字转语音例子
AIWROK软件自动文字转语音例子
群发软件AIWROK adb.exec() 实用工具库 —— 常用功能直接调用版
AIWROK adb.exec() 实用工具库 —— 常用功能直接调用版
群发软件安卓AIWROK获取包名所有方法汇总
安卓AIWROK获取包名所有方法汇总
群发软件AIWROK软件番茄小说自动阅读刷金币示例
AIWROK软件番茄小说自动阅读刷金币示例
信息发布软件AIWROK软件无碍模式一键删除所有APP版示例
AIWROK软件无碍模式一键删除所有APP版示例
信息发布软件AIWROK软件无碍模式auto模块抖音极速版滑动刷视频
AIWROK软件无碍模式auto模块抖音极速版滑动刷视频
信息发布软件快手极速版自动刷视频无障碍模式
快手极速版自动刷视频无障碍模式
信息发布软件AIWROK软件代理模式一键删除桌面所有APP版示例
AIWROK软件代理模式一键删除桌面所有APP版示例
信息发布软件AIWROK软件自动找图利用agent代理模式删除应用版示例
AIWROK软件自动找图利用agent代理模式删除应用版示例
信息发布软件解析Kimi返回的拖动距离示例
解析Kimi返回的拖动距离示例
信息发布软件自动截图并发送给DEEPSEEK识别示例过程
自动截图并发送给DEEPSEEK识别示例过程
信息发布软件AIWROK软件测试文心识别识别验证码示例
AIWROK软件测试文心识别识别验证码示例
信息发布软件元宝截图B站验证码示例测试
元宝截图B站验证码示例测试
信息发布软件示例千问自动采集文章保存记事本
示例千问自动采集文章保存记事本
信息发布软件豆包识别验证码加拖动验证码示例
豆包识别验证码加拖动验证码示例
信息发布软件豆包对话采集示例
豆包对话采集示例
信息发布软件启动自动百度贴吧发图片
启动自动百度贴吧发图片
信息发布软件自动百度贴吧示例
自动百度贴吧示例
信息发布软件代理模式简书自动流程
代理模式简书自动流程
信息发布软件启动极速版任务页看小说赚钱自动阅读
启动极速版任务页看小说赚钱自动阅读
信息发布软件agent代理模式功能小结
agent代理模式功能小结
信息发布软件完整苹果IOS小脚本文本输入与粘贴流程
完整苹果IOS小脚本文本输入与粘贴流程
信息发布软件AIWROK使用activity列表开关与进度条的协同交互
AIWROK使用activity列表开关与进度条的协同交互
群发软件苹果脚本找图后Detect对象各种方法演示
苹果脚本找图后Detect对象各种方法演示
群发软件苹果脚本网络API监控与数据管理工具
苹果脚本网络API监控与数据管理工具
群发软件IOS苹果脚本TXT文件读写H5日志打印完整示例
IOS苹果脚本TXT文件读写H5日志打印完整示例
群发软件AIWROK软件演示多线程创建暂停恢复
AIWROK软件演示多线程创建暂停恢复
群发软件AIWROK软件定时检查页面状态防止卡死技巧
AIWROK软件定时检查页面状态防止卡死技巧
群发软件安卓脚本runTime相关功能示例
安卓脚本runTime相关功能示例
群发软件floatUI实例秒显示秒关闭吐司功能
floatUI实例秒显示秒关闭吐司功能
群发软件苹果脚本HID多行实例粘帖板示例
苹果脚本HID多行实例粘帖板示例
群发软件安卓脚本HID节点查找完整方法示例
HID节点查找完整方法示例
群发软件AIWROK软件框架的抖音自动化营销系统
AIWROK软件框架的抖音自动化营销系统
群发软件赛博朋克任务控制中心H5示例
赛博朋克任务控制中心H5示例
群发软件苹果脚本现代深色UI设计风格
现代深色UI设计风格
群发软件AIWROK软件智能任务监控悬浮窗
AIWROK软件智能任务监控悬浮窗
群发软件AIWROK软件事件监听示例
AIWROK软件事件监听示例
群发软件QZ掘金原生UI界面
QZ掘金原生UI界面
群发软件安卓脚本auto方法完整示例
安卓脚本auto方法完整示例
群发软件安卓脚本暂停弹出确认对话框
安卓脚本暂停弹出确认对话框
群发软件苹果iOS脚本天气信息查询工具
苹果iOS脚本天气信息查询工具
群发软件安卓脚本简化版H5日志输出窗口
安卓脚本简化版H5日志输出窗口
群发软件安卓脚本简化版H5日志输出窗口
安卓脚本简化版H5日志输出窗口
群发软件安卓脚本应用重置、代理模式监控及补丁升级
安卓脚本应用重置、代理模式监控及补丁升级
群发软件安卓脚本里的打开关闭安装以及卡密授权信息
安卓脚本里的打开关闭安装以及卡密授权信息
群发软件AIWROK软件脚本GET下载文件并检测0KB工具
AIWROK软件脚本GET下载文件并检测0KB工具
群发软件安卓脚本HID按键方法完整示例
安卓脚本HID按键方法完整示例
群发软件AIWROK软件键鼠HID滑动所有方法功能演示
AIWROK软件键鼠HID滑动所有方法功能演示
群发软件苹果脚本里HID的5种滑动方法的使用
苹果脚本里HID的5种滑动方法的使用
群发软件苹果脚本示例7种不同的OCR识别和点击方法
苹果脚本示例7种不同的OCR识别和点击方法
群发软件AIWROK软件安卓脚本FTP上传下载例子
AIWROK软件安卓脚本FTP上传下载例子
群发软件AIWROK软件脚本JSON转换示例
AIWROK软件脚本JSON转换示例
群发软件AIWROK软件运算符高级应用实例
AIWROK软件运算符高级应用实例

QQ|( 京ICP备09078825号 )

本网站信息发布软件,是可以发布论坛,发送信息到各大博客,各大b2b软件自动发布,好不夸张的说:只要手工能发在电脑打开IE能发的网站,用这个宣传软件就可以仿制动作,进行推送发到您想发送的B2B网站或是信息发布平台上,不管是后台,还是前台,都可以进行最方便的广告发布,这个广告发布软件,可以按月购买,还可以试用软件,对网站的验证码也可以完全自动对信息发布,让客户自动找上门,使企业轻松实现b2b发布,这个信息发布软件,均是本站原创正版开发,拥有正版的血统,想要新功能,欢迎提意见给我,一好的分类信息群发软件在手,舍我其谁。QQ896757558

GMT+8, 2026-8-25 07:41 , Processed in 0.587593 second(s), 49 queries .

宣传软件--信息发布软件--b2b软件广告发布软件

快速回复 返回顶部 返回列表