const fs = require('fs'); const spawn = require('child_process').spawn; const spawnSync = require('child_process').spawnSync; const path = require('path'); const scanf = require('scanf'); const HTMLParser = require('node-html-parser'); const hw = require(path.join(__dirname, 'GTBOT.node')); /** * option: { * nox_path: E:/nox/Nox, * open_count: 2, * close_time: 1000(ms) * } */ class GT_Nox_Command{ /** * @param {String} noxName 夜神內部Id e.g. Nox_1,Nox_2 * @param {String} noxPath 夜神的安裝位置, 預設為 C:/Program Files (x86)/Nox */ constructor(titleTable,option){ this.titleToNoxIdTable = titleTable; if(typeof option !== "object") option = {}; this.noxPath = option.nox_path?option.nox_path:"C:/Program Files (x86)/Nox"; this.openCount = option.open_count?option.open_count:1; this.closeTime = option.close_time?option.close_time:1000; this.nox_command_file = path.join(this.noxPath,"bin","Nox.exe"); this.errorExec = []; } nox_command(titleName,option){ let argv_arr = ["-clone:" + this.titleToNoxIdTable[titleName] ]; if(typeof option === "object"){ for(let i in option){ let temp = "-" + i + ":" + option[i]; argv_arr[argv_arr.length] = temp; } } else if(typeof option === "string"){ argv_arr[argv_arr.length] = option; } let cmd = spawn(this.nox_command_file, argv_arr, { detached: true }); cmd.stdout.on('data', (data) => { // console.log(`stdout: \n${data}`); }); cmd.stderr.on('data', (data) => { // console.log(`stderr: ${data}`); }); cmd.on('close', (code) => { // console.log(`child process exited with code $[code]`); }); } static checkOpen(title,cb){ let count = 0; let intervalTemp = setInterval(function(){ let mainW = hw.findWindowEx(0, 0, "Qt5QWindowIcon" , title); if(mainW){ // init 時會有一個 title 是""的東西 let check1 = hw.findWindowEx(mainW, 0, "Qt5QWindowIcon" , ''); let check2 = hw.findWindowEx(mainW, 0, "Qt5QWindowIcon" , 'default_title_barWindow'); if(check1 === check2){ if(++count > 3){ clearInterval(intervalTemp); cb(); } } } },1000); } open_process(titleName){ if(this.titleToNoxIdTable[titleName] === undefined){ this.errorExec[this.errorExec.length] = titleName; return; } this.nox_command(titleName); GT_Nox_Command.checkOpen(titleName,function(){ this.finish_open(); }.bind(this)); } finish_open(){ if(this.open_arr.length <= 0){ let msg = {}; if(this.errorExec.length > 0){ msg.err = this.errorExec.slice(0);; } this.finish_open_cb(msg); this.errorExec = []; return; } else{ let nextOpen = this.open_arr.shift(); this.open_process(nextOpen); } } /** * 開啟模擬器 * @param {Array || String} titleNameArr 要開啟哪些APK * @param {Function} cb 開完了,你要做啥 * option{ * cpu:1, * memory: 960(or 1024), * resolution: '320x240', * dpi: 120, * root:false * } */ openNox(titleNameArr,cb){ let titleArr; if( Array.isArray(titleNameArr) ){ titleArr = titleNameArr; } else if(typeof titleNameArr === "string"){ titleArr = [titleNameArr] } this.open_arr = titleArr; this.finish_open_cb = cb; for(let i=0;i < this.openCount;i++){ let nextOpen = this.open_arr.shift(); this.open_process(nextOpen,i); if(this.open_arr.length === 0){ break; } } } colse_process(closeTitle){ if(this.titleToNoxIdTable[closeTitle] === undefined){ this.errorExec[this.errorExec.length] = closeTitle; return; } this.nox_command(closeTitle,"-quit"); } /** * 關閉模擬器 * @param {Array || String} titleNameArr 要關哪些APK * @param {Function} cb 關完了,你要做啥 */ closeNox(titleNameArr,cb){ let titleArr; if( Array.isArray(titleNameArr) ){ titleArr = titleNameArr; } else if(typeof titleNameArr === "string"){ titleArr = [titleNameArr] } // console.log(cb); this.activeFlage = true; let closeCount = 1; this.colse_process(titleArr[0]); let closeItl = setInterval(function(){ this.colse_process(titleArr[closeCount]); closeCount++; if(closeCount >= titleArr.length){ clearInterval(closeItl); let msg = {}; if(this.errorExec.length > 0){ msg.err = this.errorExec.slice(0);; } this.errorExec = []; if(typeof cb === 'function'){ cb(msg); } } }.bind(this),this.closeTime); } restartNox(titleNameArr,cb){ let titleArr; if( Array.isArray(titleNameArr) ){ titleArr = titleNameArr; } else if(typeof titleNameArr === "string"){ titleArr = [titleNameArr] } this.closeNox(titleArr,function(){ setTimeout(()=>{ this.openNox(titleArr,cb); },5000); }.bind(this)); } /** * 安裝APK * @param {String} apk_path apk 的絕對位置 */ install_apk(apk_path){ if(typeof apk_path !== "string"){ console.log("你的apk path呢!"); return; } this.nox_command("'-apk:" + apk_path + "'"); } /** * 執行某個App,預設開托蘭 * @param {string} appId app的唯一Id */ run_app(appId){ if(appId === undefined){ appId = "com.asobimo.toramonline"; } this.nox_command({package:appId}); } } class adb_ctrl{ constructor(option){ this.nox_path = option.nox_path?option.nox_path:"C:/Program Files (x86)/Nox"; this.adb_file = path.join(this.nox_path,"bin","nox_adb.exe"); this.setMultiNoxList.call(this); // title 對應 Nox_id /** * { * atk01: Nox_1 * } */ // this.nox_table = {}; // port 對應 title /** * { * 61025: Nox_1 * } */ // this.port_table = {}; this.nox_cmd = new GT_Nox_Command(this.nox_table,option); // this.connect(); } openNox(titleArr,cb){ this.nox_cmd.openNox.call(this.nox_cmd,titleArr,cb); } closeNox(titleArr,cb){ this.nox_cmd.closeNox.call(this.nox_cmd,titleArr,cb); } restartNox(titleArr,cb){ this.nox_cmd.restartNox.call(this.nox_cmd,titleArr,cb); } exec_adb_cb(argv,cb){ const status = spawn(this.adb_file, argv); status.stdout.setEncoding('utf8'); status.stderr.setEncoding('utf8'); let stdout = ""; let err = false; let errInfo = ""; status.stdout.on('data', (data) => { stdout += (data + "||"); }); status.stderr.on('data', (data) => { errInfo += (data + "||"); }); status.on('close', (code) => { cb({ err: err, errInfo: errInfo, data: stdout }); }); } exec_adb(argv){ return new Promise( (resolve, reject) => { const status = spawn(this.adb_file, argv); status.stdout.setEncoding('utf8'); status.stderr.setEncoding('utf8'); let stdout; status.stdout.on('data', (data) => { stdout = data; // console.log(data); }); status.stderr.on('data', (data) => { // console.log("err",data); resolve({ err: true, data: data }); }); status.on('close', (code) => { resolve({ err: false, data:stdout }); }); }); } delay(time){ return new Promise( (resolve, reject) => { setTimeout(function(){ resolve(); },time); }); } /** * 找multiNox 路徑 */ setMultiNoxList(){ let multi_list; let multi_file = path.join(process.env.LOCALAPPDATA,'MultiPlayerManager','multiplayer.xml'); try{ multi_list = fs.readFileSync(multi_file,{encoding:"utf-8"}); } catch(e){ console.log(e); console.log("找不到夜神多工器啦 冠霆要你滾"); scanf("%d"); } if(multi_list === undefined){ return; } let document = HTMLParser.parse(multi_list); let Instance = document.querySelectorAll("Instance"); this.nox_table = {}; this.port_table = {}; this.title_to_device_table = {}; for(let i=0;i < Instance.length;i++){ let temp = Instance[i].attributes; if(temp.id === "Nox_0"){ temp.id = "nox"; } this.nox_table[temp.name] = temp.id; let yee = fs.readFileSync(this.nox_path + '/bin/BignoxVMS/' + temp.id + '/' + temp.id + '.vbox',{encoding:"utf-8"}); let port = yee.split(' { let count = 0; let max = Object.keys(this.port_table).length; // console.log("scan囉"); // console.log(Object.keys(this.port_table).length); function afterScan(data){ count++; // console.log(count); // console.log(data); if(max === count){ resolve(); } } for(let i in this.port_table){ this.exec_adb_cb( ["connect","127.0.0.1:" + i] ,afterScan); } }); } /** * 連結目前所有已開的夜神模擬器 */ async connect(){ await this.adb_init(); if(this.port_table === undefined){ return "err"; } await this.scanNox(); var devices = await this.exec_adb( ["devices"] ); // console.log(devices); if(devices.err){ console.log("devices出錯啦~"); console.log(devices); scanf("%d"); return "err"; } let all_devices = devices.data.split("List of devices attached\r\n")[1]; let devices_list = all_devices.split("\tdevice\r\n"); devices_list = devices_list.reverse().slice(1); this.devices_list = devices_list; this.devices_count = devices_list.length; this.installed_count = 0; // console.log(devices_list); // console.log("你要更新的裝置數量:" + this.devices_count); } async disconnect(){ let killServer = await this.exec_adb(["kill-server"]); if(killServer.err){ if(killServer.data.indexOf("server not running") === -1){ console.log("kill出錯啦~"); console.log(killServer); scanf("%d"); return "err"; } } } /** * 停止toram跟cloneApp 的App */ async close_toram(titleName){ let device = this.title_to_device_table[titleName]; if(device === undefined) return {err: "noThis"}; // for(let i=0; i < this.devices_count;i++){ // let status = await this.exec_adb( ["-s", this.devices_list[i] ,"shell","dumpsys activity | grep top-activity"] ); let status = await this.exec_adb( ["-s", "127.0.0.1:" + device ,"shell","am","force-stop","com.asobimo.toramonline"] ); status = await this.exec_adb( ["-s", "127.0.0.1:" + device ,"shell","am","force-stop","com.zhuowang.cloneapp"] ); return {}; // } // this.nox_cmd.run_app("com.zhuowang.cloneapp:platform.gameplugin.P00"); // this.nox_cmd.run_app(); } } module.exports = adb_ctrl; // let aa = new adb_ctrl({ // nox_path: "F:/Nox", // open_count: 1, // }); // let atkArr = []; // for(let i=1;i< 3;i++){ // atkArr[atkArr.length] = "atk0" + i; // } // async function yee(gtadb){ // // await gtadb.adb_init.call(gtadb); // await gtadb.connect(); // await gtadb.close_toram("atk01"); // await gtadb.close_toram("atk02"); // } // yee(aa);