function MagicJS(scriptName = "MagicJS", logLevel = "INFO") { const MagicEnvironment = () => { const isLoon = typeof $loon !== "undefined"; const isQuanX = typeof $task !== "undefined"; const isNode = typeof module !== "undefined"; const isSurge = typeof $httpClient !== "undefined" && !isLoon; const isStorm = typeof $storm !== "undefined"; const isStash = typeof $environment !== "undefined" && typeof $environment["stash-build"] !== "undefined"; const isSurgeLike = isSurge || isLoon || isStorm || isStash; const isScriptable = typeof importModule !== "undefined"; return { isLoon: isLoon, isQuanX: isQuanX, isNode: isNode, isSurge: isSurge, isStorm: isStorm, isStash: isStash, isSurgeLike: isSurgeLike, isScriptable: isScriptable, get name() { if (isLoon) { return "Loon"; } else if (isQuanX) { return "QuantumultX"; } else if (isNode) { return "NodeJS"; } else if (isSurge) { return "Surge"; } else if (isScriptable) { return "Scriptable"; } else { return "unknown"; } }, get build() { if (isSurge) { return $environment["surge-build"]; } else if (isStash) { return $environment["stash-build"]; } else if (isStorm) { return $storm.buildVersion; } }, get language() { if (isSurge || isStash) { return $environment["language"]; } }, get version() { if (isSurge) { return $environment["surge-version"]; } else if (isStash) { return $environment["stash-version"]; } else if (isStorm) { return $storm.appVersion; } else if (isNode) { return process.version; } }, get system() { if (isSurge) { return $environment["system"]; } else if (isNode) { return process.platform; } }, get systemVersion() { if (isStorm) { return $storm.systemVersion; } }, get deviceName() { if (isStorm) { return $storm.deviceName; } } }; }; const MagicLogger = (scriptName, logLevel = "INFO") => { let _level = logLevel; let logSeparator = '\n'; const logLevels = { SNIFFER: 6, DEBUG: 5, INFO: 4, NOTIFY: 3, WARNING: 2, ERROR: 1, CRITICAL: 0, NONE: -1 }; const logEmoji = { SNIFFER: "", DEBUG: "", INFO: "", NOTIFY: "", WARNING: "❗ ", ERROR: "❌ ", CRITICAL: "❌ ", NONE: "" }; const _log = (msg, level = "INFO") => { // let level = "INFO"; // if (logs.length > 1) { // let lastParam = logs[logs.length-1]; // let leveArr = ["SNIFFER","DEBUG","INFO","NOTIFY","WARNING","ERROR","RETRY"]; // if(typeof(lastParam) == 'string' && leveArr.indexOf(lastParam) > 0){ // level = lastParam; // logs.length = logs.length-1; // } // } // let msg = logs.join('\n'); if (!(logLevels[_level] < logLevels[level.toUpperCase()])) console.log(`██[${scriptName}][${level}]` + '' + `${logEmoji[level.toUpperCase()]}${msg}` + '\n' + ``); }; const setLevel = logLevel => { _level = logLevel; }; return { getLevel: () => { return _level; }, setLevel: setLevel, sniffer: (...logs) => { let msg = logs.join(logSeparator); _log(msg, "SNIFFER"); }, log: (...logs) => { let msg = logs.join(logSeparator); console.log(`██[${scriptName}]` + '' + `${msg}` + '\n' + ``); }, debug: (...logs) => { let msg = logs.join(logSeparator); _log(msg, "DEBUG"); }, info: (...logs) => { let msg = logs.join(logSeparator); _log(msg, "INFO"); }, notify: (...logs) => { let msg = logs.join(logSeparator); _log(msg, "NOTIFY"); }, warning: (...logs) => { let msg = logs.join(logSeparator); _log(msg, "WARNING"); }, error: (...logs) => { let msg = logs.join(logSeparator); _log(msg, "ERROR"); }, retry: (...logs) => { let msg = logs.join(logSeparator); _log(msg, "RETRY"); } }; }; return new class { constructor(scriptName, logLevel) { this._startTime = Date.now(); this.version = "3.0.0"; this.scriptName = scriptName; this.env = MagicEnvironment(); this.logger = MagicLogger(scriptName, logLevel); this.http = typeof MagicHttp === "function" ? MagicHttp(this.env, this.logger) : undefined; this.data = typeof MagicData === "function" ? MagicData(this.env, this.logger) : undefined; this.notification = typeof MagicNotification === "function" ? MagicNotification(this.scriptName, this.env, this.logger, this.http) : undefined; this.utils = typeof MagicUtils === "function" ? MagicUtils(this.env, this.logger) : undefined; this.qinglong = typeof MagicQingLong === "function" ? MagicQingLong(this.env, this.data, this.logger) : undefined; if (typeof this.data !== "undefined") { let magicLoglevel = this.data.read("magic_loglevel"); const barkUrl = this.data.read("magic_bark_url"); if (magicLoglevel) { this.logger.setLevel(magicLoglevel.toUpperCase()); } if (barkUrl) { this.notification.setBark(barkUrl); } } this.logger.info(`${scriptName}, 开始执行!`); // this.checkRecordRequestBody(); } get isRequest() { return typeof $request !== "undefined"; } get isStrictRequest() { return typeof $request !== "undefined" && typeof $response === "undefined"; } get isResponse() { return typeof $response !== "undefined"; } get isDebug() { return this.logger.level === "DEBUG"; } get request() { return typeof $request !== "undefined" ? $request : undefined; } get response() { if (typeof $response !== "undefined") { if ($response.hasOwnProperty("status")) $response["statusCode"] = $response["status"]; if ($response.hasOwnProperty("statusCode")) $response["status"] = $response["statusCode"]; return $response; } else { return undefined; } } log(...logs) { this.logger.log(logs); } toStr(data, defVal = null) { try { return JSON.stringify(data); } catch { return defVal; } } toObj(str, defVal = null) { try { return JSON.parse(str); } catch { return defVal; } } checkRecordRequestBody() { if (!this.isRequest) { return; } const reqBody = $request.body; if (!reqBody) { return; } const env = this.env; const path = $request.path; let cacheKey = this.scriptName + "#" + path.replace("/", "_"); cacheKey = cacheKey.replace("?", "#"); if (env.isQuanX) $prefs.setValueForKey(reqBody, cacheKey); if (env.isLoon || env.isSurge) $persistentStore.write(reqBody, cacheKey); if (env.isNode) { const node_fs = require("fs"); node_fs.writeFileSync( `${cacheKey}.json`, reqBody, { flag: "w" }, (err) => console.log(err) ); } } getRequestBody() { const env = this.env; const path = $request.path; let cacheKey = this.scriptName + "#" + path.replace("/", "_"); cacheKey = cacheKey.replace("?", "#"); if (env.isSurge || env.isLoon) { return $persistentStore.read(cacheKey); } if (env.isQuanX) { return $prefs.valueForKey(cacheKey); } if (env.isNode) { const fpath = `${cacheKey}.json`; const node_fs = require("fs"); if (!node_fs.existsSync(fpath)) { return JSON.parse( node_fs.readFileSync(fpath) ); } } } getResponseBody() { if ($response) { return $response.body; } } // 解析cookie字符串的函数 parseCookies(cookieString, decodeURI = false) { let dict = {}; if (decodeURI) { cookieString && cookieString.split(';').forEach(function (cookie) { let parts = cookie.split('='); dict[parts.shift().trim()] = decodeURIComponent(parts.join('=')); }); } else { cookieString && cookieString.split(';').forEach(function (cookie) { let parts = cookie.split('='); dict[parts.shift().trim()] = parts.join('='); }); } return dict; } // 系列化为cookie字符串 serializeCookies(cookieData, encodeURI = false) { const parts = []; if (encodeURI) { for (let key in cookieData) { let value = cookieData[key]; let cookiePart = `${key}=${encodeURIComponent(value)}`; parts.push(cookiePart); } } else { for (let key in cookieData) { let value = cookieData[key]; let cookiePart = `${key}=${value}`; parts.push(cookiePart); } } return parts.join('; '); } parseSetCookies(setCookieStr) { let tmpArr = setCookieStr.split(/,\s*/).map(cookieStr => { const cookieParts = cookieStr.trim().split(/;\s*(?=[^=]+=[^;]*)/); // 使用正则表达式分割 const nameValue = cookieParts[0].split('=').map(part => part.trim()); const cookieObject = { name: nameValue[0], value: nameValue[1] }; // 解析其他属性 cookieParts.slice(1).forEach(attr => { const [key, val] = attr.split('=').map(part => part.trim()); cookieObject[key] = val || true; // 如果没有值,则设置为 true }); return cookieObject; }); let retData = []; let i = 0; while (i < tmpArr.length) { const curItem = tmpArr[i]; retData.push(curItem); if (curItem.Expires) { const nextItem = tmpArr[i + 1]; if (nextItem) { curItem.Expires = `${curItem.Expires},${nextItem.name}`; } i += 2; } else { i += 1; } } return retData; } objToQueryStr(obj, encode) { let str = ''; for (const key in obj) { let value = obj[key]; if (value != null && value !== '') { if (typeof value === 'object') { value = JSON.stringify(value); } else if (encode) { value = encodeURIComponent(value); } str += `${key}=${value}&`; } } str = str.substring(0, str.length - 1); return str; } parseQueryStr(str) { let obj = {}; if (str.indexOf("?") > -1) { str = str.split("?")[1]; } let arr = str.split("&"); for (let i = 0; i < arr.length; i++) { let kv = arr[i].split("="); obj[kv[0]] = kv[1]; } return obj; } deepClone(obj, newObj) { newObj = newObj || {}; for (let key in obj) { if (typeof obj[key] == 'object') { newObj[key] = (obj[key].constructor === Array) ? [] : {}; this.deepClone(obj[key], newObj[key]); } else { newObj[key] = obj[key]; } } return newObj; } /** * formatDate y:年 M:月 d:日 q:季 H:时 m:分 s:秒 S:毫秒 */ formatDate(date, format) { let o = { 'M+': date.getMonth() + 1, 'd+': date.getDate(), 'H+': date.getHours(), 'm+': date.getMinutes(), 's+': date.getSeconds(), 'q+': Math.floor((date.getMonth() + 3) / 3), 'S': date.getMilliseconds() } if (/(y+)/.test(format)) format = format.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length)) for (let k in o) if (new RegExp('(' + k + ')').test(format)) format = format.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length)) return format } /** * parseDate 字符串格式,默认'yyyy-MM-dd',支持如下:y、M、d、H、m、s、S,不支持w和q */ parseDate(str, format) { format = format || 'yyyy-MM-dd'; let obj = { y: 0, M: 1, d: 0, H: 0, h: 0, m: 0, s: 0, S: 0 }; format.replace(/([^yMdHmsS]*?)(([yMdHmsS])\3*)([^yMdHmsS]*?)/g, function (m, $1, $2, $3, $4, idx, old) { str = str.replace(new RegExp($1 + '(\\d{' + $2.length + '})' + $4), function (_m, _$1) { obj[$3] = parseInt(_$1); return ''; }); return ''; }); obj.M--; // 月份是从0开始的,所以要减去1 let date = new Date(obj.y, obj.M, obj.d, obj.H, obj.m, obj.s); if (obj.S !== 0) date.setMilliseconds(obj.S); // 如果设置了毫秒 return date; } getBaseDoneHeaders(mixHeaders = {}) { return Object.assign( { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'POST,GET,OPTIONS,PUT,DELETE', 'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept' }, mixHeaders ) } getHtmlDoneHeaders() { return this.getBaseDoneHeaders({ 'Content-Type': 'text/html;charset=UTF-8' }) } getJsonDoneHeaders() { return this.getBaseDoneHeaders({ 'Content-Type': 'text/json; charset=utf-8', 'Connection': 'keep-alive' }) } /** * 推送微信消息 * 管理后台 https://wxpusher.zjiecode.com/admin/ * 通用助手 AT_7wDWqSoT8xpJCQqJtHpshKhw7kXc0XCW * 光予助手 AT_rTc93GQYIdMU8XLRnoJaSea8WkfhSzhX * i茅台助手 AT_6b8KjFF3bYqcr7Cv7woUUES7KadNFbvR * 个人uid UID_6P4B00X6Zv8U2oKC0I2R09emxtqq */ doWxpusherSend(data) { // let data = { // appToken: "AT_rTc93GQYIdMU8XLRnoJaSea8WkfhSzhX", // content: content,// 这是主体内容 // summary: summary,// 该参数可选,默认为 msg 的前10个字符 // contentType: 1, // topicIds: [], // uids: [ // "UID_6P4B00X6Zv8U2oKC0I2R09emxtqq" // ], // url: "", // verifyPay: false // }; // if (url) { // data.url = url; // } const headers = this.getJsonDoneHeaders(); headers.Host = 'wxpusher.zjiecode.com'; headers['Content-Type'] = 'application/json;charset=UTF-8'; let options = { url: 'https://wxpusher.zjiecode.com/api/send/message', headers: headers, body: JSON.stringify(data), }; return this.http.post(options); } fastWxpusherSend(content, summary = "", url = "") { let data = { appToken: "AT_7wDWqSoT8xpJCQqJtHpshKhw7kXc0XCW", content: content,// 这是主体内容 summary: summary,// 该参数可选,默认为 msg 的前10个字符 contentType: 1,//内容类型 1表示文字 2表示html(只发送body标签内部的数据即可,不包括body标签,推荐使用这种) 3表示markdown // topicIds: [32852], topicIds: [], uids: [ "UID_6P4B00X6Zv8U2oKC0I2R09emxtqq" ], url: url, verifyPay: false, verifyPayType: 0, }; return this.doWxpusherSend(data); } isEmpty(obj) { return typeof obj == "undefined" || obj == null || obj == "" || obj == "null" || obj == "undefined" || obj.length === 0 } base64Encode(str) { let base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; let out, i, len; let c1, c2, c3; len = str.length; i = 0; out = ""; while (i < len) { c1 = str.charCodeAt(i++) & 0xff; if (i == len) { out += base64EncodeChars.charAt(c1 >> 2); out += base64EncodeChars.charAt((c1 & 0x3) << 4); out += "=="; break; } c2 = str.charCodeAt(i++); if (i == len) { out += base64EncodeChars.charAt(c1 >> 2); out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4)); out += base64EncodeChars.charAt((c2 & 0xF) << 2); out += "="; break; } c3 = str.charCodeAt(i++); out += base64EncodeChars.charAt(c1 >> 2); out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4)); out += base64EncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6)); out += base64EncodeChars.charAt(c3 & 0x3F); } return out; } base64Decode(input) { const base64_chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; // 确保输入是一个正确的Base64编码字符串 if (/([^\s]+[^0-9a-zA-Z\+\/\=]|[^0-9a-zA-Z\+\/\=]\s+)/.test(input)) { throw new Error('Invalid base64 input'); } let str = input.replace(/\s/g, ''); let output = ''; let chr1, chr2, chr3; let enc1, enc2, enc3, enc4; let i = 0; while (i < str.length) { enc1 = base64_chars.indexOf(str.charAt(i++)); enc2 = base64_chars.indexOf(str.charAt(i++)); enc3 = base64_chars.indexOf(str.charAt(i++)); enc4 = base64_chars.indexOf(str.charAt(i++)); chr1 = (enc1 << 2) | (enc2 >> 4); chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); chr3 = ((enc3 & 3) << 6) | enc4; output = output + String.fromCharCode(chr1); if (enc3 !== 64) { output = output + String.fromCharCode(chr2); } if (enc4 !== 64) { output = output + String.fromCharCode(chr3); } } output = this.utf8Decode(output); return output; } utf8Decode(str_data) { let tmp_arr = [], i = 0, c1 = 0, seqlen = 0; str_data = str_data.replace(/\r\n/g, "\n"); while (i < str_data.length) { c1 = str_data.charCodeAt(i) & 0xFF; seqlen = 0; // Single byte sequence (0xxxxxxx) if (c1 <= 0xBF) { c1 = (c1 & 0x7F); seqlen = 1; } else if (c1 <= 0xDF) { c1 = (c1 & 0x1F); seqlen = 2; } else if (c1 <= 0xEF) { c1 = (c1 & 0x0F); seqlen = 3; } else { c1 = (c1 & 0x07); seqlen = 4; } for (let ai = 1; ai < seqlen; ++ai) { c1 = ((c1 << 0x06) | (str_data.charCodeAt(ai + i) & 0x3F)); } if (seqlen === 4) { c1 -= 0x10000; tmp_arr.push(String.fromCharCode(0xD800 | ((c1 >> 10) & 0x3FF))); tmp_arr.push(String.fromCharCode(0xDC00 | (c1 & 0x3FF))); } else { tmp_arr.push(String.fromCharCode(c1)); } i += seqlen; } return tmp_arr.join(""); } parseJwt(token) { try { const segments = token.split('.'); const base64HeaderUrl = segments[0]; const base64Header = base64HeaderUrl.replace(/-/g, '+').replace(/_/g, '/'); const jsonStrHeader = this.base64Decode(base64Header).replace(/\0/g, ''); const headerData = JSON.parse(jsonStrHeader); const base64PayloadUrl = segments[1]; const base64Payload = base64PayloadUrl.replace(/-/g, '+').replace(/_/g, '/'); const jsonStrPayload = this.base64Decode(base64Payload).replace(/\0/g, ''); const payloadData = JSON.parse(jsonStrPayload); return { header: headerData, payload: payloadData, signature: segments[2], }; } catch (e) { this.log(e); return null; } } costTime() { let info = `${this.scriptName}执行完毕!` // if (this.isNode && this.isExecComm) { // info = `指令【${this.comm[1]}】执行完毕!` // } this._endTime = new Date().getTime(); const ms = this._endTime - this._startTime; const costTime = ms / 1000; this.logger.info(`${info}耗时【${costTime}】秒`); } done = (value = {}) => { // this._endTime = Date.now(); // let span = (this._endTime - this._startTime) / 1e3; // this.logger.info(`SCRIPT COMPLETED: ${span} S.`); this.costTime(); if (typeof $done !== "undefined") { $done(value); } }; }(scriptName, logLevel); } function MagicHttp(env, logger) { const phoneUA = "Mozilla/5.0 (iPhone; CPU iPhone OS 13_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.5 Mobile/15E148 Safari/604.1"; const computerUA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36 Edg/84.0.522.59"; let axiosInstance; if (env.isNode) { const axios = require("axios"); axiosInstance = axios.create(); } class InterceptorManager { constructor(isRequest = true) { this.handlers = []; this.isRequest = isRequest; } use(fulfilled, rejected, options) { if (typeof fulfilled === "function") { logger.debug(`Register fulfilled ${fulfilled.name}`); } if (typeof rejected === "function") { logger.debug(`Register rejected ${rejected.name}`); } this.handlers.push({ fulfilled: fulfilled, rejected: rejected, synchronous: options && typeof options.synchronous === "boolean" ? options.synchronous : false, runWhen: options ? options.runWhen : null }); return this.handlers.length - 1; } eject(id) { if (this.handlers[id]) { this.handlers[id] = null; } } forEach(fn) { this.handlers.forEach(element => { if (element !== null) { fn(element); } }); } } function paramsToQueryString(config) { let _config = { ...config }; if (!!_config.params) { if (!env.isNode) { let qs = Object.keys(_config.params).map(key => { const encodeKey = encodeURIComponent(key); _config.url = _config.url.replace(new RegExp(`${key}=[^&]*`, "ig"), ""); _config.url = _config.url.replace(new RegExp(`${encodeKey}=[^&]*`, "ig"), ""); return `${encodeKey}=${encodeURIComponent(_config.params[key])}`; }).join("&"); if (_config.url.indexOf("?") < 0) _config.url += "?"; if (!/(&|\?)$/g.test(_config.url)) { _config.url += "&"; } _config.url += qs; delete _config.params; logger.debug(`Params to QueryString: ${_config.url}`); } } return _config; } const mergeConfig = (method, configOrUrl) => { let config = typeof configOrUrl === "object" ? { headers: {}, ...configOrUrl } : { url: configOrUrl, headers: {} }; if (!config.method) { config["method"] = method; } config = paramsToQueryString(config); if (config["rewrite"] === true) { if (env.isSurge) { config.headers["X-Surge-Skip-Scripting"] = false; delete config["rewrite"]; } else if (env.isQuanX) { config["hints"] = false; delete config["rewrite"]; } } if (env.isSurgeLike) { const contentType = config.headers["content-type"] || config.headers["Content-Type"]; if (config["method"] !== "GET" && contentType && contentType.indexOf("application/json") >= 0 && config.body instanceof Array) { config.body = JSON.stringify(config.body); logger.debug(`Convert Array object to String: ${config.body}`); } } else if (env.isQuanX) { if (config.hasOwnProperty("body") && typeof config["body"] !== "string") config["body"] = JSON.stringify(config["body"]); config["method"] = method; } else if (env.isNode) { if (method === "POST" || method === "PUT" || method === "PATCH" || method === "DELETE") { config.data = config.data || config.body; } else if (method === "GET") { config.params = config.params || config.body; } delete config.body; } return config; }; const modifyResponse = (resp, config = null) => { if (resp) { let _resp = { ...resp, config: resp.config || config, status: resp.statusCode || resp.status, body: resp.body || resp.data, headers: resp.headers || resp.header }; if (typeof _resp.body === "string") { try { _resp.body = JSON.parse(_resp.body); } catch { } } delete _resp.data; return _resp; } else { return resp; } }; const convertHeadersToLowerCase = headers => { return Object.keys(headers).reduce((acc, key) => { acc[key.toLowerCase()] = headers[key]; return acc; }, {}); }; const convertHeadersToCamelCase = headers => { return Object.keys(headers).reduce((acc, key) => { const newKey = key.split("-").map(word => word[0].toUpperCase() + word.slice(1)).join("-"); acc[newKey] = headers[key]; return acc; }, {}); }; const raiseExceptionByStatusCode = (resp, config = null) => { if (!!resp && resp.status >= 400) { logger.debug(`Raise exception when status code is ${resp.status}`); return { name: "RequestException", message: `Request failed with status code ${resp.status}`, config: config || resp.config, response: resp }; } }; const interceptors = { request: new InterceptorManager(), response: new InterceptorManager(false) }; let requestInterceptorChain = []; let responseInterceptorChain = []; let synchronousRequestInterceptors = true; function interceptConfig(config) { config = paramsToQueryString(config); logger.debug(`HTTP ${config["method"].toUpperCase()}:` + '\n' + `${JSON.stringify(config)}`); return config; } function interceptResponse(resp) { try { resp = !!resp ? modifyResponse(resp) : resp; logger.sniffer(`HTTP ${resp.config["method"].toUpperCase()}:` + '\n' + `${JSON.stringify(resp.config)}` + '\n' + `STATUS CODE:` + '\n' + `${resp.status}` + '\n' + `RESPONSE:` + '\n' + `${typeof resp.body === "object" ? JSON.stringify(resp.body) : resp.body}`); const err = raiseExceptionByStatusCode(resp); if (!!err) { return Promise.reject(err); } return resp; } catch (err) { logger.error(err); return resp; } } const registerInterceptors = config => { try { requestInterceptorChain = []; responseInterceptorChain = []; interceptors.request.forEach(interceptor => { if (typeof interceptor.runWhen === "function" && interceptor.runWhen(config) === false) { return; } synchronousRequestInterceptors = synchronousRequestInterceptors && interceptor.synchronous; requestInterceptorChain.unshift(interceptor.fulfilled, interceptor.rejected); }); interceptors.response.forEach(interceptor => { responseInterceptorChain.push(interceptor.fulfilled, interceptor.rejected); }); } catch (err) { logger.error(`Failed to register interceptors: ${err}.`); } }; const request = (method, config) => { let dispatchRequest; const _method = method.toUpperCase(); config = mergeConfig(_method, config); if (env.isNode) { dispatchRequest = axiosInstance; } else { if (env.isSurgeLike) { dispatchRequest = config => { return new Promise((resolve, reject) => { $httpClient[method.toLowerCase()](config, (err, resp, body) => { if (err) { let newErr = { name: err.name || err, message: err.message || err, stack: err.stack || err, config: config, response: modifyResponse(resp) }; reject(newErr); } else { resp.config = config; resp.body = body; resolve(resp); } }); }); }; } else { dispatchRequest = config => { return new Promise((resolve, reject) => { $task.fetch(config).then(resp => { resp = modifyResponse(resp, config); const err = raiseExceptionByStatusCode(resp, config); if (err) { return Promise.reject(err); } resolve(resp); }).catch(err => { let newErr = { name: err.message || err.error, message: err.message || err.error, stack: err.error, config: config, response: !!err.response ? modifyResponse(err.response) : null }; reject(newErr); }); }); }; } } let promise; registerInterceptors(config); const defaultRequestInterceptors = [interceptConfig, undefined]; const defaultResponseInterceptors = [interceptResponse, undefined]; if (!synchronousRequestInterceptors) { logger.debug("Interceptors are executed in asynchronous mode"); let chain = [dispatchRequest, undefined]; Array.prototype.unshift.apply(chain, defaultRequestInterceptors); Array.prototype.unshift.apply(chain, requestInterceptorChain); chain = chain.concat(defaultResponseInterceptors); chain = chain.concat(responseInterceptorChain); promise = Promise.resolve(config); while (chain.length) { try { let onFulfilled = chain.shift(); let onRejected = chain.shift(); if (!env.isNode && config["timeout"] && onFulfilled === dispatchRequest) { onFulfilled = requestTimeout; } if (typeof onFulfilled === "function") { logger.debug(`Executing request fulfilled ${onFulfilled.name}`); } if (typeof onRejected === "function") { logger.debug(`Executing request rejected ${onRejected.name}`); } promise = promise.then(onFulfilled, onRejected); } catch (err) { logger.error(`request exception: ${err}`); } } return promise; } else { logger.debug("Interceptors are executed in synchronous mode"); Array.prototype.unshift.apply(requestInterceptorChain, defaultRequestInterceptors); requestInterceptorChain = requestInterceptorChain.concat([interceptConfig, undefined]); while (requestInterceptorChain.length) { let onFulfilled = requestInterceptorChain.shift(); let onRejected = requestInterceptorChain.shift(); try { if (typeof onFulfilled === "function") { logger.debug(`Executing request fulfilled ${onFulfilled.name}`); } config = onFulfilled(config); } catch (error) { if (typeof onRejected === "function") { logger.debug(`Executing request rejected ${onRejected.name}`); } onRejected(error); break; } } try { if (!env.isNode && config["timeout"]) { promise = requestTimeout(config); } else { promise = dispatchRequest(config); } } catch (err) { return Promise.reject(err); } Array.prototype.unshift.apply(responseInterceptorChain, defaultResponseInterceptors); while (responseInterceptorChain.length) { promise = promise.then(responseInterceptorChain.shift(), responseInterceptorChain.shift()); } return promise; } function requestTimeout(config) { try { const timer = new Promise((_, reject) => { setTimeout(() => { let err = { message: `timeout of ${config["timeout"]}ms exceeded.`, config: config }; reject(err); }, config["timeout"]); }); return Promise.race([dispatchRequest(config), timer]); } catch (err) { logger.error(`Request Timeout exception: ${err}.`); } } }; return { request: request, interceptors: interceptors, convertHeadersToLowerCase: convertHeadersToLowerCase, convertHeadersToCamelCase: convertHeadersToCamelCase, modifyResponse: modifyResponse, get: configOrUrl => { return request("GET", configOrUrl); }, post: configOrUrl => { return request("POST", configOrUrl); }, put: configOrUrl => { return request("PUT", configOrUrl); }, patch: configOrUrl => { return request("PATCH", configOrUrl); }, delete: configOrUrl => { return request("DELETE", configOrUrl); }, head: configOrUrl => { return request("HEAD", configOrUrl); }, options: configOrUrl => { return request("OPTIONS", configOrUrl); } }; } function MagicData(env, logger) { let node = { fs: undefined, data: {} }; if (env.isNode) { node.fs = require("fs"); try { node.fs.accessSync("./magic.json", node.fs.constants.R_OK | node.fs.constants.W_OK); } catch (err) { node.fs.writeFileSync("./magic.json", "{}", { encoding: "utf8" }); } node.data = require("./magic.json"); } const defaultValueComparator = (oldVal, newVal) => { if (typeof newVal === "object") { return false; } else { return oldVal === newVal; } }; const _typeConvertor = val => { if (val === "true") { return true; } else if (val === "false") { return false; } else if (typeof val === "undefined") { return null; } else { return val; } }; const _valConvertor = (val, default_, session, read_no_session) => { if (session) { try { if (typeof val === "string") val = JSON.parse(val); if (val["magic_session"] === true) { val = val[session]; } else { val = null; } } catch { val = null; } } if (typeof val === "string" && val !== "null") { try { val = JSON.parse(val); } catch { } } if (read_no_session === false && !!val && val["magic_session"] === true) { val = null; } if ((val === null || typeof val === "undefined") && default_ !== null && typeof default_ !== "undefined") { val = default_; } val = _typeConvertor(val); return val; }; const convertToObject = obj => { if (typeof obj === "string") { let data = {}; try { data = JSON.parse(obj); const type = typeof data; if (type !== "object" || data instanceof Array || type === "bool" || data === null) { data = {}; } } catch { } return data; } else if (obj instanceof Array || obj === null || typeof obj === "undefined" || obj !== obj || typeof obj === "boolean") { return {}; } else { return obj; } }; const readForNode = (key, default_ = null, session = "", read_no_session = false, externalData = null) => { let data = externalData || node.data; if (!!data && typeof data[key] !== "undefined" && data[key] !== null) { val = data[key]; } else { val = !!session ? {} : null; } val = _valConvertor(val, default_, session, read_no_session); return val; }; const read = (key, default_ = null, session = "", read_no_session = false, externalData = null) => { let val = ""; if (externalData || env.isNode) { val = readForNode(key, default_, session, read_no_session, externalData); } else { if (env.isSurgeLike) { val = $persistentStore.read(key); } else if (env.isQuanX) { val = $prefs.valueForKey(key); } val = _valConvertor(val, default_, session, read_no_session); } logger.debug(`READ DATA [${key}]${!!session ? `[${session}]` : ""} <${typeof val}>` + '\n' + `${JSON.stringify(val)}`); return val; }; const writeForNode = (key, val, session = "", externalData = null) => { let data = externalData || node.data; data = convertToObject(data); if (!!session) { let obj = convertToObject(data[key]); obj["magic_session"] = true; obj[session] = val; data[key] = obj; } else { data[key] = val; } if (externalData !== null) { externalData = data; } return data; }; const write = (key, val, session = "", externalData = null) => { if (typeof val === "undefined" || val !== val) { return false; } if (!env.isNode && (typeof val === "boolean" || typeof val === "number")) { val = String(val); } let data = ""; if (externalData || env.isNode) { data = writeForNode(key, val, session, externalData); } else { if (!session) { data = val; } else { if (env.isSurgeLike) { data = !!$persistentStore.read(key) ? $persistentStore.read(key) : data; } else if (env.isQuanX) { data = !!$prefs.valueForKey(key) ? $prefs.valueForKey(key) : data; } data = convertToObject(data); data["magic_session"] = true; data[session] = val; } } if (!!data && typeof data === "object") { data = JSON.stringify(data, null, 4); } logger.debug(`WRITE DATA [${key}]${session ? `[${session}]` : ""} <${typeof val}>` + '\n' + `${JSON.stringify(val)}`); if (!externalData) { if (env.isSurgeLike) { return $persistentStore.write(data, key); } else if (env.isQuanX) { return $prefs.setValueForKey(data, key); } else if (env.isNode) { try { node.fs.writeFileSync("./magic.json", data); return true; } catch (err) { logger.error(err); return false; } } } return true; }; const update = (key, val, session, comparator = defaultValueComparator, externalData = null) => { val = _typeConvertor(val); const oldValue = read(key, null, session, false, externalData); if (comparator(oldValue, val) === true) { return false; } else { const result = write(key, val, session, externalData); let newVal = read(key, null, session, false, externalData); if (comparator === defaultValueComparator && typeof newVal === "object") { return result; } return comparator(val, newVal); } }; const delForNode = (key, session, externalData) => { let data = externalData || node.data; data = convertToObject(data); if (!!session) { obj = convertToObject(data[key]); delete obj[session]; data[key] = obj; } else { delete data[key]; } if (!!externalData) { externalData = data; } return data; }; const del = (key, session = "", externalData = null) => { let data = {}; if (externalData || env.isNode) { data = delForNode(key, session, externalData); if (!externalData) { node.fs.writeFileSync("./magic.json", JSON.stringify(data, null, 4)); } else { externalData = data; } } else { if (!session) { if (env.isStorm) { return $persistentStore.remove(key); } else if (env.isSurgeLike) { return $persistentStore.write(null, key); } else if (env.isQuanX) { return $prefs.removeValueForKey(key); } } else { if (env.isSurgeLike) { data = $persistentStore.read(key); } else if (env.isQuanX) { data = $prefs.valueForKey(key); } data = convertToObject(data); delete data[session]; const json = JSON.stringify(data, null, 4); write(key, json); } } logger.debug(`DELETE KEY [${key}]${!!session ? `[${session}]` : ""}`); }; const allSessionNames = (key, externalData = null) => { let _sessions = []; let data = read(key, null, null, true, externalData); data = convertToObject(data); if (data["magic_session"] !== true) { _sessions = []; } else { _sessions = Object.keys(data).filter(key => key !== "magic_session"); } logger.debug(`READ ALL SESSIONS [${key}] <${typeof _sessions}>` + '\n' + `${JSON.stringify(_sessions, null, 4)}`); return _sessions; }; const allSessions = (key, externalData = null) => { let _sessions = {}; let data = read(key, null, null, true, externalData); data = convertToObject(data); if (data["magic_session"] === true) { _sessions = { ...data }; delete _sessions["magic_session"]; } logger.debug(`READ ALL SESSIONS [${key}] <${typeof _sessions}>` + '\n' + `${JSON.stringify(_sessions, null, 4)}`); return _sessions; }; return { read: read, write: write, del: del, update: update, allSessions: allSessions, allSessionNames: allSessionNames, defaultValueComparator: defaultValueComparator, convertToObject: convertToObject }; } function MagicNotification(scriptName, env, logger, http) { let _barkUrl = null; let _barkKey = null; let notifyInfo = [] const setBark = url => { try { let _url = url.replace(/\/+$/g, ""); _barkUrl = `${/^https?:\/\/([^/]*)/.exec(_url)[0]}/push`; _barkKey = /\/([^\/]+)\/?$/.exec(_url)[1]; } catch (ex) { logger.error(`Bark url error: ${ex}.`); } }; function appendNotifyInfo(info, type) { if (type == 1) { notifyInfo = info } else { notifyInfo.push(info) } } function prependNotifyInfo(info) { notifyInfo.splice(0, 0, info) } function msg(subtitle, message, openUrl, mediaUrl) { let opts = {}; if (openUrl) { opts["open-url"] = openUrl; } if (mediaUrl) { opts["media-url"] = mediaUrl; } if (!message || message.length == 0) { if (Array.isArray(notifyInfo)) { message = notifyInfo.join("\n"); } else { message = notifyInfo; } } if (message && message.length > 0) { post(scriptName, "", message, opts); } } function post(title = scriptName, subTitle = "", body = "", opts = "") { const _adaptOpts = _opts => { try { let newOpts = {}; if (typeof _opts === "string") { if (_opts.length > 0) { if (env.isLoon) { newOpts = { openUrl: _opts }; } else if (env.isQuanX) { newOpts = { "open-url": _opts }; } else if (env.isSurge) { newOpts = { url: _opts }; } } } else if (typeof _opts === "object") { if (env.isLoon) { newOpts["openUrl"] = !!_opts["open-url"] ? _opts["open-url"] : ""; newOpts["mediaUrl"] = !!_opts["media-url"] ? _opts["media-url"] : ""; } else if (env.isQuanX) { newOpts = !!_opts["open-url"] || !!_opts["media-url"] ? _opts : {}; } else if (env.isSurge) { let openUrl = _opts["open-url"] || _opts["openUrl"]; newOpts = openUrl ? { url: openUrl } : {}; } } return newOpts; } catch (err) { logger.error(`通知选项转换失败${err}`); } return _opts; }; opts = _adaptOpts(opts); if (arguments.length === 1) { title = scriptName; subTitle = "", body = arguments[0]; } logger.notify('\n' + `title:${title}` + '\n' + `subTitle:${subTitle}` + '\n' + `body:${body}` + '\n' + `options:${typeof opts === "object" ? JSON.stringify(opts) : opts}`); if (env.isSurge) { $notification.post(title, subTitle, body, opts); } else if (env.isLoon) { if (!!opts) $notification.post(title, subTitle, body, opts); else $notification.post(title, subTitle, body); } else if (env.isQuanX) { $notify(title, subTitle, body, opts); } if (_barkUrl && _barkKey) { bark(title, subTitle, body); } } function debug(title = scriptName, subTitle = "", body = "", opts = "") { if (logger.getLevel() === "DEBUG") { if (arguments.length === 1) { title = scriptName; subTitle = ""; body = arguments[0]; } this.post(title, subTitle, body, opts); } } function bark(title = scriptName, subTitle = "", body = "", opts = "") { if (typeof http === "undefined" || typeof http.post === "undefined") { throw "Bark notification needs to import MagicHttp module."; } let options = { url: _barkUrl, headers: { "content-type": "application/json; charset=utf-8" }, body: { title: title, body: subTitle ? `${subTitle}` + '\n' + `${body}` : body, device_key: _barkKey } }; http.post(options).catch(ex => { logger.error(`Bark notify error: ${ex}`); }); } return { post: post, debug: debug, bark: bark, setBark: setBark, appendNotifyInfo: appendNotifyInfo, prependNotifyInfo: prependNotifyInfo, msg: msg, }; } function MagicUtils(env, logger) { const retry = (fn, retries = 5, interval = 0, callback = null) => { return (...args) => { return new Promise((resolve, reject) => { function _retry(...args) { Promise.resolve().then(() => fn.apply(this, args)).then(result => { if (typeof callback === "function") { Promise.resolve().then(() => callback(result)).then(() => { resolve(result); }).catch(ex => { if (retries >= 1) { if (interval > 0) setTimeout(() => _retry.apply(this, args), interval); else _retry.apply(this, args); } else { reject(ex); } retries--; }); } else { resolve(result); } }).catch(ex => { logger.error(ex); if (retries >= 1 && interval > 0) { setTimeout(() => _retry.apply(this, args), interval); } else if (retries >= 1) { _retry.apply(this, args); } else { reject(ex); } retries--; }); } _retry.apply(this, args); }); }; }; const formatTime = (time, fmt = "yyyy-MM-dd hh:mm:ss") => { let o = { "M+": time.getMonth() + 1, "d+": time.getDate(), "h+": time.getHours(), "m+": time.getMinutes(), "s+": time.getSeconds(), "q+": Math.floor((time.getMonth() + 3) / 3), S: time.getMilliseconds() }; if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (time.getFullYear() + "").substr(4 - RegExp.$1.length)); for (let k in o) if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, RegExp.$1.length === 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length)); return fmt; }; const now = () => { return formatTime(new Date(), "yyyy-MM-dd hh:mm:ss"); }; const today = () => { return formatTime(new Date(), "yyyy-MM-dd"); }; const sleep = time => { return new Promise(resolve => setTimeout(resolve, time)); }; const assert = (val, msg = null) => { if (env.isNode) { const _assert = require("assert"); if (msg) _assert(val, msg); else _assert(val); } else { if (val !== true) { let err = `AssertionError: ${msg || "The expression evaluated to a falsy value."}`; logger.error(err); } } }; return { retry: retry, formatTime: formatTime, now: now, today: today, sleep: sleep, assert: assert }; } function MagicQingLong(env, data, logger) { let qlUrl = ""; let qlName = ""; let qlClient = ""; let qlSecret = ""; let qlPwd = ""; let qlToken = ""; const magicJsonFileName = "magic.json"; const timeout = 3e3; const http = (() => MagicHttp(env, logger))(); const init = (url, clientId, clientSecret, username, password) => { qlUrl = url; qlClient = clientId; qlSecret = clientSecret; qlName = username; qlPwd = password; }; function readQingLongConfig(config) { qlUrl = qlUrl || data.read("magic_qlurl"); qlToken = qlToken || data.read("magic_qltoken"); logger.debug(`QingLong url: ${qlUrl}` + '\n' + `QingLong token: ${qlToken}`); return config; } function setBaseUrlAndTimeout(config) { if (!qlUrl) { qlUrl = data.read("magic_qlurl"); } if (config.url.indexOf(qlUrl) < 0) { config.url = `${qlUrl}${config.url}`; } return { ...config, timeout: timeout }; } function setTimestamp(config) { config.params = { ...config.params, t: Date.now() }; return config; } async function setAuthorization(config) { qlToken = qlToken || data.read("magic_qltoken", ""); if (!qlToken) { await getToken(); } config.headers["authorization"] = `Bearer ${qlToken}`; return config; } function switchClientMode(config) { qlClient = qlClient || data.read("magic_qlclient"); if (!!qlClient) { config.url = config.url.replace("/api/", "/open/"); } return config; } async function refreshToken(error) { try { const message = error.message || error.error || JSON.stringify(error); if ((message.indexOf("NSURLErrorDomain") >= 0 && message.indexOf("-1012") >= 0 || !!error.response && error.response.status === 401) && !!error.config && error.config.refreshToken !== true) { logger.warning(`QingLong Panel token has expired`); logger.info("Refreshing the QingLong Panel token"); await getToken(); error.config["refreshToken"] = true; logger.info("Call the previous method again"); return await http.request(error.config.method, error.config); } else { return Promise.reject(error); } } catch (ex) { return Promise.reject(ex); } } http.interceptors.request.use(setBaseUrlAndTimeout, undefined); http.interceptors.request.use(switchClientMode, undefined, { runWhen: config => { return config.url.indexOf("api/user/login") < 0 && config.url.indexOf("open/auth/token") < 0; } }); http.interceptors.request.use(setAuthorization, undefined, { runWhen: config => { return config.url.indexOf("api/user/login") < 0 && config.url.indexOf("open/auth/token") < 0; } }); http.interceptors.request.use(setTimestamp, undefined, { runWhen: config => { return config.url.indexOf("open/auth/token") < 0; } }); http.interceptors.request.use(readQingLongConfig, undefined); http.interceptors.response.use(undefined, refreshToken); async function getToken() { qlClient = qlClient || data.read("magic_qlclient"); qlSecret = qlSecret || data.read("magic_qlsecrt"); qlName = qlName || data.read("magic_qlname"); qlPwd = qlPwd || data.read("magic_qlpwd"); if (qlUrl && qlClient && qlSecret) { logger.info("Get token from QingLong Panel"); await http.get({ url: `/open/auth/token`, headers: { "content-type": "application/json" }, params: { client_id: qlClient, client_secret: qlSecret } }).then(resp => { if (Object.keys(resp.body).length > 0 && resp.body.data && resp.body.data.token) { logger.info("Successfully logged in to QingLong Panel"); qlToken = resp.body.data.token; data.write("magic_qltoken", qlToken); } else { throw new Error("Get QingLong Panel token failed."); } }).catch(err => { logger.error(`Error logging in to QingLong Panel.` + '\n' + `${err.message || err}`); }); } else if (qlUrl && qlName && qlPwd) { await http.post({ url: `/api/user/login`, headers: { "content-type": "application/json" }, body: { username: qlName, password: qlPwd } }).then(resp => { logger.info("Successfully logged in to QingLong Panel"); qlToken = resp.body.data.token; data.write("magic_qltoken", qlToken); }).catch(err => { logger.error(`Error logging in to QingLong Panel.` + '\n' + `${err.message || err}`); }); } return qlToken; } async function setEnv(name, value, id = null) { qlUrl = qlUrl || data.read("magic_qlurl"); if (id === null) { let envIds = await setEnvs([{ name: name, value: value }]); if (!!envIds && envIds.length === 1) { return envIds[0]; } } else { await http.put({ url: `/api/envs`, headers: { "content-type": "application/json" }, body: { name: name, value: value, id: id } }).then(resp => { if (resp.body.code === 200) { logger.debug(`QINGLONG UPDATE ENV ${name} <${typeof value}> (${id})` + '\n' + `${JSON.stringify(value)}`); return true; } else { logger.error(`Error adding environment variable from QingLong Panel.` + '\n' + `${JSON.stringify(resp)}`); } }).catch(err => { logger.error(`Error adding environment variable from QingLong Panel.` + '\n' + `${err.message || err}`); return false; }); } } async function setEnvs(envs) { let envIds = []; await http.post({ url: `/api/envs`, headers: { "content-type": "application/json" }, body: envs }).then(resp => { if (resp.body.code === 200) { resp.body.data.forEach(element => { logger.debug(`QINGLONG ADD ENV ${element.name} <${typeof element.value}> (${element.id})` + '\n' + `${JSON.stringify(element)}`); envIds.push(element.id); }); } else { logger.error(`Error adding environments variable from QingLong Panel.` + '\n' + `${JSON.stringify(resp)}`); } }).catch(err => { logger.error(`Error adding environments variable from QingLong Panel.` + '\n' + `${err.message || err}`); }); return envIds; } async function delEnvs(ids) { return await http.delete({ url: `/api/envs`, headers: { accept: "application/json", "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6", connection: "keep-alive", "content-type": "application/json;charset=UTF-8", "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.63 Safari/537.36 Edg/102.0.1245.30" }, body: ids }).then(resp => { if (resp.body.code === 200) { logger.debug(`QINGLONG DELETE ENV IDS: ${ids}`); return true; } else { logger.error(`Error deleting environments variable from QingLong Panel.` + '\n' + `${JSON.stringify(resp)}`); return false; } }).catch(err => { logger.error(`Error deleting environments variable from QingLong Panel.` + '\n' + `${err.message || err}`); }); } async function getEnvs(name = null, searchValue = "", retired = 0) { let envs = []; await http.get({ url: `/api/envs`, headers: { "content-type": "application/json" }, params: { searchValue: searchValue } }).then(resp => { if (resp.body.code === 200) { const allEnvs = resp.body.data; if (!!name) { let _envs = []; for (const env of allEnvs) { if (env.name === name) { envs.push(env); } } envs = _envs; } envs = allEnvs; } else { throw new Error(`Error reading environment variable from QingLong Panel.` + '\n' + `${JSON.stringify(resp)}`); } }).catch(err => { throw new Error(`Error reading environments variable from QingLong Panel.` + '\n' + `${err.message || err}`); }); return envs; } async function getEnv(id) { let env = null; const allEnvs = await getEnvs(); for (const _env of allEnvs) { if (_env.id === id) { env = _env; break; } } return env; } async function disableEnvs(ids) { let result = false; await http.put({ url: `/api/envs/disable`, headers: { accept: "application/json", "accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6", connection: "keep-alive", "content-type": "application/json;charset=UTF-8", "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.63 Safari/537.36 Edg/102.0.1245.30" }, body: ids }).then(resp => { if (resp.body.code === 200) { logger.debug(`QINGLONG DISABLED ENV IDS: ${ids}`); result = true; } else { logger.error(`Error disabling environments variable from QingLong Panel.` + '\n' + `${JSON.stringify(resp)}`); } }).catch(err => { logger.error(`Error disabling environments variable from QingLong Panel.` + '\n' + `${err.message || err}`); }); return result; } async function enableEnvs(ids) { let result = false; await http.put({ url: `/api/envs/enable`, headers: { accept: "application/json", "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6", connection: "keep-alive", "content-type": "application/json;charset=UTF-8", "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.63 Safari/537.36 Edg/102.0.1245.30" }, body: ids }).then(resp => { if (resp.body.code === 200) { logger.debug(`QINGLONG ENABLED ENV IDS: ${ids}`); result = true; } else { logger.error(`Error enabling environments variable from Qilong panel.` + '\n' + `${JSON.stringify(resp)}`); } }).catch(err => { logger.error(`Error enabling environments variable from Qilong panel.` + '\n' + `${err.message || err}`); }); return result; } async function addScript(name, path = "", content = "") { let result = false; await http.post({ url: `/api/scripts`, headers: { "content-type": "application/json" }, body: { filename: name, path: path, content: content } }).then(resp => { if (resp.body.code === 200) { result = true; } else { logger.error(`Error reading data from QingLong Panel.` + '\n' + `${JSON.stringify(resp)}`); } }).catch(err => { logger.error(`Error reading data from QingLong Panel.` + '\n' + `${err.message || err}`); }); return result; } async function getScript(name, path = "") { let content = ""; await http.get({ url: `/api/scripts/${name}`, params: { path: path } }).then(resp => { if (resp.body.code === 200) { content = resp.body.data; } else { throw new Error(`Error reading data from QingLong Panel.` + '\n' + `${JSON.stringify(resp)}`); } }).catch(err => { throw new Error(`Error reading data from QingLong Panel.` + '\n' + `${err.message || err}`); }); return content; } async function editScript(name, path = "", content = "") { let result = false; await http.put({ url: `/api/scripts`, headers: { "content-type": "application/json" }, body: { filename: name, path: path, content: content } }).then(resp => { if (resp.body.code === 200) { result = true; } else { logger.error(`Error reading data from QingLong Panel.` + '\n' + `${JSON.stringify(resp)}`); } }).catch(err => { logger.error(`Error reading data from QingLong Panel.` + '\n' + `${err.message || err}`); }); return result; } async function delScript(name, path = "") { let result = false; await http.delete({ url: `/api/scripts`, headers: { "content-type": "application/json" }, body: { filename: name, path: path } }).then(resp => { if (resp.body.code === 200) { result = true; } else { logger.error(`Error reading data from QingLong Panel.` + '\n' + `${JSON.stringify(resp)}`); } }).catch(err => { logger.error(`Error reading data from QingLong Panel.` + '\n' + `${err.message || err}`); }); return result; } async function write(key, val, session = "") { let qlContent = await getScript(magicJsonFileName, ""); let qlData = data.convertToObject(qlContent); let writeResult = data.write(key, val, session, qlData); qlContent = JSON.stringify(qlData, null, 4); let editResult = await editScript(magicJsonFileName, "", qlContent); return editResult && writeResult; } async function batchWrite(...args) { let qlContent = await getScript(magicJsonFileName, ""); let qlData = data.convertToObject(qlContent); for (let arg of args) { data.write(arg[0], arg[1], typeof arg[2] !== "undefined" ? arg[2] : "", qlData); } qlContent = JSON.stringify(qlData, null, 4); return await editScript(magicJsonFileName, "", qlContent); } async function update(key, val, session, comparator = data.defaultValueComparator) { let qlContent = await getScript(magicJsonFileName, ""); let qlData = data.convertToObject(qlContent); const updateResult = data.update(key, val, session, comparator, qlData); let editScriptResult = false; if (updateResult === true) { qlContent = JSON.stringify(qlData, null, 4); editScriptResult = await editScript(magicJsonFileName, "", qlContent); } return updateResult && editScriptResult; } async function batchUpdate(...args) { let qlContent = await getScript(magicJsonFileName, ""); let qlData = data.convertToObject(qlContent); for (let arg of args) { data.update(arg[0], arg[1], typeof arg[2] !== "undefined" ? arg[2] : "", typeof arg[3] !== "undefined" ? arg["comparator"] : data.defaultValueComparator, qlData); } qlContent = JSON.stringify(qlData, null, 4); return await editScript(magicJsonFileName, "", qlContent); } async function read(key, val, session = "", read_no_session = false) { let qlContent = await getScript(magicJsonFileName, ""); let qlData = data.convertToObject(qlContent); return data.read(key, val, session, read_no_session, qlData); } async function batchRead(...args) { let qlContent = await getScript(magicJsonFileName, ""); let qlData = data.convertToObject(qlContent); let results = []; for (let arg of args) { const result = data.read(arg[0], arg[1], typeof arg[2] !== "undefined" ? arg[2] : "", typeof arg[3] === "boolean" ? arg[3] : false, qlData); results.push(result); } return results; } async function del(key, session = "") { let qlContent = await getScript(magicJsonFileName, ""); let qlData = data.convertToObject(qlContent); const delResult = data.del(key, session, qlData); qlContent = JSON.stringify(qlData, null, 4); const editResult = await editScript(magicJsonFileName, "", qlContent); return delResult && editResult; } async function batchDel(...args) { let qlContent = await getScript(magicJsonFileName, ""); let qlData = data.convertToObject(qlContent); for (let arg of args) { data.del(arg[0], typeof arg[1] !== "undefined" ? arg[1] : "", qlData); } qlContent = JSON.stringify(qlData, null, 4); return await editScript(magicJsonFileName, "", qlContent); } async function allSessionNames(key) { let qlContent = await getScript(magicJsonFileName, ""); let qlData = data.convertToObject(qlContent); return data.allSessionNames(key, qlData); } async function allSessions(key) { let qlContent = await getScript(magicJsonFileName, ""); let qlData = data.convertToObject(qlContent); return data.allSessions(key, qlData); } return { url: qlUrl || data.read("magic_qlurl"), init: init, getToken: getToken, setEnv: setEnv, setEnvs: setEnvs, getEnv: getEnv, getEnvs: getEnvs, delEnvs: delEnvs, disableEnvs: disableEnvs, enableEnvs: enableEnvs, addScript: addScript, getScript: getScript, editScript: editScript, delScript: delScript, write: write, read: read, del: del, update: update, batchWrite: batchWrite, batchRead: batchRead, batchUpdate: batchUpdate, batchDel: batchDel, allSessions: allSessions, allSessionNames: allSessionNames }; }