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
    };
}