Browse Source

增加光予助手脚本

shawenguan 2 years ago
parent
commit
6a37637779
5 changed files with 1584 additions and 0 deletions
  1. 87 0
      BoxJsSub/JoJo_BoxJs.json
  2. 137 0
      Scripts/gandartHelper.js
  3. 171 0
      Scripts/util/Crypto.js
  4. 388 0
      Scripts/util/OpenAPI.js
  5. 801 0
      Scripts/util/ToolKit.js

+ 87 - 0
BoxJsSub/JoJo_BoxJs.json

@@ -0,0 +1,87 @@
+{
+    "id": "JoJO.app.sub",
+    "name": "JoJO 脚本订阅",
+    "description": "JoJO私人QX订阅",
+    "author": "@JoJO",
+    "icon": "https://avatars.githubusercontent.com/u/16010188",
+    "repo": "http://git.jojo21.cf/shawenguan/Quantumult-X",
+    "apps": [
+        {
+            "id": "jojo.gandart",
+            "name": "光予助手",
+            "author": "@jojo",
+            "repo": "http://git.jojo21.cf/shawenguan/Quantumult-X/src/master/Scripts/gandartHelper.js",
+            "script": "http://git.jojo21.cf/shawenguan/Quantumult-X/src/master/Scripts/gandartHelper.js",
+            "icons": [
+                "https://raw.githubusercontent.com/Orz-3/mini/master/Alpha/10086.png",
+                "https://raw.githubusercontent.com/Orz-3/mini/master/Color/10086.png"
+            ],
+            "scripts": [
+                {
+                    "name": "监控购买",
+                    "script": "http://git.jojo21.cf/shawenguan/Quantumult-X/src/master/Scripts/gandartHelper.js"
+                },
+                {
+                    "name": "极速合成",
+                    "script": "http://git.jojo21.cf/shawenguan/Quantumult-X/src/master/Scripts/gandartHelper.js"
+                },
+                {
+                    "name": "极速置换",
+                    "script": "http://git.jojo21.cf/shawenguan/Quantumult-X/src/master/Scripts/gandartHelper.js"
+                }
+            ],
+            "keys": [
+                "@gandart.isNotify",
+                "@gandart.token",
+                "@gandart.castingId",
+                "@gandart.transactionRecordId",
+                "@gandart.limitPrice",
+                "@gandart.collectionName",
+            ],
+            "settings": [
+                {
+                  "id": "@gandart.isNotify",
+                  "name": "开启/关闭Telegram通知",
+                  "val": false,
+                  "type": "boolean",
+                  "desc": "默认关闭"
+                },
+                {
+                  "id": "@gandart.token",
+                  "name": "token",
+                  "val": "",
+                  "type": "text",
+                  "desc": "授权token"
+                },
+                {
+                  "id": "@gandart.castingId",
+                  "name": "产品铸造品类ID",
+                  "val": 0,
+                  "type": "number",
+                  "desc": "产品铸造品类ID"
+                },
+                {
+                  "id": "@gandart.transactionRecordId",
+                  "name": "产品记录ID",
+                  "val": 0,
+                  "type": "number",
+                  "desc": "产品记录ID()非编号"
+                },
+                {
+                  "id": "@gandart.limitPrice",
+                  "name": "价格上限",
+                  "val": 0,
+                  "type": "number",
+                  "desc": "价格上限"
+                },
+                {
+                  "id": "@gandart.collectionName",
+                  "name": "产品名称",
+                  "val": 0,
+                  "type": "number",
+                  "desc": "产品名称"
+                }
+            ]
+        }
+    ]
+}

File diff suppressed because it is too large
+ 137 - 0
Scripts/gandartHelper.js


+ 171 - 0
Scripts/util/Crypto.js

