utools.getAppVersion() < "2.6.1" && alert("请升级 uTools 至最新版本"); // ----------------------------------------------------------------- const fs = require("fs"); const os = require("os"); const child_process = require("child_process"); const iconv = require("iconv-lite"); const electron = require("electron"); const path = require("path"); const axios = require("axios"); const http = require("http"); const url = require("url"); const kill = require("tree-kill"); const crypto = require("crypto"); require("ses"); const sharp = require("sharp"); const md5 = (input) => { return crypto.createHash("md5").update(input, "utf8").digest("hex"); }; window._ = require("lodash"); window.getuToolsLite = require("./lib/utoolsLite"); // window.yuQueClient = axios.create({ // baseURL: 'https://www.yuque.com/api/v2/', // headers: { // 'Content-Type': 'application/json', // // 只读权限 // 'X-Auth-Token': 'WNrd0Z4kfCZLFrGLVAaas93DZ7sbG6PirKq7VxBL' // } // }); window.getSharedQcById = async (id) => { const url = "https://qc.qaz.ink/home/quick/script/getScript"; const timeStamp = parseInt(new Date().getTime() / 1000); const { data } = await axios.get(url, { params: { id, }, headers: { "verify-encrypt": md5("quickcommand666" + timeStamp), "verify-time": timeStamp, }, }); return JSON.stringify(data.data); }; // 检测进程是否存在 let isProcessExits = (pid) => { try { return process.kill(pid, 0); } catch (e) { return false; } }; window.isAppVersion4 = () => utools.getAppVersion() >= "4.0.0"; // 多开检测 window.multiProcessDetection = () => { let pids = JSON.parse(localStorage.getItem("processes")) || []; if (pids.length) pids = pids.filter((x) => isProcessExits(x)); pids.push(process.pid); localStorage.setItem("processes", JSON.stringify(pids)); if (pids.length > 1) return true; return false; }; // axios.defaults.adapter = require('axios/lib/adapters/http') if (!utools.isWindows()) process.env.PATH = `/usr/local/bin:/usr/local/sbin:${process.env.PATH}`; if (utools.isMacOS()) process.env.PATH = `/opt/homebrew/bin:/opt/homebrew/sbin:${process.env.PATH}`; 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", cb) { kill(pid, signal, cb); }, // 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) { if ( !/^((ht|f)tps?):\/\/([\w\-]+(\.[\w\-]+)*\/)*[\w\-]+(\.[\w\-]+)*\/?(\?([\w\-\.,@?^=%&:\/~\+#]*)+)?/.test( url ) ) throw "url 不合法"; let local = getQuickcommandTempFile("js"); await this.downloadFile(url, local); let source = require(local); fs.unlinkSync(local); return source; }, // 唤醒 uTools wakeUtools: function () { let uToolsPath = utools.isMacOs() ? process.execPath.replace(/\/Frameworks\/.*/, "/MacOS/uTools") : process.execPath; child_process.exec(uToolsPath, () => {}); }, readClipboard: function () { return electron.clipboard.readText(); }, writeClipboard: function (text) { electron.clipboard.writeText(text.toString()); }, }; if (process.platform === "win32") { // 运行vbs脚本 quickcommand.runVbs = function (script) { return new Promise((reslove, reject) => { var tempfile = getQuickcommandTempFile("vbs", "TempVBSScript"); 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")); } ); }); }); }; // 运行powershell脚本 quickcommand.runPowerShell = function (script) { return new Promise((reslove, reject) => { let base64str = Buffer.from(script, "utf16le").toString("base64"); child_process.exec( `powershell.exe -e "${base64str}"`, { encoding: "buffer", }, (err, stdout, stderr) => { if (err) reject(iconv.decode(stderr, "gbk")); else reslove(iconv.decode(stdout, "gbk")); } ); }); }; } if (process.platform === "darwin") { // 运行AppleScript脚本 quickcommand.runAppleScript = function (script) { return new Promise((reslove, reject) => { child_process.execFile( "osascript", ["-e", script], (err, stdout, stderr) => { if (err) reject(stderr); else reslove(stdout); } ); }); }; } // 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, command; 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, `\\"`); cd = dir ? `-d "${dir.replace(/\\/g, "/")}"` : ""; command = `${appPath}wt.exe ${cd} cmd /k "${cmdline}"`; } else { cmdline = cmdline.replace(/"/g, `^"`); cd = dir ? `cd /d "${dir.replace(/\\/g, "/")}" &&` : ""; command = `${cd} start "" cmd /k "${cmdline}"`; } } else if (utools.isMacOs()) { cmdline = cmdline.replace(/"/g, `\\"`); cd = dir ? `cd ${dir.replace(/ /g, "\\\\ ")} &&` : ""; command = fs.existsSync("/Applications/iTerm.app") ? `osascript -e 'tell application "iTerm" if application "iTerm" is running then create window with default profile end if tell current session of first window to write text "clear && ${cd} ${cmdline}" activate end tell'` : `osascript -e 'tell application "Terminal" if application "Terminal" is running then do script "clear && ${cd} ${cmdline}" else do script "clear && ${cd} ${cmdline}" in window 1 end if activate end tell'`; } 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", "SleepVBSScript"); cmd = `echo set ws=CreateObject("Wscript.Shell") > ${tempFilePath} && echo Wscript.sleep ${ms} >> ${tempFilePath} && cscript /nologo ${tempFilePath}`; } else { cmd = `sleep ${ms / 1000}`; } return cmd; }; window.htmlEncode = (value) => { let dom = quickcommand.htmlParse().querySelector("body"); dom.innerText = value; return dom.innerHTML; }; window.removeHtmlTags = (value) => { return quickcommand.htmlParse(value).querySelector("body").innerText; }; window.hexEncode = (text) => Buffer.from(text, "utf8").toString("hex"); window.hexDecode = (text) => Buffer.from(text, "hex").toString("utf8"); window.base64Decode = (text) => Buffer.from(text, "base64").toString("utf8"); window.processPlatform = process.platform; window.joinPath = path.join; window.getUtoolsPlugins = () => { let root = utools.isMacOs() ? path.join(os.homedir(), "Library/Application Support/uTools/plugins/") : utools.isWindows() ? path.join(os.homedir(), "AppData/Roaming/uTools/plugins") : path.join(os.homedir(), ".config/uTools/plugins"); let plugins = {}; let files = fs.readdirSync(root); let deleted = path.join(root, "deleted"); let deletedList = fs.existsSync(deleted) ? fs.readFileSync(path.join(root, "deleted"), "utf8").split("|") : []; files.forEach((file) => { if (/[a-zA-Z0-9\-]+\.asar$/.test(file) && !deletedList.includes(file)) { let pluginInfo = JSON.parse( fs.readFileSync(path.join(root, file, "plugin.json")) ); pluginInfo.logoPath = path.join(root, file, pluginInfo.logo); let keyWordFeatures = []; pluginInfo.features.forEach((f) => { f.cmds.some((c) => { c.length && keyWordFeatures.push(c); return true; }); }); if (!_.isEmpty(keyWordFeatures)) { pluginInfo["keyWordFeatures"] = keyWordFeatures; plugins[pluginInfo.pluginName] = pluginInfo; } } }); return plugins; }; window.getQuickcommandTempFile = (ext, name, dir = "quickcommandTempDir") => { if (!name) name = new Date().getTime() + (Math.random() * 10 ** 6).toFixed(); let tempDir = path.join(os.tmpdir(), dir); if (!fs.existsSync(tempDir)) fs.mkdirSync(tempDir); return path.join(tempDir, `${name}.${ext}`); }; window.delTempFile = (...args) => { let tmpPath = path.join(os.tmpdir(), ...args); if (fs.existsSync(tmpPath)) fs.unlinkSync(tmpPath); }; window.getBase64Ico = (filepath) => { 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; } return sourceImage; }; 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.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, }); return result ? result.trim() : ""; } }; let runUbrowser = (path) => { utools.ubrowser .goto(path) .css( ` .ant-modal-content, .ant-modal-mask, [class*='index-module_contentWrapper'], [class*='index-module_reward'], [class*='ReaderLayout-module_asideWrapper'], [class*='CornerBubble-module_cornerBubble'], [class*='DocReader-module_comment'], #header, #footer { display: none }` ) .run({ width: 980, height: 750, }); }; const docsRepoUrl = "https://www.yuque.com/fofolee/qcdocs3"; window.showUb = { help: function (path = "") { runUbrowser(docsRepoUrl + "/bg31vl" + path); }, docs: function (path = "") { runUbrowser(docsRepoUrl + "/pt589p" + path); }, changelog: function (path = "") { runUbrowser(docsRepoUrl + "/ucnd2o" + path); }, }; window.clipboardReadText = () => electron.clipboard.readText(); window.convertFilePathToUtoolsPayload = (files) => files.map((file) => { let isFile = fs.statSync(file).isFile(); return { isFile: isFile, isDirectory: !isFile, name: path.basename(file), path: file, }; }); let getSandboxFuns = () => { var sandbox = { fetch: fetch.bind(window), utools: getuToolsLite(), electron, axios, Audio, _, AbortController, AbortSignal, Buffer, require, // 兼容老版本 fs, path, os, child_process, }; shortCodes.forEach((f) => { sandbox[f.name] = f; }); return sandbox; }; // 简化报错信息 let liteErr = (e) => { if (!e) return; return e.error ? e.error.stack.replace(/([ ] +at.+)|(.+\.js:\d+)/g, "").trim() : e.message; }; // vm 模块将无法在渲染进程中使用,改用 ses 来执行代码 window.evalCodeInSandbox = (code, addVars = {}) => { let sandboxWithAD = Object.assign(addVars, getSandboxFuns()); sandboxWithAD.quickcommand = _.cloneDeep(quickcommand); try { return new Compartment(sandboxWithAD).evaluate(code); } catch (error) { throw liteErr(error); } }; let isWatchingError = false; window.runCodeInSandbox = (code, callback, addVars = {}) => { let sandbox = getSandboxFuns(); sandbox.console = { log: (...stdout) => { console.log("Result:", stdout); callback(stdout, null); }, error: (...stderr) => { callback(null, stderr); }, }; let sandboxWithAD = Object.assign(addVars, sandbox); sandboxWithAD.quickcommand = _.cloneDeep(quickcommand); if (addVars.enterData) { sandboxWithAD.quickcommand.enterData = addVars.enterData; sandboxWithAD.quickcommand.payload = addVars.enterData.payload; } try { new Compartment(sandboxWithAD).evaluate(code); } catch (e) { console.log("Error: ", e); callback(null, liteErr(e)); } // 自动捕捉错误 let cbUnhandledError = (e) => { removeAllListener(); console.log("UnhandledError: ", e); callback(null, liteErr(e)); }; let cbUnhandledRejection = (e) => { removeAllListener(); console.log("UnhandledRejection: ", e); callback(null, liteErr(e.reason)); }; let removeAllListener = () => { window.removeEventListener("error", cbUnhandledError); window.removeEventListener("unhandledrejection", cbUnhandledRejection); isWatchingError = false; }; if (!isWatchingError) { window.addEventListener("error", cbUnhandledError); window.addEventListener("unhandledrejection", cbUnhandledRejection); isWatchingError = true; } }; window.runCodeFile = (cmd, option, terminal, callback, realTime = true) => { let { bin, argv, ext, charset, scptarg, envPath, alias } = option; let script = getQuickcommandTempFile(ext, "quickcommandTempScript"); // 批处理和 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); // } let 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}`; } let processEnv = _.cloneDeep(process.env); if (envPath) processEnv.PATH = envPath; if (alias) cmdline = alias + "\n" + cmdline; // 在终端中输出 if (terminal) cmdline = getCommandToLaunchTerminal(cmdline); child = child_process.spawn(cmdline, { encoding: "buffer", shell: true, env: processEnv, }); let chunks = [], err_chunks = []; console.log("Running: " + cmdline); child.stdout.on("data", (chunk) => { if (charset.outputCode) chunk = iconv.decode(chunk, charset.outputCode); realTime ? callback(chunk.toString(), null) : chunks.push(chunk); }); child.stderr.on("data", (err_chunk) => { if (charset.outputCode) err_chunk = iconv.decode(err_chunk, charset.outputCode); realTime ? callback(null, err_chunk.toString()) : err_chunks.push(err_chunk); }); if (!realTime) { child.on("close", (code) => { let stdout = chunks.join(""); let stderr = err_chunks.join(""); callback(stdout, stderr); }); } return child; }; const dbStorage = utools.dbStorage; let httpServer; window.quickcommandHttpServer = () => { let run = (port = 33442) => { let httpResponse = (res, code, result) => { // 只收受一次 console.log,接收后就关闭连接 if (res.finished) return; res.writeHead(code, { "Content-Type": "text/html", }); if (result) res.write(result); res.end(); }; let runUserCode = (res, userVars) => { let cmd = dbStorage.getItem("cfg_serverCode"); // 不需要返回输出的提前关闭连接 if (!cmd.includes("console.log")) httpResponse(res, 200); window.runCodeInSandbox( cmd, (stdout, stderr) => { // 错误返回 500 if (stderr) return httpResponse(res, 500, stderr.join(" ")); return httpResponse(res, 200, stdout.join(" ")); }, userVars ); }; httpServer = http.createServer(); httpServer.on("request", (req, res) => { if (req.method === "GET") { let parsedParams = _.cloneDeep(url.parse(req.url, true).query); runUserCode(res, parsedParams); } else if (req.method === "POST") { let data = []; req.on("data", (chunk) => { data.push(chunk); }); req.on("end", () => { let parsedParams; let params = data.join("").toString(); // 先尝试作为 json 解析 try { parsedParams = JSON.parse(params); } catch (error) { parsedParams = _.cloneDeep(url.parse("?" + params, true).query); } runUserCode(res, parsedParams); }); } else { httpResponse(res, 405); } }); httpServer.listen(port, "localhost"); httpServer.on("error", (err) => { utools.showNotification("快捷命令服务:", err); }); }; let stop = () => { if (!httpServer) return; httpServer.close(); }; return { run, stop, }; }; // 处理背景图片 window.imageProcessor = async (imagePath) => { try { // 读取图片 let image = sharp(imagePath); let metadata = await image.metadata(); // 设置固定目标尺寸 let targetWidth = 1280; let targetHeight = 720; const ratio = metadata.width / metadata.height; if (ratio > 16 / 9) { targetHeight = Math.min(720, Math.round(targetWidth / ratio)); } else { targetWidth = Math.min(1280, Math.round(targetHeight * ratio)); } // 调整大小并压缩 let processedBuffer = await image .resize(targetWidth, targetHeight, { fit: "contain", background: { r: 0, g: 0, b: 0, alpha: 0 }, }) .jpeg({ quality: 80, progressive: true }) .toBuffer(); return `data:image/jpeg;base64,${processedBuffer.toString("base64")}`; } catch (error) { console.error("处理图片失败:", error); return null; } };