2022-04-14 16:51:47 +08:00

579 lines
18 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const fs = require('fs');
const os = require('os');
const child_process = require("child_process")
const iconv = require('iconv-lite')
const electron = require('electron')
const {
NodeVM,
VM
} = require('./lib/vm2')
const path = require("path")
const axios = require('axios');
const pictureCompress = require("./lib/picture-compressor")
window._ = require("lodash")
// axios.defaults.adapter = require('axios/lib/adapters/http')
if (!utools.isWindows()) process.env.PATH += ':/usr/local/bin:/usr/local/sbin'
// window.startTime = new Date().getTime()
const shortCodes = [
open = path => {
utools.shellOpenItem(path)
},
locate = path => {
utools.shellShowItemInFolder(path);
},
visit = url => {
utools.shellOpenExternal(url);
},
system = cmd => {
child_process.exec(cmd);
},
message = msg => {
utools.showNotification(msg)
},
keyTap = (key, ...modifier) => utools.simulateKeyboardTap(key, ...modifier),
copyTo = text => {
electron.clipboard.writeText(text)
},
send = text => {
copyTo(text);
quickcommand.simulatePaste();
}
]
const ctlKey = utools.isMacOs() ? 'command' : 'control'
window.quickcommand = {
// 模拟复制操作
simulateCopy: function() {
utools.simulateKeyboardTap('c', ctlKey);
},
// 模拟粘贴操作
simulatePaste: function() {
utools.simulateKeyboardTap('v', ctlKey);
},
// setTimout 不能在 vm2 中使用,同时在 electron 中有 bug
sleep: function(ms) {
var start = new Date().getTime()
try {
// node 16.13.1
child_process.execSync(getSleepCodeByShell(ms), {
timeout: ms,
windowsHide: true
})
} catch (ex) {}
var end = new Date().getTime()
return (end - start)
},
// 重写 setTimeout
setTimeout: function(callback, ms) {
var start = new Date().getTime()
child_process.exec(getSleepCodeByShell(ms), {
timeout: ms
}, (err, stdout, stderr) => {
var end = new Date().getTime()
callback(end - start)
})
},
// 关闭进程
kill: function(pid, signal = 'SIGTERM') {
process.kill(pid, signal)
},
// dom 解析
htmlParse: function(html) {
return new DOMParser().parseFromString(html, 'text/html')
},
// 下载文件
downloadFile: function(url, file = {}) {
return new Promise((reslove, reject) => {
if (file instanceof Object) file = utools.showSaveDialog(JSON.parse(JSON.stringify(file)))
axios({
method: 'get',
url: url,
responseType: 'arraybuffer'
}).then(res => {
var filebuffer = Buffer.from(res.data)
fs.writeFile(file, filebuffer, err => {
if (err) reject(err)
else reslove(filebuffer)
})
}).catch(err => {
reject(err)
})
})
},
// 上传文件
uploadFile: function(url, file = {}, name = 'file', formData = {}) {
return new Promise((reslove, reject) => {
var objfile
if (file instanceof File) {
objfile = file
} else {
if (file instanceof Object) file = utools.showOpenDialog(JSON.parse(JSON.stringify(file)))[0]
if (!fs.existsSync(file)) return reject('文件不存在')
var arraybuffer = fs.readFileSync(file).buffer
var objfile = new File([arraybuffer], path.basename(file))
}
var form = new FormData();
form.append(name, objfile)
var keys = Object.keys(formData)
if (keys.length) keys.forEach(k => form.append(k, formData[k]))
axios.post(url, form, {
headers: {
'accept': 'application/json',
'Content-Type': `multipart/form-data; boundary=${formData._boundary}`,
}
}).then(res => {
reslove(res)
}).catch(err => {
reject(err)
})
})
},
// 载入在线资源
loadRemoteScript: async function(url, forceUpdate = false) {
if (!/^((ht|f)tps?):\/\/([\w\-]+(\.[\w\-]+)*\/)*[\w\-]+(\.[\w\-]+)*\/?(\?([\w\-\.,@?^=%&:\/~\+#]*)+)?/.test(url)) throw 'url 不合法'
let remote = url
let root = path.join(os.tmpdir(), 'qcRemoteScript')
if (!fs.existsSync(root)) fs.mkdirSync(root)
let local = path.join(root, require('crypto').createHash('md5').update(url).digest('hex'))
if (forceUpdate || !fs.existsSync(local)) await this.downloadFile(remote, local)
return require(local)
}
}
// 运行vbs脚本
if (process.platform == 'win32') quickcommand.runVbs = function(script) {
return new Promise((reslove, reject) => {
var tempfile = path.join(os.tmpdir(), 'TempVBSScript.vbs')
fs.writeFile(tempfile, iconv.encode(script, 'gbk'), err => {
child_process.exec(`cscript.exe /nologo "${tempfile}"`, {
encoding: "buffer"
}, (err, stdout, stderr) => {
if (err) reject(iconv.decode(stderr, 'gbk'))
else reslove(iconv.decode(stdout, 'gbk'))
});
})
})
}
window.temporaryStore = {
listeners: {}
}
window.temporaryStoreSoldOut = () => {
_.forIn(temporaryStore.listeners, (listener, key) => {
document.removeEventListener(...listener)
})
window.temporaryStore = {
listeners: {}
}
}
// python -c
window.runPythonCommand = py => {
try {
let result = child_process.execFileSync("python", ["-c", py], {
windowsHide: true,
encoding: 'buffer'
})
return iconv.decode(result, utools.isWindows() ? 'gbk' : 'utf8').trim()
} catch (e) {
alert(e)
return ""
}
}
// 在终端中执行
if (process.platform !== 'linux') quickcommand.runInTerminal = function(cmdline, dir) {
let command = getCommandToLaunchTerminal(cmdline, dir)
child_process.exec(command)
}
let getCommandToLaunchTerminal = (cmdline, dir) => {
let cd = ''
if (utools.isWindows()) {
let appPath = path.join(utools.getPath('home'), '/AppData/Local/Microsoft/WindowsApps/')
// 直接 existsSync wt.exe 无效
if (fs.existsSync(appPath) && fs.readdirSync(appPath).includes('wt.exe')) {
cmdline = cmdline.replace(/"/g, `\\"`)
if (dir) cd = `-d "${dir.replace(/\\/g, '/')}"`
command = `${appPath}wt.exe ${cd} cmd /k "${cmdline}"`
} else {
cmdline = cmdline.replace(/"/g, `^"`)
if (dir) cd = `cd /d "${dir.replace(/\\/g, '/')}" &&`
command = `${cd} start "" cmd /k "${cmdline}"`
}
} else {
cmdline = cmdline.replace(/"/g, `\\"`)
if (dir) cd = `cd ${dir.replace(/ /g, `\\\\ `)} &&`
if (fs.existsSync('/Applications/iTerm.app')) {
command = `osascript -e 'tell application "iTerm"
create window with default profile
tell current session of current window to write text "clear && ${cd} ${cmdline}"
end tell'`
} else {
command = `osascript -e 'tell application "Terminal"
do script "clear && ${cd} ${cmdline}"
activate
end tell'`
}
}
console.log(command);
return command
}
window.pluginInfo = () => {
return JSON.parse(fs.readFileSync(path.join(__dirname, 'plugin.json')))
}
let getSleepCodeByShell = ms => {
var cmd, tempFilePath
if (utools.isWindows()) {
tempFilePath = getQuickcommandTempFile('vbs')
cmd = `echo set ws=CreateObject("Wscript.Shell") > ${tempFilePath} && echo Wscript.sleep ${ms} >> ${tempFilePath} && cscript /nologo ${tempFilePath}`
} else {
cmd = `sleep ${ms / 1000}`
}
return cmd
}
// 屏蔽危险函数
window.getuToolsLite = () => {
var utoolsLite = Object.assign({}, utools)
if (utools.isDev()) return utoolsLite
// 数据库相关接口
delete utoolsLite.db
delete utoolsLite.dbStorage
delete utoolsLite.removeFeature
delete utoolsLite.setFeature
delete utoolsLite.onDbPull
// 支付相关接口
delete utoolsLite.fetchUserServerTemporaryToken
delete utoolsLite.getUserServerTemporaryToken
delete utoolsLite.openPayment
delete utoolsLite.fetchUserPayments
return utoolsLite
}
let getSandboxFuns = () => {
var sandbox = {
utools: getuToolsLite(),
quickcommand: quickcommand,
electron: electron,
axios: axios,
Audio: Audio,
fetch: fetch
}
shortCodes.forEach(f => {
sandbox[f.name] = f
})
return sandbox
}
let createNodeVM = () => {
var sandbox = getSandboxFuns()
const vm = new NodeVM({
require: {
external: true,
builtin: ["*"],
},
console: 'redirect',
env: process.env,
sandbox: sandbox,
});
return vm
}
let stringifyAll = item => {
var cache = [];
var string = JSON.stringify(item, (key, value) => {
if (typeof value === 'object' && value !== null) {
if (cache.indexOf(value) !== -1) return
cache.push(value);
}
return value;
}, '\t')
if (string != "{}") return string
else return item.toString()
}
let parseItem = item => {
if (typeof item == "object") {
if (Buffer.isBuffer(item)) {
var bufferString = `[Buffer ${item.slice(0, 50).toString('hex').match(/\w{1,2}/g).join(" ")}`
if (item.length > 50) bufferString += `... ${(item.length / 1000).toFixed(2)}kb`
return bufferString + ']'
} else if (item instanceof ArrayBuffer) {
return `ArrayBuffer(${item.byteLength})`
} else if (item instanceof Blob) {
return `Blob {size: ${item.size}, type: "${item.type}"}`
} else {
try {
return stringifyAll(item)
} catch (error) {}
}
} else if (typeof item == "undefined") {
return "undefined"
}
return item.toString()
}
window.convertFilePathToUtoolsPayload = files => files.map(file => {
let isFile = fs.statSync(file).isFile()
return {
isFile: isFile,
isDirectory: !isFile,
name: path.basename(file),
path: file
}
})
let parseStdout = stdout => stdout.map(x => parseItem(x)).join("\n")
window.VmEval = (cmd, sandbox = {}) => new VM({
sandbox: sandbox
}).run(cmd)
// The vm module of Node.js is deprecated in the renderer process and will be removed
window.runCodeInVm = (cmd, cb) => {
const vm = createNodeVM()
//重定向 console
vm.on('console.log', (...stdout) => {
console.log(stdout);
cb(parseStdout(stdout), null)
});
vm.on('console.error', stderr => {
cb(null, stderr.toString())
});
let liteErr = e => {
if (!e) return
return e.stack.replace(/([ ] +at.+)|(.+\.js:\d+)/g, '').trim()
}
// 错误处理
try {
vm.run(cmd, path.join(__dirname, 'preload.js'));
} catch (e) {
console.log('Error: ', e)
cb(null, liteErr(e))
}
let cbUnhandledError = e => {
removeAllListener()
console.log('UnhandledError: ', e)
cb(null, liteErr(e.error))
}
let cbUnhandledRejection = e => {
removeAllListener()
console.log('UnhandledRejection: ', e)
cb(null, liteErr(e.reason))
}
let removeAllListener = () => {
window.removeEventListener('error', cbUnhandledError)
window.removeEventListener('unhandledrejection', cbUnhandledRejection)
delete window.isWatchingError
}
if (!window.isWatchingError) {
window.addEventListener('error', cbUnhandledError)
window.addEventListener('unhandledrejection', cbUnhandledRejection)
window.isWatchingError = true
}
}
window.htmlEncode = (value) => {
return String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;")
}
window.hexEncode = text => Buffer.from(text, 'utf8').toString('hex')
window.hexDecode = text => Buffer.from(text, 'hex').toString('utf8')
window.processPlatform = process.platform
window.getQuickcommandTempFile = ext => {
return path.join(os.tmpdir(), `quickcommandTempFile.${ext}`)
}
window.getBase64Ico = async (filepath, compressed = true) => {
let sourceImage, ext = path.extname(filepath).slice(1)
if (['png', 'jpg', 'jpeg', 'bmp', 'ico', 'gif', 'svg'].includes(ext)) {
if (ext == 'svg') ext = 'svg+xml'
sourceImage = `data:image/${ext};base64,` + fs.readFileSync(filepath, 'base64')
if (ext == 'png') return sourceImage
} else {
sourceImage = utools.getFileIcon(filepath)
return sourceImage
}
if (!compressed) return sourceImage
let compressedImage = await getCompressedIco(sourceImage)
return compressedImage
}
let getCompressedIco = async (img, width = 80) => {
let compressedImage = await pictureCompress({
img: img,
width: width,
height: width,
type: 'png',
quality: 1
})
return compressedImage.img
}
window.getFileInfo = options => {
var file
if (options.type == 'file') {
file = options.argvs
} else if (options.type == 'dialog') {
var dialog = utools.showOpenDialog(options.argvs);
if (!dialog) return false
file = dialog[0]
} else {
return false
}
var information = {
name: path.basename(file),
ext: path.extname(file),
path: file
}
if (options.readfile) {
var codec = (information.ext == '.bat' || information == '.ps1') ? 'gbk' : 'utf8'
information.data = iconv.decode(fs.readFileSync(file), codec)
}
return information
}
window.getCurrentFolderPathFix = () => {
let pwd = utools.getCurrentFolderPath()
let pwdFix = pwd ? pwd : path.join(utools.getPath('home'), 'desktop')
return pwdFix.replace(/\\/g, '\\\\')
}
window.getMatchedFilesFix = payload => {
let MatchedFiles = payload
let Matched = cmd.match(/\{\{MatchedFiles(\[\d+\]){0,1}(\.\w{1,11}){0,1}\}\}/g)
Matched && Matched.forEach(m => {
repl = eval(m.slice(2, -2))
typeof repl == 'object' ? (repl = JSON.stringify(repl)) : (repl = repl.replace('\\', '\\\\'))
cmd = cmd.replace(m, repl.replace('$', '$$$'))
})
}
window.saveFile = (content, file) => {
if (file instanceof Object) file = utools.showSaveDialog(file)
if (!file) return false
try {
fs.writeFileSync(file, content)
return true
} catch (error) {
return false
}
}
window.getSelectFile = hwnd => {
if (utools.isWindows()) {
var cmd = `powershell.exe -NoProfile "(New-Object -COM 'Shell.Application').Windows() | Where-Object { $_.HWND -eq ${hwnd} } | Select-Object -Expand Document | select @{ n='SelectItems'; e={$_.SelectedItems()} } | select -Expand SelectItems | select -Expand Path "`;
let result = child_process.execSync(cmd, {
encoding: "buffer",
windowsHide: true
})
return iconv.decode(result, 'GBK').trim().replace(/\\/g, '/');
} else {
var cmd = `osascript -e 'tell application "Finder" to set selectedItems to selection as alias list
if selectedItems is {} then return
set parentPath to do shell script "dirname " & quoted form of POSIX path of (item 1 of selectedItems)
set pathData to ""
repeat with theItem in selectedItems
set pathData to pathData & POSIX path of theItem & linefeed
end repeat
'
`
let result = child_process.execSync(cmd, {
encoding: "utf8",
windowsHide: true
})
console.log(result);
return result ? result.trim() : ""
}
}
window.clipboardReadText = () => electron.clipboard.readText()
window.runCodeFile = (cmd, option, terminal, callback) => {
var bin = option.bin,
argv = option.argv,
ext = option.ext,
charset = option.charset,
scptarg = option.scptarg || "";
let script = getQuickcommandTempFile(ext)
// 批处理和 powershell 默认编码为 GBK, 解决批处理的换行问题
if (charset.scriptCode) cmd = iconv.encode(cmd.replace(/\n/g, '\r\n'), charset.scriptCode);
fs.writeFileSync(script, cmd);
// var argvs = [script]
// if (argv) {
// argvs = argv.split(' ')
// argvs.push(script);
// }
var child, cmdline
if (bin.slice(-7) == 'csc.exe') {
cmdline = `${bin} ${argv} /out:"${script.slice(0, -2) + 'exe'}" "${script}" && "${script.slice(0, -2) + 'exe'}" ${scptarg}`
} else if (bin == 'gcc') {
var suffix = utools.isWindows() ? '.exe' : ''
cmdline = `${bin} ${argv} "${script.slice(0, -2)}" "${script}" && "${script.slice(0, -2) + suffix}" ${scptarg}`
} else if (utools.isWindows() && bin == 'bash') {
cmdline = `${bin} ${argv} "${script.replace(/\\/g, '/').replace(/C:/i, '/mnt/c')}" ${scptarg}`
} else {
cmdline = `${bin} ${argv} "${script}" ${scptarg}`
}
// 在终端中输出
if (terminal) cmdline = getCommandToLaunchTerminal(cmdline)
child = child_process.spawn(cmdline, {
encoding: 'buffer',
shell: true
})
// var chunks = [],
// err_chunks = [];
console.log('running: ' + cmdline);
child.stdout.on('data', chunk => {
if (charset.outputCode) chunk = iconv.decode(chunk, charset.outputCode)
callback(chunk.toString(), null)
// chunks.push(chunk)
})
child.stderr.on('data', stderr => {
if (charset.outputCode) stderr = iconv.decode(stderr, charset.outputCode)
callback(null, stderr.toString())
// err_chunks.push(err_chunk)
})
// child.on('close', code => {
// let stdout = chunks.join("");
// let stderr = err_chunks.join("");
// callback(stdout, stderr)
// })
}