UNPKG

@zigbang/honeyfarm-node

Version:

## Raspberry Pi HoneyFarm Node ### Setup && Run

536 lines (535 loc) 28.8 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.node = void 0; var shelljs_1 = __importDefault(require("shelljs")); var child_process_1 = __importDefault(require("child_process")); var commander_1 = __importDefault(require("commander")); var logger_1 = __importDefault(require("./logger")); var api_1 = __importDefault(require("./api")); var bms_1 = __importDefault(require("./bms")); var types_1 = require("./util/types"); var upnp_1 = __importDefault(require("./upnp")); var node = /** @class */ (function () { function node() { this.resources = {}; this.maxDevices = 16; this.portMap = {}; this.isMacMachine = false; this.appiumDefaultPort = 4724; this.useUPnP = false; this.endpoint = "http://localhost:4723"; this.wdaDefaultPort = 8101; this.mjpegServerDefaultPort = 9101; this.iosDevice = undefined; this.CHECK_BATTERY_TERM = 5 * 60 * 1000; // 5min } node.prototype.run = function () { return __awaiter(this, void 0, void 0, function () { var _this = this; return __generator(this, function (_a) { switch (_a.label) { case 0: shelljs_1.default.config.silent = true; commander_1.default .option('--endpoint <endpoint>', 'Setting endpoint') .option('--appiumBeginPort <appiumBeginPort>', 'Setting appium Begin port') .option('--upnp', 'Enable upnp mode') .action(function () { _this.endpoint = commander_1.default.endpoint || _this.endpoint; _this.appiumDefaultPort = Number.parseInt(commander_1.default.appiumBeginPort) || _this.appiumDefaultPort; _this.useUPnP = commander_1.default.upnp || _this.useUPnP; }) .parse(process.argv); this.api = new api_1.default(this.endpoint); this.bms = new bms_1.default(); return [4 /*yield*/, this.init()]; case 1: _a.sent(); return [4 /*yield*/, this.updateDeviceStatus()]; case 2: _a.sent(); this.checkBatteryStatus(); setInterval(function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { this.checkBatteryStatus(); this.updateBmsTimerStates(); return [2 /*return*/]; }); }); }, this.CHECK_BATTERY_TERM); return [2 /*return*/]; } }); }); }; node.prototype.checkBatteryStatus = function () { return __awaiter(this, void 0, void 0, function () { var rule; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.api.getBatteryChargeRule()]; case 1: rule = _a.sent(); if (rule && true === rule.hasOwnProperty("threshold")) { this.bms.setBatteryChargeLevelRange(rule.threshold.min, rule.threshold.max); } else { logger_1.default.warn("Invalid battery threshold value: ".concat(rule)); } this.bms.checkBatteryStatus(this.resources); return [2 /*return*/]; } }); }); }; node.prototype.init = function () { return __awaiter(this, void 0, void 0, function () { var i, _a; return __generator(this, function (_b) { switch (_b.label) { case 0: for (i = 0; i < this.maxDevices; i++) { this.portMap[this.appiumDefaultPort + i] = "FREE"; } if (!(process.platform === "darwin")) return [3 /*break*/, 2]; this.isMacMachine = true; _a = this; return [4 /*yield*/, this.getIosDeviceList()]; case 1: _a.iosDevice = _b.sent(); _b.label = 2; case 2: return [2 /*return*/]; } }); }); }; node.prototype.getIosDeviceList = function () { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, new Promise(function (resolve, reject) { var nid = require("node-ios-device"); nid.devices(function (err, devices) { if (err) { reject(err); } else { resolve(devices); } }); })]; }); }); }; node.prototype.updateDeviceStatus = function () { return __awaiter(this, void 0, void 0, function () { var onlineSerials, unregisteredDevices; var _this = this; return __generator(this, function (_a) { switch (_a.label) { case 0: if (!this.useUPnP) return [3 /*break*/, 3]; return [4 /*yield*/, upnp_1.default.getExternalIp()]; case 1: _a.sent(); return [4 /*yield*/, upnp_1.default.getMappedPort()]; case 2: _a.sent(); _a.label = 3; case 3: return [4 /*yield*/, this.getOnlineSerials()]; case 4: onlineSerials = _a.sent(); logger_1.default.info("Discovered Devices: ".concat(JSON.stringify(onlineSerials))); // 현재 등록된 디바이스 중 연결되지 않는 디바이스 제거 return [4 /*yield*/, Promise.all(Object.keys(this.resources) .filter(function (serial) { return !Object.keys(onlineSerials).includes(serial); }) .map(function (serial) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.removeDevice(serial)]; case 1: _a.sent(); return [2 /*return*/]; } }); }); })) // 현재 연결된 디바이스 중 클라이언트에 등록되지 않았을 경우 ]; case 5: // 현재 등록된 디바이스 중 연결되지 않는 디바이스 제거 _a.sent(); // 현재 연결된 디바이스 중 클라이언트에 등록되지 않았을 경우 return [4 /*yield*/, Promise.all(Object.entries(onlineSerials).map(function (_a) { var key = _a[0], value = _a[1]; return __awaiter(_this, void 0, void 0, function () { var resource, checkAppiumServer; return __generator(this, function (_b) { switch (_b.label) { case 0: if (!!this.resources[key]) return [3 /*break*/, 2]; return [4 /*yield*/, this.addDeviceToLocal(key, value)]; case 1: _b.sent(); return [3 /*break*/, 3]; case 2: resource = this.resources[key]; checkAppiumServer = shelljs_1.default.exec("lsof -i :".concat(resource.port, " -t")).stdout; if (!checkAppiumServer) this.startAppiumServer(key, resource.port, resource.wdaPort); _b.label = 3; case 3: if (value === "android") { this.updateBatteryLevel(key); } return [2 /*return*/]; } }); }); })) // 클라이언트에 등록된 디바이스 중 서버에 등록되지 않은 경우 ]; case 6: // 현재 연결된 디바이스 중 클라이언트에 등록되지 않았을 경우 _a.sent(); return [4 /*yield*/, Promise.all(Object.entries(this.resources).map(function (_a) { var serial = _a[0], device = _a[1]; return __awaiter(_this, void 0, void 0, function () { var exist; return __generator(this, function (_b) { switch (_b.label) { case 0: return [4 /*yield*/, this.api.getDevice(device.port, device.platform, device.version, serial)]; case 1: exist = _b.sent(); if (!exist) { return [2 /*return*/, serial]; } else { return [2 /*return*/, undefined]; } return [2 /*return*/]; } }); }); }))]; case 7: unregisteredDevices = _a.sent(); return [4 /*yield*/, Promise.all(unregisteredDevices.map(function (serial) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: if (!serial) return [3 /*break*/, 2]; return [4 /*yield*/, this.addDeviceToServer(serial)]; case 1: _a.sent(); _a.label = 2; case 2: return [2 /*return*/]; } }); }); }))]; case 8: _a.sent(); setTimeout(function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.updateDeviceStatus()]; case 1: _a.sent(); return [2 /*return*/]; } }); }); }, 10000); return [2 /*return*/]; } }); }); }; node.prototype.updateBmsTimerStates = function () { return __awaiter(this, void 0, void 0, function () { var rst; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.api.postBmsTimerState(this.bms.timer.get_enabled_timers_())]; case 1: rst = _a.sent(); if (rst && 'ok' === rst.result_code) { //success // Logger.info(`BMS timer status is updated.`) } else { logger_1.default.warn("Fail to update BMS timer status: ".concat(JSON.stringify(rst))); } return [2 /*return*/]; } }); }); }; node.prototype.updateBatteryLevel = function (udid) { return __awaiter(this, void 0, void 0, function () { var remaining, batteryLevel, vals, port; return __generator(this, function (_a) { switch (_a.label) { case 0: remaining = -1; batteryLevel = shelljs_1.default.exec("adb -s ".concat(udid, " shell dumpsys battery | grep level")).stdout; if (batteryLevel.includes("level")) { vals = batteryLevel.split(":"); if (vals.length === 2) { remaining = parseInt(vals[1]); } } this.resources[udid].batteryLevel = remaining; port = this.resources[udid].port; console.log("batteryLevel : ", this.resources[udid]); return [4 /*yield*/, this.api.updateDeviceStatus(port, { batteryLevel: remaining })]; case 1: _a.sent(); return [2 /*return*/]; } }); }); }; node.prototype.getOnlineSerials = function () { return __awaiter(this, void 0, void 0, function () { var serials, _a, devices, onlineDevices; return __generator(this, function (_b) { switch (_b.label) { case 0: serials = {}; if (!this.isMacMachine) return [3 /*break*/, 2]; _a = this; return [4 /*yield*/, this.getIosDeviceList()]; case 1: _a.iosDevice = _b.sent(); this.iosDevice.map(function (device) { serials[device.udid] = "ios"; }); this.getOnlineSimulator().map(function (device) { serials[device.udid] = "ios"; }); _b.label = 2; case 2: devices = shelljs_1.default.exec("adb devices | grep -v devices").stdout; onlineDevices = devices.match(/(\w+-?\w+?)\tdevice$/gm) || []; onlineDevices.map(function (device) { serials[device.replace("\tdevice", "")] = "android"; }); return [2 /*return*/, serials]; } }); }); }; node.prototype.getOnlineSimulator = function () { var result = []; var data = shelljs_1.default.exec("xcrun simctl list devices --json"); var deviceList = JSON.parse(data.toString()); for (var _i = 0, _a = Object.entries(deviceList.devices); _i < _a.length; _i++) { var _b = _a[_i], version = _b[0], devices = _b[1]; if (!version.includes("iOS")) { continue; } for (var _c = 0, _d = devices; _c < _d.length; _c++) { var device = _d[_c]; if (device.state === "Booted") { var versionRex = version === null || version === void 0 ? void 0 : version.match(/\w+-\d+-\d+/g); result.push({ udid: device.udid, name: device.name, productVersion: versionRex ? versionRex[0].replace("iOS-", "").replace("-", ".") : "", type: "simulator" }); } } } return result; }; node.prototype.addDeviceToLocal = function (serial, platform) { var _a; return __awaiter(this, void 0, void 0, function () { var _b, result, port, _, wdaPort, mjpegServerPort, devicelist, deviceInfo, name_1, platformVersion, webviewVersion; return __generator(this, function (_c) { switch (_c.label) { case 0: if (!(platform === "ios")) return [3 /*break*/, 2]; _b = this; return [4 /*yield*/, this.getIosDeviceList()]; case 1: _b.iosDevice = _c.sent(); _c.label = 2; case 2: result = Object.entries(this.portMap).find(function (_a) { var port = _a[0], status = _a[1]; return status === "FREE"; }); if (!result) { logger_1.default.error("All Ports Already in USED"); return [2 /*return*/]; } port = result[0], _ = result[1]; wdaPort = this.wdaDefaultPort + Number(port) - this.appiumDefaultPort; mjpegServerPort = this.mjpegServerDefaultPort + Number(port) - this.appiumDefaultPort; try { this.startAppiumServer(serial, port, wdaPort.toString()); if (platform === "ios") { devicelist = this.iosDevice.concat(this.getOnlineSimulator()); deviceInfo = devicelist.filter(function (device) { return device.udid === serial; }); name_1 = ((_a = deviceInfo[0]) === null || _a === void 0 ? void 0 : _a.productType) ? types_1.iPhone_TYPE[deviceInfo[0].productType] : deviceInfo[0].name; this.resources[serial] = { platform: platform, version: deviceInfo[0].productVersion, port: port, name: name_1, wdaPort: "".concat(wdaPort), mjpegServerPort: "".concat(mjpegServerPort), type: deviceInfo[0].type }; } else { platformVersion = shelljs_1.default.exec("adb -s ".concat(serial, " shell getprop ro.build.version.release")).stdout.replace("\n", "").trim(); webviewVersion = shelljs_1.default.exec("adb -s ".concat(serial, " shell dumpsys package com.android.chrome | grep versionName")) .stdout.split("\n")[0] .replace("versionName=", "") .match(/\d+\.\d+\.\d+/)[0]; this.resources[serial] = { platform: platform, version: platformVersion, port: port, webviewVersion: webviewVersion }; } this.portMap[port] = "USED"; logger_1.default.info("Add Device(Local) ".concat(serial, " to Port ").concat(port)); } catch (e) { logger_1.default.error("Error while Add Device to Local. Removing ".concat(serial)); logger_1.default.error(e); this.removeDevice(serial); } return [2 /*return*/]; } }); }); }; node.prototype.startAppiumServer = function (serial, port, wdaPort) { return __awaiter(this, void 0, void 0, function () { var appiumOptions, appium, e_1; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 3, , 4]); logger_1.default.info("Starting Appium Server with Port:".concat(port)); appiumOptions = ["-p", "".concat(port), "--webdriveragent-port", "".concat(wdaPort), "--relaxed-security"]; appium = child_process_1.default.spawn("appium", appiumOptions, { detached: true }); appium.stdout.on("data", function (data) { logger_1.default.info(data); }); if (!this.useUPnP) return [3 /*break*/, 2]; return [4 /*yield*/, upnp_1.default.addPortMapping(Number(port), 60 * 60 * 24 * 30)]; case 1: _a.sent(); _a.label = 2; case 2: return [3 /*break*/, 4]; case 3: e_1 = _a.sent(); logger_1.default.error("Error while Add Device to Local. Removing ".concat(serial)); logger_1.default.error(e_1); this.removeDevice(serial); return [3 /*break*/, 4]; case 4: return [2 /*return*/]; } }); }); }; node.prototype.addDeviceToServer = function (serial) { return __awaiter(this, void 0, void 0, function () { var device, e_2; return __generator(this, function (_a) { switch (_a.label) { case 0: device = this.resources[serial]; if (!device) { logger_1.default.error("Trying to Add Device that are not registerd to local ".concat(serial)); return [2 /*return*/]; } _a.label = 1; case 1: _a.trys.push([1, 3, , 4]); logger_1.default.info("Adding Device(Server) ".concat(serial, " to Port ").concat(device.port)); return [4 /*yield*/, this.api.registerDeviceStatus(device.port, device.platform, device.version, serial, device.name, device.wdaPort, device.mjpegServerPort, device.type, device.batteryLevel)]; case 2: _a.sent(); logger_1.default.info("Added Device(Server) ".concat(serial, " to Port ").concat(device.port)); return [3 /*break*/, 4]; case 3: e_2 = _a.sent(); logger_1.default.error("Error while Add Device to Server"); logger_1.default.error(e_2); return [3 /*break*/, 4]; case 4: return [2 /*return*/]; } }); }); }; node.prototype.removeDevice = function (serial) { return __awaiter(this, void 0, void 0, function () { var port, appiumPID, e_3; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, , 3]); port = this.resources[serial].port; logger_1.default.warn("Remove Device: ".concat(serial, " from port: ").concat(port)); return [4 /*yield*/, this.api.deregisterDeviceStatus(port)]; case 1: _a.sent(); delete this.resources[serial]; this.portMap[port] = "FREE"; appiumPID = shelljs_1.default.exec("lsof -n -i4TCP:".concat(port, " | grep node | awk '{print $2}'")); child_process_1.default.exec("kill -9 ".concat(appiumPID)); if (this.useUPnP) { upnp_1.default.removePortMapping(Number(port)); } return [3 /*break*/, 3]; case 2: e_3 = _a.sent(); logger_1.default.error(e_3); return [3 /*break*/, 3]; case 3: return [2 /*return*/]; } }); }); }; return node; }()); exports.node = node; new node().run();