node-red-contrib-voice-assistant
Version:
一个小度-小爱-猫精音箱控制NODE-RED自定义设备的节点
251 lines (250 loc) • 9.52 kB
JavaScript
"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 __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const os_1 = __importDefault(require("os"));
const got_1 = __importDefault(require("got"));
const decompress_zip_1 = __importDefault(require("decompress-zip"));
const ini_1 = __importDefault(require("ini"));
const child_process_1 = require("child_process");
const sleep = (ms) => {
return new Promise((resolve) => setTimeout(resolve, ms));
};
class FrpClient {
constructor() {
this.url = '';
this.cacheUrl = path_1.default.join(__dirname, 'frpc.zip');
this.target = path_1.default.join(__dirname, 'frpc');
this.closeFlag = false;
this.url = this.getCdnUrl();
this.cacheUrl = path_1.default.join(__dirname, 'frpc.zip');
//frp路径
this.target = path_1.default.join(__dirname, 'frpc');
this.config = {
common: {
server_addr: 'yyddyym.top',
server_port: 7000,
pritoken: '',
},
};
}
getCdnUrl() {
const arch = os_1.default.platform() + os_1.default.arch();
const cdn = 'https://hasskit.com/';
const cdnPath = 'public/frp/';
const cdnFiles = {
linuxarm: cdn + cdnPath + 'linux-arm.zip',
linuxarm64: cdn + cdnPath + 'linux-arm64.zip',
linuxia32: cdn + cdnPath + 'linux-386.zip',
linuxx64: cdn + cdnPath + 'linux-amd64.zip',
};
const url = cdnFiles[arch];
if (!url) {
console.error('frpc - platform ' + arch + ' is not supported.');
return;
}
return url;
}
download() {
const downloadStream = got_1.default.stream(this.url + '');
const fileWriteStream = fs_1.default.createWriteStream(this.cacheUrl);
return new Promise((resolve, reject) => {
downloadStream
.on('downloadProgress', (info) => {
this._ondownloadProgress && this._ondownloadProgress((info.percent.toFixed(2) * 100).toString());
})
.on('error', (err) => {
resolve(null);
});
fileWriteStream.on('finish', () => {
resolve(true);
console.log('下载完成');
});
downloadStream.pipe(fileWriteStream);
});
}
set onDownloadProgress(fn) {
this._ondownloadProgress = fn;
}
removeFrp() {
fs_1.default.unlinkSync(this.target);
}
//解包
extract() {
return new Promise((resolve) => {
const zip = new decompress_zip_1.default(this.cacheUrl);
zip.extract({ path: __dirname });
zip.once('error', error);
zip.once('extract', () => {
fs_1.default.chmodSync(this.target, 755);
if (!fs_1.default.existsSync(this.target) || fs_1.default.statSync(this.target).size <= 0) {
return error(new Error('corrupted file ' + this.target));
}
console.log('frp - binary unpacked to ' + this.target);
resolve(null);
fs_1.default.unlinkSync(this.cacheUrl);
});
function error(e) {
console.error('ngrok - error unpacking binary', e);
resolve(e);
}
});
}
//frp运行
createSection(ports, options) {
options.forEach((it, index) => {
if (index <= ports.length - 1) {
this.config[ports[index].proxyName] = {
type: 'tcp',
local_ip: it.url,
local_port: it.port,
remote_port: ports[index].port,
};
}
});
}
createConfig() {
fs_1.default.writeFileSync(path_1.default.join(__dirname, 'frpc.ini'), ini_1.default.stringify(this.config));
}
handleData(data) {
const obj = { code: 0, msg: data, tunnel_addr: '', local_addr: '' };
console.log('handleData', data);
const proxyname = /\[([0-9a-z]{32})\]/.exec(data);
if (data.includes('service.go:104')) {
obj.code = 401;
obj.msg = 'frp连接失败';
}
else if (data.includes('service.go:244')) {
obj.code = 244;
obj.msg = 'frp连接成功';
}
else if (data.includes('control.go:162')) {
obj.code = 401;
obj.local_addr = this.config[proxyname[1]].local_ip + ':' + this.config[proxyname[1]].local_port;
obj.msg = '远程端口占用';
}
else if (data.includes('control.go:164')) {
//console.log('164', proxyname, data);
obj.local_addr = this.config[proxyname[1]].local_ip + ':' + this.config[proxyname[1]].local_port;
obj.tunnel_addr = this.config.common.server_addr + ':' + this.config[proxyname[1]].remote_port;
obj.code = 200;
obj.msg = '端口映射成功';
}
return obj;
}
setup(node) {
return __awaiter(this, void 0, void 0, function* () {
/* console.log(node.options)
node.options = [
{ url: '192.168.1.132', port: 8123 },
{ url: '192.168.1.132', port: 1880 },
]; */
for (let i = 0; i < node.options.length; i++) {
if (node.options[i].url.trim() === '' && node.options[i].port.trim() === '') {
return { msg: '映射参数未配配置' };
}
}
const topic = node.brokerConn.mqttClient.pubTopic + '/getFrpData';
node.brokerConn.mqttClient._client.publish(topic, `{"id":"${(Math.random() * 42949672950000000).toString(16)}"}`);
yield sleep(1000);
if (!this.authObj) {
console.log('认证失败');
return { msg: '获取参数失败' };
}
this.createSection(this.authObj.frpcPort, node.options);
this.config.common.pritoken = this.authObj.token;
this.createConfig();
yield sleep(500);
if (!fs_1.default.existsSync(this.target) || fs_1.default.statSync(this.target).size <= 0) {
this.onDownloadProgress = function (msg) {
node.status({ fill: 'green', shape: 'dot', text: '下载进度:' + msg + '%' });
};
if (!this.url)
return;
const status = yield this.download();
if (!status) {
node.status({ fill: 'red', shape: 'dot', text: '下载失败' });
this.removeFrp();
return;
}
const extract = yield this.extract();
if (!extract) {
return;
}
else {
this.removeFrp();
return { msg: '解包失败' };
}
}
return;
});
}
run(cb) {
this.child = (0, child_process_1.spawn)(this.target, ['-c', path_1.default.join(__dirname, 'frpc.ini')]);
this.child.on('error', function (err) {
console.log('error', err);
});
this.child.on('close', (code) => {
this.emitClose && this.emitClose();
});
this.child.on('exit', () => {
cb && cb({ code: 104, msg: 'frp断开连接' });
if (!this.closeFlag)
setTimeout(() => {
this.run(cb);
}, 3000);
});
this.child.stdout.on('data', (msg) => {
msg.toString()
.trim()
.split('\n')
.forEach((it) => {
const msgObj = this.handleData(it);
cb && cb(msgObj);
});
});
}
start(cb) {
try {
this.run(cb);
return true;
}
catch (_a) {
console.log(_a);
return false;
}
}
reconnect() {
this.close(() => {
setTimeout(() => {
this.start(null);
}, 3000);
});
}
close(done) {
try {
(0, child_process_1.spawn)('kill', ['-9', this.child.pid.toString()]);
this.closeFlag = true;
if (done) {
this.emitClose();
done();
}
}
catch (_a) {
done && done();
}
}
}
exports.default = FrpClient;