@@ -0,0 +1,171 @@
+/*!
+ * Joseph Myer's md5() algorithm wrapped in a self-invoked function to prevent
+ */
+
+
+function Crypto() {
+    function md5cycle(x, k) {
+        let a = x[0], b = x[1], c = x[2], d = x[3];
+
+        a = ff(a, b, c, d, k[0], 7, -680876936);
+        d = ff(d, a, b, c, k[1], 12, -389564586);
+        c = ff(c, d, a, b, k[2], 17, 606105819);
+        b = ff(b, c, d, a, k[3], 22, -1044525330);
+        a = ff(a, b, c, d, k[4], 7, -176418897);
+        d = ff(d, a, b, c, k[5], 12, 1200080426);
+        c = ff(c, d, a, b, k[6], 17, -1473231341);
+        b = ff(b, c, d, a, k[7], 22, -45705983);
+        a = ff(a, b, c, d, k[8], 7, 1770035416);
+        d = ff(d, a, b, c, k[9], 12, -1958414417);
+        c = ff(c, d, a, b, k[10], 17, -42063);
+        b = ff(b, c, d, a, k[11], 22, -1990404162);
+        a = ff(a, b, c, d, k[12], 7, 1804603682);
+        d = ff(d, a, b, c, k[13], 12, -40341101);
+        c = ff(c, d, a, b, k[14], 17, -1502002290);
+        b = ff(b, c, d, a, k[15], 22, 1236535329);
+
+        a = gg(a, b, c, d, k[1], 5, -165796510);
+        d = gg(d, a, b, c, k[6], 9, -1069501632);
+        c = gg(c, d, a, b, k[11], 14, 643717713);
+        b = gg(b, c, d, a, k[0], 20, -373897302);
+        a = gg(a, b, c, d, k[5], 5, -701558691);
+        d = gg(d, a, b, c, k[10], 9, 38016083);
+        c = gg(c, d, a, b, k[15], 14, -660478335);
+        b = gg(b, c, d, a, k[4], 20, -405537848);
+        a = gg(a, b, c, d, k[9], 5, 568446438);
+        d = gg(d, a, b, c, k[14], 9, -1019803690);
+        c = gg(c, d, a, b, k[3], 14, -187363961);
+        b = gg(b, c, d, a, k[8], 20, 1163531501);
+        a = gg(a, b, c, d, k[13], 5, -1444681467);
+        d = gg(d, a, b, c, k[2], 9, -51403784);
+        c = gg(c, d, a, b, k[7], 14, 1735328473);
+        b = gg(b, c, d, a, k[12], 20, -1926607734);
+
+        a = hh(a, b, c, d, k[5], 4, -378558);
+        d = hh(d, a, b, c, k[8], 11, -2022574463);
+        c = hh(c, d, a, b, k[11], 16, 1839030562);
+        b = hh(b, c, d, a, k[14], 23, -35309556);
+        a = hh(a, b, c, d, k[1], 4, -1530992060);
+        d = hh(d, a, b, c, k[4], 11, 1272893353);
+        c = hh(c, d, a, b, k[7], 16, -155497632);
+        b = hh(b, c, d, a, k[10], 23, -1094730640);
+        a = hh(a, b, c, d, k[13], 4, 681279174);
+        d = hh(d, a, b, c, k[0], 11, -358537222);
+        c = hh(c, d, a, b, k[3], 16, -722521979);
+        b = hh(b, c, d, a, k[6], 23, 76029189);
+        a = hh(a, b, c, d, k[9], 4, -640364487);
+        d = hh(d, a, b, c, k[12], 11, -421815835);
+        c = hh(c, d, a, b, k[15], 16, 530742520);
+        b = hh(b, c, d, a, k[2], 23, -995338651);
+
+        a = ii(a, b, c, d, k[0], 6, -198630844);
+        d = ii(d, a, b, c, k[7], 10, 1126891415);
+        c = ii(c, d, a, b, k[14], 15, -1416354905);
+        b = ii(b, c, d, a, k[5], 21, -57434055);
+        a = ii(a, b, c, d, k[12], 6, 1700485571);
+        d = ii(d, a, b, c, k[3], 10, -1894986606);
+        c = ii(c, d, a, b, k[10], 15, -1051523);
+        b = ii(b, c, d, a, k[1], 21, -2054922799);
+        a = ii(a, b, c, d, k[8], 6, 1873313359);
+        d = ii(d, a, b, c, k[15], 10, -30611744);
+        c = ii(c, d, a, b, k[6], 15, -1560198380);
+        b = ii(b, c, d, a, k[13], 21, 1309151649);
+        a = ii(a, b, c, d, k[4], 6, -145523070);
+        d = ii(d, a, b, c, k[11], 10, -1120210379);
+        c = ii(c, d, a, b, k[2], 15, 718787259);
+        b = ii(b, c, d, a, k[9], 21, -343485551);
+
+        x[0] = add32(a, x[0]);
+        x[1] = add32(b, x[1]);
+        x[2] = add32(c, x[2]);
+        x[3] = add32(d, x[3]);
+    }
+
+    function cmn(q, a, b, x, s, t) {
+        a = add32(add32(a, q), add32(x, t));
+        return add32((a << s) | (a >>> (32 - s)), b);
+    }
+
+    function ff(a, b, c, d, x, s, t) {
+        return cmn((b & c) | ((~b) & d), a, b, x, s, t);
+    }
+
+    function gg(a, b, c, d, x, s, t) {
+        return cmn((b & d) | (c & (~d)), a, b, x, s, t);
+    }
+
+    function hh(a, b, c, d, x, s, t) {
+        return cmn(b ^ c ^ d, a, b, x, s, t);
+    }
+
+    function ii(a, b, c, d, x, s, t) {
+        return cmn(c ^ (b | (~d)), a, b, x, s, t);
+    }
+
+    function md51(s) {
+        // Converts the string to UTF-8 "bytes" when necessary
+        if (/[\x80-\xFF]/.test(s)) {
+            s = unescape(encodeURI(s));
+        }
+        txt = '';
+        let n = s.length, state = [1732584193, -271733879, -1732584194, 271733878], i;
+        for (i = 64; i <= s.length; i += 64) {
+            md5cycle(state, md5blk(s.substring(i - 64, i)));
+        }
+        s = s.substring(i - 64);
+        let tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+        for (i = 0; i < s.length; i++)
+            tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3);
+        tail[i >> 2] |= 0x80 << ((i % 4) << 3);
+        if (i > 55) {
+            md5cycle(state, tail);
+            for (i = 0; i < 16; i++) tail[i] = 0;
+        }
+        tail[14] = n * 8;
+        md5cycle(state, tail);
+        return state;
+    }
+
+    function md5blk(s) { /* I figured global was faster.   */
+        let md5blks = [], i; /* Andy King said do it this way. */
+        for (i = 0; i < 64; i += 4) {
+            md5blks[i >> 2] = s.charCodeAt(i) +
+                (s.charCodeAt(i + 1) << 8) +
+                (s.charCodeAt(i + 2) << 16) +
+                (s.charCodeAt(i + 3) << 24);
+        }
+        return md5blks;
+    }
+
+    let hex_chr = '0123456789abcdef'.split('');
+
+    function rhex(n) {
+        let s = '', j = 0;
+        for (; j < 4; j++)
+            s += hex_chr[(n >> (j * 8 + 4)) & 0x0F] +
+                hex_chr[(n >> (j * 8)) & 0x0F];
+        return s;
+    }
+
+    function hex(x) {
+        for (let i = 0; i < x.length; i++)
+            x[i] = rhex(x[i]);
+        return x.join('');
+    }
+
+    md5 = function (s) {
+        return hex(md51(s));
+    }
+
+    /* this function is much faster, so if possible we use it. Some IEs are the
+    only ones I know of that need the idiotic second function, generated by an
+    if clause.  */
+    function add32(a, b) {
+        return (a + b) & 0xFFFFFFFF;
+    }
+
+    let crypto = {
+        md5: md5
+    };
+    return crypto;
+}

+ 388 - 0
Scripts/util/OpenAPI.js

