|
|
@@ -0,0 +1,487 @@ |
|
|
|
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('<Forwarding name="port2" proto="1" hostip="127.0.0.1" hostport="')[1].slice(0,5); |
|
|
|
|
|
|
|
this.port_table[port] = temp.name; |
|
|
|
this.title_to_device_table[temp.name] = port; |
|
|
|
} |
|
|
|
// console.log(this.nox_table); |
|
|
|
// console.log(this.port_table); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* adb 初始化, adb 操作時,必做喔 |
|
|
|
*/ |
|
|
|
async adb_init(){ |
|
|
|
// var killServer = spawnSync(this.adb_file, ["kill-server"] ,{encoding: "utf-8"}); |
|
|
|
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"; |
|
|
|
} |
|
|
|
} |
|
|
|
let startServer = await this.exec_adb( ["start-server"] ); |
|
|
|
if(startServer.err){ |
|
|
|
console.log("start出錯啦~"); |
|
|
|
console.log(startServer); |
|
|
|
scanf("%d"); |
|
|
|
return "err"; |
|
|
|
} |
|
|
|
console.log("初始化完成~ 天佑冠霆啦!!"); |
|
|
|
} |
|
|
|
|
|
|
|
scanNox(){ |
|
|
|
return new Promise( (resolve, reject) => { |
|
|
|
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); |