node-red-contrib-voice-assistant
Version:
一个小度-小爱-猫精音箱控制NODE-RED自定义设备的节点
207 lines (198 loc) • 6.84 kB
JavaScript
const fs = require('fs');
const path = require('path');
const os = require('os');
const got = require('got');
const Zip = require('decompress-zip');
const uuid = require('uuid');
const ini = require('ini');
const { spawn } = require('child_process');
class FrpClient {
constructor() {
this.url = this.getCdnUrl();
this._ondownloadProgress = [];
this.cacheUrl = path.join(__dirname, 'frpc.zip');
//frp路径
this.target = path.join(__dirname, 'frpc');
this.config = {
common: {
server_addr: '47.110.232.79',
server_port: 7000,
},
};
}
getCdnUrl() {
const arch = os.platform() + os.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.stream(this.url + '');
const fileWriteStream = fs.createWriteStream(this.cacheUrl);
return new Promise((resolve, reject) => {
downloadStream
.on('downloadProgress', (info) => {
this._ondownloadProgress && this._ondownloadProgress(info.percent.toFixed(2) * 100);
})
.on('error', (err) => {
resolve();
});
fileWriteStream.on('finish', () => {
resolve(true);
console.log('下载完成');
});
downloadStream.pipe(fileWriteStream);
});
}
set onDownloadProgress(fn) {
this._ondownloadProgress = fn;
}
removeFrp() {
fs.unlinkSync(this.target);
}
//解包
extract() {
return new Promise((resolve) => {
new Zip(this.cacheUrl)
.extract({ path: __dirname })
.once('error', error)
.once('extract', () => {
fs.chmodSync(this.target, 755);
if (!fs.existsSync(this.target) || fs.statSync(this.target).size <= 0) {
return error(new Error('corrupted file ' + this.target));
}
console.log('frp - binary unpacked to ' + this.target);
resolve(null);
fs.unlinkSync(this.cacheUrl);
});
function error(e) {
console.error('ngrok - error unpacking binary', e);
resolve(e);
}
});
}
//frp运行
createSection(local_addr = '127.0.0.1', local_port = 0, remote_port = 0) {
const section = (0, uuid.v4)().replace(/-/g, '');
this.config[section] = {
type: 'tcp',
local_ip: local_addr,
local_port: local_port,
remote_port: remote_port,
};
return section;
}
createConfig() {
fs.writeFileSync(path.join(__dirname, 'frpc.ini'), ini.stringify(this.config));
}
handleData(data) {
const obj = { code: 0, msg: data };
if (data.includes('service.go:104')) {
obj.code = 104;
obj.msg = 'frp连接失败';
} else if (data.includes('service.go:301')) {
obj.code = 301;
obj.msg = 'frp连接成功';
} else if (data.includes('control.go:178')) {
obj.code = 178;
obj.msg = '端口占用';
} else if (data.includes('control.go:180')) {
obj.code = 180;
obj.msg = '端口映射成功';
}
return obj;
}
async setup(node) {
node.options = [
{ url: '127.0.0.1', port: 8123 },
{ url: '127.0.0.1', port: 1880 },
];
node.options.forEach((it) => {
this.createSection(it.url, it.port, 10004);
});
this.createConfig();
if (!fs.existsSync(this.target) || fs.statSync(this.target).size <= 0) {
this.onDownloadProgress = function (msg) {
node.status({ fill: 'green', shape: 'dot', text: '下载进度:' + msg + '%' });
};
if (!this.url) return;
const status = await this.download();
if (!status) {
node.status({ fill: 'red', shape: 'dot', text: '下载失败' });
this.removeFrp();
return;
}
const extract = await this.extract();
if (!extract) {
console.log('解包成功');
return;
} else {
this.removeFrp();
}
}
}
run(cb) {
this.child = spawn(this.target, ['-c', path.join(__dirname, 'frpc.ini')]);
this.child.on('error', function (err) {
console.log('error', err);
});
this.child.on('close', (code) => {
console.log(code);
this.emitClose && this.emitClose();
this.close();
});
this.child.stdout.on('data', (msg) => {
const msgObj = this.handleData(msg.toString().trim());
cb && cb(msgObj);
});
}
start(cb) {
try {
this.run(cb);
return true;
} catch (_a) {
console.log(_a);
return false;
}
}
close(done) {
this.connect = false;
try {
spawn('kill', ['-9', child.pid.toString()]);
if (done) {
this.emitClose = done;
done();
}
} catch (_a) {
done && done();
}
}
}
module.exports = FrpClient;
/* (async () => {
const frpClient = new FrpClient();
if (!fs.existsSync(frpClient.target) || fs.statSync(frpClient.target).size <= 0) {
frpClient.onDownloadProgress = function (msg) {
console.log('下载进度:', msg, '%');
};
if (!frpClient.url) return;
const status = await frpClient.download();
if (!status) return;
const extract = await frpClient.extract();
if (!extract) console.log('解包成功');
}
frpClient.createSection(undefined, 1880, 10004);
frpClient.createConfig();
frpClient.start();
})(); */