@@ -0,0 +1,388 @@
+/**
+ * OpenAPI
+ * @author: Peng-YM
+ * https://github.com/Peng-YM/QuanX/blob/master/Tools/OpenAPI/README.md
+ */
+
+function ENV() {
+    const isJSBox = typeof require == "function" && typeof $jsbox != "undefined";
+    return {
+        isQX: typeof $task !== "undefined",
+        isLoon: typeof $loon !== "undefined",
+        isSurge: typeof $httpClient !== "undefined" && typeof $utils !== "undefined",
+        isBrowser: typeof document !== "undefined",
+        isNode: typeof require == "function" && !isJSBox,
+        isJSBox,
+        isRequest: typeof $request !== "undefined",
+        isScriptable: typeof importModule !== "undefined",
+    };
+}
+
+function HTTP(defaultOptions = {
+    baseURL: ""
+}) {
+    const {
+        isQX,
+        isLoon,
+        isSurge,
+        isScriptable,
+        isNode,
+        isBrowser
+    } = ENV();
+    const methods = ["GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS", "PATCH"];
+    const URL_REGEX = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/
+
+    function send(method, options) {
+        options = typeof options === "string" ? {
+            url: options
+        } : options;
+        const baseURL = defaultOptions.baseURL;
+        if (baseURL && !URL_REGEX.test(options.url || "")) {
+            options.url = baseURL ? baseURL + options.url : options.url;
+        }
+        if (options.body && options.headers && !options.headers['Content-Type']) {
+            options.headers['Content-Type'] = 'application/x-www-form-urlencoded'
+        }
+        options = {
+            ...defaultOptions,
+            ...options
+        };
+        const timeout = options.timeout;
+        const events = {
+            ...{
+                onRequest: () => {
+                },
+                onResponse: (resp) => resp,
+                onTimeout: () => {
+                },
+            },
+            ...options.events,
+        };
+
+        events.onRequest(method, options);
+
+        let worker;
+        if (isQX) {
+            worker = $task.fetch({
+                method,
+                ...options
+            });
+        } else if (isLoon || isSurge || isNode) {
+            worker = new Promise((resolve, reject) => {
+                const request = isNode ? require("request") : $httpClient;
+                request[method.toLowerCase()](options, (err, response, body) => {
+                    if (err) reject(err);
+                    else
+                        resolve({
+                            statusCode: response.status || response.statusCode,
+                            headers: response.headers,
+                            body,
+                        });
+                });
+            });
+        } else if (isScriptable) {
+            const request = new Request(options.url);
+            request.method = method;
+            request.headers = options.headers;
+            request.body = options.body;
+            worker = new Promise((resolve, reject) => {
+                request
+                    .loadString()
+                    .then((body) => {
+                        resolve({
+                            statusCode: request.response.statusCode,
+                            headers: request.response.headers,
+                            body,
+                        });
+                    })
+                    .catch((err) => reject(err));
+            });
+        } else if (isBrowser) {
+            worker = new Promise((resolve, reject) => {
+                fetch(options.url, {
+                    method,
+                    headers: options.headers,
+                    body: options.body,
+                })
+                    .then(response => response.json())
+                    .then(response => resolve({
+                    statusCode: response.status,
+                    headers: response.headers,
+                    body: response.data,
+                })).catch(reject);
+            });
+        }
+
+        let timeoutid;
+        const timer = timeout ?
+            new Promise((_, reject) => {
+                timeoutid = setTimeout(() => {
+                    events.onTimeout();
+                    return reject(
+                        `${method} URL: ${options.url} exceeds the timeout ${timeout} ms`
+                    );
+                }, timeout);
+            }) :
+            null;
+
+        return (timer ?
+                Promise.race([timer, worker]).then((res) => {
+                    clearTimeout(timeoutid);
+                    return res;
+                }) :
+                worker
+        ).then((resp) => events.onResponse(resp));
+    }
+
+    const http = {};
+    methods.forEach(
+        (method) =>
+            (http[method.toLowerCase()] = (options) => send(method, options))
+    );
+    return http;
+}
+
+function API(name = "untitled", debug = false) {
+    const {
+        isQX,
+        isLoon,
+        isSurge,
+        isNode,
+        isJSBox,
+        isScriptable
+    } = ENV();
+    return new (class {
+        constructor(name, debug) {
+            this.name = name;
+            this.debug = debug;
+
+            this.http = HTTP();
+            this.env = ENV();
+
+            this.node = (() => {
+                if (isNode) {
+                    const fs = require("fs");
+
+                    return {
+                        fs,
+                    };
+                } else {
+                    return null;
+                }
+            })();
+            this.initCache();
+
+            const delay = (t, v) =>
+                new Promise(function (resolve) {
+                    setTimeout(resolve.bind(null, v), t);
+                });
+
+            Promise.prototype.delay = function (t) {
+                return this.then(function (v) {
+                    return delay(t, v);
+                });
+            };
+        }
+
+        // persistence
+        // initialize cache
+        initCache() {
+            if (isQX) this.cache = JSON.parse($prefs.valueForKey(this.name) || "{}");
+            if (isLoon || isSurge)
+                this.cache = JSON.parse($persistentStore.read(this.name) || "{}");
+
+            if (isNode) {
+                // create a json for root cache
+                let fpath = "root.json";
+                if (!this.node.fs.existsSync(fpath)) {
+                    this.node.fs.writeFileSync(
+                        fpath,
+                        JSON.stringify({}), {
+                            flag: "wx"
+                        },
+                        (err) => console.log(err)
+                    );
+                }
+                this.root = {};
+
+                // create a json file with the given name if not exists
+                fpath = `${this.name}.json`;
+                if (!this.node.fs.existsSync(fpath)) {
+                    this.node.fs.writeFileSync(
+                        fpath,
+                        JSON.stringify({}), {
+                            flag: "wx"
+                        },
+                        (err) => console.log(err)
+                    );
+                    this.cache = {};
+                } else {
+                    this.cache = JSON.parse(
+                        this.node.fs.readFileSync(`${this.name}.json`)
+                    );
+                }
+            }
+        }
+
+        // store cache
+        persistCache() {
+            const data = JSON.stringify(this.cache, null, 2);
+            if (isQX) $prefs.setValueForKey(data, this.name);
+            if (isLoon || isSurge) $persistentStore.write(data, this.name);
+            if (isNode) {
+                this.node.fs.writeFileSync(
+                    `${this.name}.json`,
+                    data, {
+                        flag: "w"
+                    },
+                    (err) => console.log(err)
+                );
+                this.node.fs.writeFileSync(
+                    "root.json",
+                    JSON.stringify(this.root, null, 2), {
+                        flag: "w"
+                    },
+                    (err) => console.log(err)
+                );
+            }
+        }
+
+        write(data, key) {
+            this.log(`SET ${key}`);
+            if (key.indexOf("#") !== -1) {
+                key = key.substr(1);
+                if (isSurge || isLoon) {
+                    return $persistentStore.write(data, key);
+                }
+                if (isQX) {
+                    return $prefs.setValueForKey(data, key);
+                }
+                if (isNode) {
+                    this.root[key] = data;
+                }
+            } else {
+                this.cache[key] = data;
+            }
+            this.persistCache();
+        }
+
+        read(key) {
+            this.log(`READ ${key}`);
+            if (key.indexOf("#") !== -1) {
+                key = key.substr(1);
+                if (isSurge || isLoon) {
+                    return $persistentStore.read(key);
+                }
+                if (isQX) {
+                    return $prefs.valueForKey(key);
+                }
+                if (isNode) {
+                    return this.root[key];
+                }
+            } else {
+                return this.cache[key];
+            }
+        }
+
+        delete(key) {
+            this.log(`DELETE ${key}`);
+            if (key.indexOf("#") !== -1) {
+                key = key.substr(1);
+                if (isSurge || isLoon) {
+                    return $persistentStore.write(null, key);
+                }
+                if (isQX) {
+                    return $prefs.removeValueForKey(key);
+                }
+                if (isNode) {
+                    delete this.root[key];
+                }
+            } else {
+                delete this.cache[key];
+            }
+            this.persistCache();
+        }
+
+        // notification
+        notify(title, subtitle = "", content = "", options = {}) {
+            const openURL = options["open-url"];
+            const mediaURL = options["media-url"];
+
+            if (isQX) $notify(title, subtitle, content, options);
+            if (isSurge) {
+                $notification.post(
+                    title,
+                    subtitle,
+                    content + `${mediaURL ? "\n多媒体:" + mediaURL : ""}`, {
+                        url: openURL,
+                    }
+                );
+            }
+            if (isLoon) {
+                let opts = {};
+                if (openURL) opts["openUrl"] = openURL;
+                if (mediaURL) opts["mediaUrl"] = mediaURL;
+                if (JSON.stringify(opts) === "{}") {
+                    $notification.post(title, subtitle, content);
+                } else {
+                    $notification.post(title, subtitle, content, opts);
+                }
+            }
+            if (isNode || isScriptable) {
+                const content_ =
+                    content +
+                    (openURL ? `\n点击跳转: ${openURL}` : "") +
+                    (mediaURL ? `\n多媒体: ${mediaURL}` : "");
+                if (isJSBox) {
+                    const push = require("push");
+                    push.schedule({
+                        title: title,
+                        body: (subtitle ? subtitle + "\n" : "") + content_,
+                    });
+                } else {
+                    console.log(`${title}\n${subtitle}\n${content_}\n\n`);
+                }
+            }
+        }
+
+        // other helper functions
+        log(msg) {
+            if (this.debug) console.log(`[${this.name}] LOG: ${this.stringify(msg)}`);
+        }
+
+        info(msg) {
+            console.log(`[${this.name}] INFO: ${this.stringify(msg)}`);
+        }
+
+        error(msg) {
+            console.log(`[${this.name}] ERROR: ${this.stringify(msg)}`);
+        }
+
+        wait(millisec) {
+            return new Promise((resolve) => setTimeout(resolve, millisec));
+        }
+
+        done(value = {}) {
+            if (isQX || isLoon || isSurge) {
+                $done(value);
+            } else if (isNode && !isJSBox) {
+                if (typeof $context !== "undefined") {
+                    $context.headers = value.headers;
+                    $context.statusCode = value.statusCode;
+                    $context.body = value.body;
+                }
+            }
+        }
+
+        stringify(obj_or_str) {
+            if (typeof obj_or_str === 'string' || obj_or_str instanceof String)
+                return obj_or_str;
+            else
+                try {
+                    return JSON.stringify(obj_or_str, null, 2);
+                } catch (err) {
+                    return "[object Object]";
+                }
+        }
+    })(name, debug);
+}

+ 801 - 0
Scripts/util/ToolKit.js

@@ -0,0 +1,801 @@
+/**
+ * 根据自己的习惯整合各个开发者而形成的工具包(@NobyDa, @chavyleung)
+ * 兼容surge,quantumult x,loon,node环境
+ * 并且加入一些好用的方法
+ * 方法如下:
+ *      isEmpty: 判断字符串是否是空(undefined,null,空串)
+ *      getRequestUrl: 获取请求的url(目前仅支持surge和quanx)
+ *      getResponseBody: 获取响应体(目前仅支持surge和quanx)
+ *      boxJsJsonBuilder:构建最简默认boxjs配置json
+ *      randomString: 生成随机字符串
+ *      autoComplete: 自动补齐字符串
+ *      customReplace: 自定义替换
+ *      hash: 字符串做hash
+ *
+ * ⚠️当开启当且仅当执行失败的时候通知选项,请在执行失败的地方执行execFail()
+ *
+ * @param scriptName 脚本名,用于通知时候的标题
+ * @param scriptId 每个脚本唯一的id,用于存储持久化的时候加入key
+ * @param options 传入一些参数,目前参数如下;
+ *                                      [email protected]:6166(这个是默认值,本人surge调试脚本用,可自行修改)
+ *                                      target_boxjs_json_path=/Users/lowking/Desktop/Scripts/lowking.boxjs.json(生成boxjs配置的目标文件路径)
+ * @constructor
+ */
+function ToolKit(scriptName, scriptId, options) {
+    return new (class {
+        constructor(scriptName, scriptId, options) {
+            this.tgEscapeCharMapping = {'&': '&', '#': '#'}
+            this.userAgent = `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0.2 Safari/605.1.15`
+            this.prefix = `lk`
+            this.name = scriptName
+            this.id = scriptId
+            this.data = null
+            this.dataFile = this.getRealPath(`${this.prefix}${this.id}.dat`)
+            this.boxJsJsonFile = this.getRealPath(`${this.prefix}${this.id}.boxjs.json`)
+
+            //surge http api等一些扩展参数
+            this.options = options
+
+            //命令行入参
+            this.isExecComm = false
+
+            //默认脚本开关
+            this.isEnableLog = this.getVal(`${this.prefix}IsEnableLog${this.id}`)
+            this.isEnableLog = this.isEmpty(this.isEnableLog) ? true : JSON.parse(this.isEnableLog)
+            this.isNotifyOnlyFail = this.getVal(`${this.prefix}NotifyOnlyFail${this.id}`)
+            this.isNotifyOnlyFail = this.isEmpty(this.isNotifyOnlyFail) ? false : JSON.parse(this.isNotifyOnlyFail)
+
+            //tg通知开关
+            this.isEnableTgNotify = this.getVal(`${this.prefix}IsEnableTgNotify${this.id}`)
+            this.isEnableTgNotify = this.isEmpty(this.isEnableTgNotify) ? false : JSON.parse(this.isEnableTgNotify)
+            this.tgNotifyUrl = this.getVal(`${this.prefix}TgNotifyUrl${this.id}`)
+            this.isEnableTgNotify = this.isEnableTgNotify ? !this.isEmpty(this.tgNotifyUrl) : this.isEnableTgNotify
+
+            //计时部分
+            this.costTotalStringKey = `${this.prefix}CostTotalString${this.id}`
+            this.costTotalString = this.getVal(this.costTotalStringKey)
+            this.costTotalString = this.isEmpty(this.costTotalString) ? `0,0` : this.costTotalString.replace("\"", "")
+            this.costTotalMs = this.costTotalString.split(",")[0]
+            this.execCount = this.costTotalString.split(",")[1]
+            this.costTotalMs = this.isEmpty(this.costTotalMs) ? 0 : parseInt(this.costTotalMs)
+            this.execCount = this.isEmpty(this.execCount) ? 0 : parseInt(this.execCount)
+
+            this.logSeparator = '\n██'
+            this.now = new Date()
+            this.startTime = this.now.getTime()
+            this.node = (() => {
+                if (this.isNode()) {
+                    const request = require('request')
+                    return ({request})
+                } else {
+                    return (null)
+                }
+            })()
+            this.execStatus = true
+            this.notifyInfo = []
+            this.log(`${this.name}, 开始执行!`)
+            this.execComm()
+        }
+
+        //当执行命令的目录不是脚本所在目录时,自动把文件路径改成指令传入的路径并返回完整文件路径
+        getRealPath(fileName) {
+            if (this.isNode()) {
+                let targetPath = process.argv.slice(1, 2)[0].split("/")
+                targetPath[targetPath.length - 1] = fileName
+
+                return targetPath.join("/")
+            }
+            return fileName
+        }
+
+        async execComm() {
+            //支持node命令,实现发送手机测试
+            if (this.isNode()) {
+                this.comm = process.argv.slice(1)
+                let isHttpApiErr = false
+                if (this.comm[1] == "p") {
+                    this.isExecComm = true
+                    //phone
+                    this.log(`开始执行指令【${this.comm[1]}】=> 发送到手机测试脚本!`)
+                    if (this.isEmpty(this.options) || this.isEmpty(this.options.httpApi)) {
+                        this.log(`未设置options,使用默认值`)
+                        //设置默认值
+                        if (this.isEmpty(this.options)) {
+                            this.options = {}
+                        }
+                        this.options.httpApi = `[email protected]:6166`
+                    } else {
+                        //判断格式
+                        if (!/.*?@.*?:[0-9]+/.test(this.options.httpApi)) {
+                            isHttpApiErr = true
+                            this.log(`❌httpApi格式错误!格式:[email protected]:6166`)
+                            this.done()
+                        }
+                    }
+                    if (!isHttpApiErr) {
+                        this.callApi(this.comm[2])
+                    }
+                }
+            }
+        }
+
+        callApi(timeout) {
+            // 直接用接收到文件路径,解决在不同目录下都可以使用 node xxxx/xxx.js p 指令发送脚本给手机执行
+            // let fname = this.getCallerFileNameAndLine().split(":")[0].replace("[", "")
+            let fname = this.comm[0]
+            this.log(`获取【${fname}】内容传给手机`)
+            let scriptStr = ''
+            this.fs = this.fs ? this.fs : require('fs')
+            this.path = this.path ? this.path : require('path')
+            const curDirDataFilePath = this.path.resolve(fname)
+            const rootDirDataFilePath = this.path.resolve(process.cwd(), fname)
+            const isCurDirDataFile = this.fs.existsSync(curDirDataFilePath)
+            const isRootDirDataFile = !isCurDirDataFile && this.fs.existsSync(rootDirDataFilePath)
+            if (isCurDirDataFile || isRootDirDataFile) {
+                const datPath = isCurDirDataFile ? curDirDataFilePath : rootDirDataFilePath
+                try {
+                    scriptStr = this.fs.readFileSync(datPath)
+                } catch (e) {
+                    scriptStr = ''
+                }
+            } else {
+                scriptStr = ''
+            }
+            let options = {
+                url: `http://${this.options.httpApi.split("@")[1]}/v1/scripting/evaluate`,
+                headers: {
+                    "X-Key": `${this.options.httpApi.split("@")[0]}`
+                },
+                body: {
+                    "script_text": `${scriptStr}`,
+                    "mock_type": "cron",
+                    "timeout": (!this.isEmpty(timeout) && timeout > 5) ? timeout : 5
+                },
+                json: true
+            }
+            this.post(options, (_error, _response, _data) => {
+                this.log(`已将脚本【${fname}】发给手机!`)
+                this.done()
+            })
+        }
+
+        getCallerFileNameAndLine() {
+            let error
+            try {
+                throw Error('')
+            } catch (err) {
+                error = err
+            }
+
+            const stack = error.stack
+            const stackArr = stack.split('\n')
+            let callerLogIndex = 1
+
+            if (callerLogIndex !== 0) {
+                const callerStackLine = stackArr[callerLogIndex]
+                this.path = this.path ? this.path : require('path')
+                return `[${callerStackLine.substring(callerStackLine.lastIndexOf(this.path.sep) + 1, callerStackLine.lastIndexOf(':'))}]`
+            } else {
+                return '[-]'
+            }
+        }
+
+        getFunName(fun) {
+            var ret = fun.toString()
+            ret = ret.substr('function '.length)
+            ret = ret.substr(0, ret.indexOf('('))
+            return ret
+        }
+
+        boxJsJsonBuilder(info, param) {
+            if (this.isNode()) {
+                let boxjsJsonPath = "/Users/lowking/Desktop/Scripts/lowking.boxjs.json"
+                // 从传入参数param读取配置的boxjs的json文件路径
+                if (param && param.hasOwnProperty("target_boxjs_json_path")) {
+                    boxjsJsonPath = param["target_boxjs_json_path"]
+                }
+                if (!this.fs.existsSync(boxjsJsonPath)) {
+                    return
+                }
+                if (!this.isJsonObject(info) || !this.isJsonObject(param)) {
+                    this.log("构建BoxJsJson传入参数格式错误,请传入json对象")
+                    return
+                }
+                this.log('using node')
+                let needAppendKeys = ["settings", "keys"]
+                const domain = 'https://raw.githubusercontent.com/Orz-3'
+                let boxJsJson = {}
+                let scritpUrl = '#lk{script_url}'
+                if (param && param.hasOwnProperty('script_url')) {
+                    scritpUrl = this.isEmpty(param['script_url']) ? "#lk{script_url}" : param['script_url']
+                }
+                boxJsJson.id = `${this.prefix}${this.id}`
+                boxJsJson.name = this.name
+                boxJsJson.desc_html = `⚠️使用说明</br>详情【<a href='${scritpUrl}?raw=true'><font class='red--text'>点我查看</font></a>】`
+                boxJsJson.icons = [`${domain}/mini/master/Alpha/${this.id.toLocaleLowerCase()}.png`, `${domain}/mini/master/Color/${this.id.toLocaleLowerCase()}.png`]
+                boxJsJson.keys = []
+                boxJsJson.settings = [
+                    {
+                        "id": `${this.prefix}IsEnableLog${this.id}`,
+                        "name": "开启/关闭日志",
+                        "val": true,
+                        "type": "boolean",
+                        "desc": "默认开启"
+                    },
+                    {
+                        "id": `${this.prefix}NotifyOnlyFail${this.id}`,
+                        "name": "只当执行失败才通知",
+                        "val": false,
+                        "type": "boolean",
+                        "desc": "默认关闭"
+                    },
+                    {
+                        "id": `${this.prefix}IsEnableTgNotify${this.id}`,
+                        "name": "开启/关闭Telegram通知",
+                        "val": false,
+                        "type": "boolean",
+                        "desc": "默认关闭"
+                    },
+                    {
+                        "id": `${this.prefix}TgNotifyUrl${this.id}`,
+                        "name": "Telegram通知地址",
+                        "val": "",
+                        "type": "text",
+                        "desc": "Tg的通知地址,如:https://api.telegram.org/bot-token/sendMessage?chat_id=-100140&parse_mode=Markdown&text="
+                    }
+                ]
+                boxJsJson.author = "#lk{author}"
+                boxJsJson.repo = "#lk{repo}"
+                boxJsJson.script = `${scritpUrl}?raw=true`
+                // 除了settings和keys追加,其他的都覆盖
+                if (!this.isEmpty(info)) {
+                    for (let i in needAppendKeys) {
+                        let key = needAppendKeys[i]
+                        if (!this.isEmpty(info[key])) {
+                            // 处理传入的每项设置
+                            if (key === 'settings') {
+                                for (let i = 0; i < info[key].length; i++) {
+                                    let input = info[key][i]
+                                    for (let j = 0; j < boxJsJson.settings.length; j++) {
+                                        let def = boxJsJson.settings[j]
+                                        if (input.id === def.id) {
+                                            // id相同,就使用外部传入的配置
+                                            boxJsJson.settings.splice(j, 1)
+                                        }
+                                    }
+                                }
+                            }
+                            boxJsJson[key] = boxJsJson[key].concat(info[key])
+                        }
+                        delete info[key]
+                    }
+                }
+                Object.assign(boxJsJson, info)
+                if (this.isNode()) {
+                    this.fs = this.fs ? this.fs : require('fs')
+                    this.path = this.path ? this.path : require('path')
+                    const curDirDataFilePath = this.path.resolve(this.boxJsJsonFile)
+                    const rootDirDataFilePath = this.path.resolve(process.cwd(), this.boxJsJsonFile)
+                    const isCurDirDataFile = this.fs.existsSync(curDirDataFilePath)
+                    const isRootDirDataFile = !isCurDirDataFile && this.fs.existsSync(rootDirDataFilePath)
+                    const jsondata = JSON.stringify(boxJsJson, null, '\t')
+                    if (isCurDirDataFile) {
+                        this.fs.writeFileSync(curDirDataFilePath, jsondata)
+                    } else if (isRootDirDataFile) {
+                        this.fs.writeFileSync(rootDirDataFilePath, jsondata)
+                    } else {
+                        this.fs.writeFileSync(curDirDataFilePath, jsondata)
+                    }
+                    // 写到项目的boxjs订阅json中
+                    let boxjsJson = JSON.parse(this.fs.readFileSync(boxjsJsonPath))
+                    if (boxjsJson.hasOwnProperty("apps") && Array.isArray(boxjsJson["apps"]) && boxjsJson["apps"].length > 0) {
+                        let apps = boxjsJson.apps
+                        let targetIdx = apps.indexOf(apps.filter((app) => {
+                            return app.id == boxJsJson.id
+                        })[0])
+                        if (targetIdx >= 0) {
+                            boxjsJson.apps[targetIdx] = boxJsJson
+                        } else {
+                            boxjsJson.apps.push(boxJsJson)
+                        }
+                        let ret = JSON.stringify(boxjsJson, null, 2)
+                        if (!this.isEmpty(param)) {
+                            for (const key in param) {
+                                let val = ''
+                                if (param.hasOwnProperty(key)) {
+                                    val = param[key]
+                                } else if (key === 'author') {
+                                    val = '@lowking'
+                                } else if (key === 'repo') {
+                                    val = 'https://github.com/lowking/Scripts'
+                                }
+                                ret = ret.replace(`#lk{${key}}`, val)
+                            }
+                        }
+                        // 全部处理完毕检查是否有漏掉未配置的参数,进行提醒
+                        const regex = /(?:#lk\{)(.+?)(?=\})/
+                        let m = regex.exec(ret)
+                        if (m !== null) {
+                            this.log(`生成BoxJs还有未配置的参数,请参考https://github.com/lowking/Scripts/blob/master/util/example/ToolKitDemo.js#L17-L18传入参数:\n`)
+                        }
+                        let loseParamSet = new Set()
+                        while ((m = regex.exec(ret)) !== null) {
+                            loseParamSet.add(m[1])
+                            ret = ret.replace(`#lk{${m[1]}}`, ``)
+                        }
+                        loseParamSet.forEach(p => {
+                            console.log(`${p} `)
+                        })
+                        this.fs.writeFileSync(boxjsJsonPath, ret)
+                    }
+                }
+            }
+        }
+
+        isJsonObject(obj) {
+            return typeof (obj) == "object" && Object.prototype.toString.call(obj).toLowerCase() == "[object object]" && !obj.length
+        }
+
+        appendNotifyInfo(info, type) {
+            if (type == 1) {
+                this.notifyInfo = info
+            } else {
+                this.notifyInfo.push(info)
+            }
+        }
+
+        prependNotifyInfo(info) {
+            this.notifyInfo.splice(0, 0, info)
+        }
+
+        execFail() {
+            this.execStatus = false
+        }
+
+        isRequest() {
+            return typeof $request != "undefined"
+        }
+
+        isSurge() {
+            return typeof $httpClient != "undefined"
+        }
+
+        isQuanX() {
+            return typeof $task != "undefined"
+        }
+
+        isLoon() {
+            return typeof $loon != "undefined"
+        }
+
+        isJSBox() {
+            return typeof $app != "undefined" && typeof $http != "undefined"
+        }
+
+        isStash() {
+            return 'undefined' !== typeof $environment && $environment['stash-version']
+        }
+
+        isNode() {
+            return typeof require == "function" && !this.isJSBox()
+        }
+
+        sleep(time) {
+            return new Promise((resolve) => setTimeout(resolve, time))
+        }
+
+        log(message) {
+            if (this.isEnableLog) console.log(`${this.logSeparator}${message}`)
+        }
+
+        logErr(message) {
+            this.execStatus = true
+            if (this.isEnableLog) {
+                console.log(`${this.logSeparator}${this.name}执行异常:`)
+                console.log(message)
+                console.log(`\n${message.message}`)
+            }
+        }
+
+        msg(subtitle, message, openUrl, mediaUrl) {
+            if (!this.isRequest() && this.isNotifyOnlyFail && this.execStatus) {
+                //开启了当且仅当执行失败的时候通知,并且执行成功了,这时候不通知
+            } else {
+                if (this.isEmpty(message)) {
+                    if (Array.isArray(this.notifyInfo)) {
+                        message = this.notifyInfo.join("\n")
+                    } else {
+                        message = this.notifyInfo
+                    }
+                }
+                if (!this.isEmpty(message)) {
+                    if (this.isEnableTgNotify) {
+                        this.log(`${this.name}Tg通知开始`)
+                        //处理特殊字符
+                        for (let key in this.tgEscapeCharMapping) {
+                            if (!this.tgEscapeCharMapping.hasOwnProperty(key)) {
+                                continue
+                            }
+                            message = message.replace(key, this.tgEscapeCharMapping[key])
+                        }
+                        this.get({
+                            url: encodeURI(`${this.tgNotifyUrl}📌${this.name}\n${message}`)
+                        }, (_error, _statusCode, _body) => {
+                            this.log(`Tg通知完毕`)
+                        })
+                    } else {
+                        let options = {}
+                        const hasOpenUrl = !this.isEmpty(openUrl)
+                        const hasMediaUrl = !this.isEmpty(mediaUrl)
+
+                        if (this.isQuanX()) {
+                            if (hasOpenUrl) options["open-url"] = openUrl
+                            if (hasMediaUrl) options["media-url"] = mediaUrl
+                            $notify(this.name, subtitle, message, options)
+                        }
+                        if (this.isSurge() || this.isStash()) {
+                            if (hasOpenUrl) options["url"] = openUrl
+                            $notification.post(this.name, subtitle, message, options)
+                        }
+                        if (this.isNode()) this.log("⭐️" + this.name + "\n" + subtitle + "\n" + message)
+                        if (this.isJSBox()) $push.schedule({
+                            title: this.name,
+                            body: subtitle ? subtitle + "\n" + message : message
+                        })
+                    }
+                }
+            }
+        }
+
+        getVal(key, defaultValue = "") {
+            let value
+            if (this.isSurge() || this.isLoon() || this.isStash()) {
+                value = $persistentStore.read(key)
+            } else if (this.isQuanX()) {
+                value = $prefs.valueForKey(key)
+            } else if (this.isNode()) {
+                this.data = this.loadData()
+                value = process.env[key] || this.data[key]
+            } else {
+                value = (this.data && this.data[key]) || null
+            }
+            return !value ? defaultValue : value
+        }
+
+        setVal(key, val) {
+            if (this.isSurge() || this.isLoon() || this.isStash()) {
+                return $persistentStore.write(val, key)
+            } else if (this.isQuanX()) {
+                return $prefs.setValueForKey(val, key)
+            } else if (this.isNode()) {
+                this.data = this.loadData()
+                this.data[key] = val
+                this.writeData()
+                return true
+            } else {
+                return (this.data && this.data[key]) || null
+            }
+        }
+
+        loadData() {
+            if (this.isNode()) {
+                this.fs = this.fs ? this.fs : require('fs')
+                this.path = this.path ? this.path : require('path')
+                const curDirDataFilePath = this.path.resolve(this.dataFile)
+                const rootDirDataFilePath = this.path.resolve(process.cwd(), this.dataFile)
+                const isCurDirDataFile = this.fs.existsSync(curDirDataFilePath)
+                const isRootDirDataFile = !isCurDirDataFile && this.fs.existsSync(rootDirDataFilePath)
+                if (isCurDirDataFile || isRootDirDataFile) {
+                    const datPath = isCurDirDataFile ? curDirDataFilePath : rootDirDataFilePath
+                    try {
+                        return JSON.parse(this.fs.readFileSync(datPath))
+                    } catch (e) {
+                        return {}
+                    }
+                } else return {}
+            } else return {}
+        }
+
+        writeData() {
+            if (this.isNode()) {
+                this.fs = this.fs ? this.fs : require('fs')
+                this.path = this.path ? this.path : require('path')
+                const curDirDataFilePath = this.path.resolve(this.dataFile)
+                const rootDirDataFilePath = this.path.resolve(process.cwd(), this.dataFile)
+                const isCurDirDataFile = this.fs.existsSync(curDirDataFilePath)
+                const isRootDirDataFile = !isCurDirDataFile && this.fs.existsSync(rootDirDataFilePath)
+                const jsondata = JSON.stringify(this.data)
+                if (isCurDirDataFile) {
+                    this.fs.writeFileSync(curDirDataFilePath, jsondata)
+                } else if (isRootDirDataFile) {
+                    this.fs.writeFileSync(rootDirDataFilePath, jsondata)
+                } else {
+                    this.fs.writeFileSync(curDirDataFilePath, jsondata)
+                }
+            }
+        }
+
+        adapterStatus(response) {
+            if (response) {
+                if (response.status) {
+                    response["statusCode"] = response.status
+                } else if (response.statusCode) {
+                    response["status"] = response.statusCode
+                }
+            }
+            return response
+        }
+
+        get(options, callback = () => {}) {
+            if (this.isQuanX()) {
+                if (typeof options == "string") options = {
+                    url: options
+                }
+                options["method"] = "GET"
+                $task.fetch(options).then(response => {
+                    callback(null, this.adapterStatus(response), response.body)
+                }, reason => callback(reason.error, null, null))
+            }
+            if (this.isSurge() || this.isLoon() || this.isStash()) $httpClient.get(options, (error, response, body) => {
+                callback(error, this.adapterStatus(response), body)
+            })
+            if (this.isNode()) {
+                this.node.request(options, (error, response, body) => {
+                    callback(error, this.adapterStatus(response), body)
+                })
+            }
+            if (this.isJSBox()) {
+                if (typeof options == "string") options = {
+                    url: options
+                }
+                options["header"] = options["headers"]
+                options["handler"] = function (resp) {
+                    let error = resp.error
+                    if (error) error = JSON.stringify(resp.error)
+                    let body = resp.data
+                    if (typeof body == "object") body = JSON.stringify(resp.data)
+                    callback(error, this.adapterStatus(resp.response), body)
+                }
+                $http.get(options)
+            }
+        }
+
+        post(options, callback = () => {}) {
+            if (this.isQuanX()) {
+                if (typeof options == "string") options = {
+                    url: options
+                }
+                options["method"] = "POST"
+                $task.fetch(options).then(response => {
+                    callback(null, this.adapterStatus(response), response.body)
+                }, reason => callback(reason.error, null, null))
+            }
+            if (this.isSurge() || this.isLoon() || this.isStash()) {
+                $httpClient.post(options, (error, response, body) => {
+                    callback(error, this.adapterStatus(response), body)
+                })
+            }
+            if (this.isNode()) {
+                this.node.request.post(options, (error, response, body) => {
+                    callback(error, this.adapterStatus(response), body)
+                })
+            }
+            if (this.isJSBox()) {
+                if (typeof options == "string") options = {
+                    url: options
+                }
+                options["header"] = options["headers"]
+                options["handler"] = function (resp) {
+                    let error = resp.error
+                    if (error) error = JSON.stringify(resp.error)
+                    let body = resp.data
+                    if (typeof body == "object") body = JSON.stringify(resp.data)
+                    callback(error, this.adapterStatus(resp.response), body)
+                }
+                $http.post(options)
+            }
+        }
+
+        put(options, callback = () => {}) {
+            if (this.isQuanX()) {
+                // no test
+                if (typeof options == "string") options = {
+                    url: options
+                }
+                options["method"] = "PUT"
+                $task.fetch(options).then(response => {
+                    callback(null, this.adapterStatus(response), response.body)
+                }, reason => callback(reason.error, null, null))
+            }
+            if (this.isSurge() || this.isLoon() || this.isStash()) {
+                options.method = "PUT"
+                $httpClient.put(options, (error, response, body) => {
+                    callback(error, this.adapterStatus(response), body)
+                })
+            }
+            if (this.isNode()) {
+                options.method = "PUT"
+                this.node.request.put(options, (error, response, body) => {
+                    callback(error, this.adapterStatus(response), body)
+                })
+            }
+            if (this.isJSBox()) {
+                // no test
+                if (typeof options == "string") options = {
+                    url: options
+                }
+                options["header"] = options["headers"]
+                options["handler"] = function (resp) {
+                    let error = resp.error
+                    if (error) error = JSON.stringify(resp.error)
+                    let body = resp.data
+                    if (typeof body == "object") body = JSON.stringify(resp.data)
+                    callback(error, this.adapterStatus(resp.response), body)
+                }
+                $http.post(options)
+            }
+        }
+
+        costTime() {
+            let info = `${this.name}执行完毕!`
+            if (this.isNode() && this.isExecComm) {
+                info = `指令【${this.comm[1]}】执行完毕!`
+            }
+            const endTime = new Date().getTime()
+            const ms = endTime - this.startTime
+            const costTime = ms / 1000
+            this.execCount++
+            this.costTotalMs += ms
+            this.log(`${info}耗时【${costTime}】秒\n总共执行【${this.execCount}】次,平均耗时【${((this.costTotalMs / this.execCount) / 1000).toFixed(4)}】秒`)
+            this.setVal(this.costTotalStringKey, JSON.stringify(`${this.costTotalMs},${this.execCount}`))
+            // this.setVal(this.execCountKey, JSON.stringify(0))
+            // this.setVal(this.costTotalMsKey, JSON.stringify(0))
+        }
+
+        done(value = {}) {
+            this.costTime()
+            if (this.isSurge() || this.isQuanX() || this.isLoon() || this.isStash()) {
+                $done(value)
+            }
+        }
+
+        getRequestUrl() {
+            return $request.url
+        }
+
+        getResponseBody() {
+            return $response.body
+        }
+
+        isGetCookie(reg) {
+            return !!($request.method != 'OPTIONS' && this.getRequestUrl().match(reg))
+        }
+
+        isEmpty(obj) {
+            return typeof obj == "undefined" || obj == null || obj == "" || obj == "null" || obj == "undefined" || obj.length === 0
+        }
+
+        randomString(len) {
+            len = len || 32
+            var $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890'
+            var maxPos = $chars.length
+            var pwd = ''
+            for (let i = 0; i < len; i++) {
+                pwd += $chars.charAt(Math.floor(Math.random() * maxPos))
+            }
+            return pwd
+        }
+
+        /**
+         * 自动补齐字符串
+         * @param str 原始字符串
+         * @param prefix 前缀
+         * @param suffix 后缀
+         * @param fill 补齐用字符
+         * @param len 目标补齐长度,不包含前后缀
+         * @param direction 方向:0往后补齐
+         * @param ifCode 是否打码
+         * @param clen 打码长度
+         * @param startIndex 起始坐标
+         * @param cstr 打码字符
+         * @returns {*}
+         */
+        autoComplete(str, prefix, suffix, fill, len, direction, ifCode, clen, startIndex, cstr) {
+            str += ``
+            if (str.length < len) {
+                while (str.length < len) {
+                    if (direction == 0) {
+                        str += fill
+                    } else {
+                        str = fill + str
+                    }
+                }
+            }
+            if (ifCode) {
+                let temp = ``
+                for (var i = 0; i < clen; i++) {
+                    temp += cstr
+                }
+                str = str.substring(0, startIndex) + temp + str.substring(clen + startIndex)
+            }
+            str = prefix + str + suffix
+            return this.toDBC(str)
+        }
+
+        /**
+         * @param str 源字符串 "#{code}, #{value}"
+         * @param param 用于替换的数据,结构如下
+         * @param prefix 前缀 "#{"
+         * @param suffix 后缀 "}"
+         * {
+         *     "code": 1,
+         *     "value": 2
+         * }
+         * 按上面的传入,输出为"1, 2"
+         * 对应的#{code}用param里面code的值替换,#{value}也是
+         * @returns {*|void|string}
+         */
+        customReplace(str, param, prefix, suffix) {
+            try {
+                if (this.isEmpty(prefix)) {
+                    prefix = "#{"
+                }
+                if (this.isEmpty(suffix)) {
+                    suffix = "}"
+                }
+
+                for (let i in param) {
+                    str = str.replace(`${prefix}${i}${suffix}`, param[i])
+                }
+            } catch (e) {
+                this.logErr(e)
+            }
+
+            return str
+        }
+
+        toDBC(txtstring) {
+            var tmp = ""
+            for (var i = 0; i < txtstring.length; i++) {
+                if (txtstring.charCodeAt(i) == 32) {
+                    tmp = tmp + String.fromCharCode(12288)
+                } else if (txtstring.charCodeAt(i) < 127) {
+                    tmp = tmp + String.fromCharCode(txtstring.charCodeAt(i) + 65248)
+                }
+            }
+            return tmp
+        }
+
+        hash(str) {
+            let h = 0,
+                i,
+                chr
+            for (i = 0; i < str.length; i++) {
+                chr = str.charCodeAt(i)
+                h = (h << 5) - h + chr
+                h |= 0 // Convert to 32bit integer
+            }
+            return String(h)
+        }
+
+        /**
+         * 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
+        }
+    })(scriptName, scriptId, options)
+}

Some files were not shown because too many files changed in this diff