/******************************************
 * @name 小米刷步
 * @channel https://t.me/yqc_123/
 * @feedback https://t.me/yqc_777/
 * @author 𝒀𝒖𝒉𝒆𝒏𝒈
 * @update 20240322
 * @version 1.1.4
******************************************
## 更新日志

### 20240323
    更改递增模式机制

### 20240322
    增加递增模式

### 20240229
    重构代码
    支持邮箱登录

### 20240228
    优化通知
    适配NodeJS/Surge/Loon
    新增区间自定义步数
    新增多账号适配(具体配置移动到BoxJS查看)

🙇https://raw.githubusercontent.com/577fkj/mimotion/main/main.py
### 前言
    * 📝一次对BoxJS持久化的尝试
    * 🔗使用脚本前请先添加本脚本的BoxJS订阅 ➡️ https://raw.githubusercontent.com/Yuheng0101/X/main/Tasks/boxjs.json
    * 🔍一次对工具网络请求的调试(仅对Quantumult X可阻止重定向, 其他代理工具请自测, 亦或私信我告知其阻止重定向的方法)
    * 20240228已更新支持tf版LOON和Surge, 相信在不久后会上新正式版
    * 📌使用脚本前请先配置好小米账号和密码
    * ⚠️ 脚本使用与作者无关，切勿将其用于商业用途❌，转载请注明出处✔️
```js
[task_local]
# 小米刷步
33 8 * * * https://git.vsprit.com/shawenguan/Quantumult-X/raw/master/Scripts/xiaomi/ZeppLifeHelper.js, tag=小米刷步, img-url=https://git.vsprit.com/shawenguan/Quantumult-X/raw/master/Icons/ZeppLife.png, enabled=true
```
******************************************/
const $ = new Env('小米刷步')
typeof require !== 'undefined' && require('dotenv').config()
$.message = []
$.isTrue = (val) => val === 'true' || val === true
// prettier-ignore
$.qs = { parse(ele, con_1, con_2) { con_1 = con_1 || "&", con_2 = con_2 || "="; for (var temp = ele.split(con_1), obj = {}, n = temp.length, i = 0; i < n; i++) { var tempKey = temp[i].split(con_2); obj[tempKey[0]] = tempKey[1] } return obj }, stringify(ele, con_1, con_2) { con_1 = con_1 || "&", con_2 = con_2 || "="; var str = ""; for (var key in ele) str += key + con_2 + ele[key] + con_1; return str = str.substr(0, str.length - 1) }, escape(str) { return encodeURIComponent(str) }, unescape(str) { return decodeURIComponent(str) } }
const random = (min, max) => Math.floor(Math.random() * (max - min + 1) + min)
const ObjectKeys2LowerCase = (obj) => Object.fromEntries(Object.entries(obj).map(([k, v]) => [k.toLowerCase(), v]))
// ----------------------------------------
const is_debug = $.isTrue($.isNode() ? process.env.XIAOMI_STEP_DEBUG : $.getdata('xiaomi_step_debug')) || true // 是否调试状态
// 配置参数
const enable_increment_mode = $.isTrue($.isNode() ? process.env.XIAOMI_STEP_INCREMENT_MODE : $.getdata('xiaomi_step_increment_mode')) // 是否开启增量模式
const run_count = $.isNode() ? process.env.XIAOMI_STEP_RUN_COUNT : $.getdata('xiaomi_step_run_count') || 1 // 运行次数 => 这里需要配合cron进行使用|cron运行几次，填几次
const usernames = ($.isNode() ? process.env.XIAOMI_STEP_USERNAME : $.getdata('xiaomi_step_username')) || '' // 使用&&分割多账号
const passwords = ($.isNode() ? process.env.XIAOMI_STEP_PASSWORD : $.getdata('xiaomi_step_password')) || '' // 同上
const space = ($.isNode() ? process.env.XIAOMI_STEP_SPACE : $.getdata('xiaomi_step_space')) || '10000-19999' // 区间: 使用-分隔使用&进行分割，如果存在&则匹配每个账号
const step = ($.isNode() ? process.env.XIAOMI_STEP_STEP : $.getdata('xiaomi_step_step')) || '' // 步数: 0/空为随机 // 使用&分割多账号, 不填使用随机区间
var increment_list = $.toObj($.getdata(`xiaomi_step_increment_cache_list`) || '[]') // 增量缓存
const useSpace = step ? false : true // 是否使用区间
const logger = createLogger(is_debug)
// ----------------------------------------
// 执行
!(async () => {
    if (!usernames || !passwords) throw new Error('❌请先配置小米账号(手机号)和密码')
    const userArr = usernames.split('&&')
    const pwdArr = passwords.split('&&')
    const spaceArr = space.split('&')
    const stepArr = step.split('&').filter(Boolean)
    if (userArr.length !== pwdArr.length) throw new Error('❌账号和密码数量不匹配, 请检查')
    if (spaceArr.length > 1 && spaceArr.length !== userArr.length) throw new Error('❌区间数量不匹配, 请检查')
    if (stepArr.length > 1 && stepArr.length !== userArr.length) throw new Error('❌步数数量不匹配, 请检查')
    logger.info(`用户开启${enable_increment_mode ? '递增' : '常规'}模式`)
    for (let i = 0; i < userArr.length; i++) {
        // 单账号开启递增模式 => cron多次对常规模式的账号不合适
        // const [
        //     username,
        //     /**
        //      * 刷步模式
        //      * 0 直接随机
        //      * 1 递增模式
        //      * 默认为0
        //      */
        //     mode = 0
        // ] = userArr[i].split('#')
        const username = userArr[i]
        const password = pwdArr[i]
        const userType = username.includes('@') ? 'email' : 'phone'
        const user = userType === 'phone' ? username.slice(0, 3) + '****' + username.slice(-4) : username.slice(0, 2) + '****' + username.slice(username.indexOf('@'))
        const range = spaceArr.length > 1 ? spaceArr[i] : space
        const maxStep = useSpace ? random(...range.split('-').map((i) => parseInt(i))) : stepArr.length > 1 ? stepArr[i] : step
        const startTime = $.time('yyyy-MM-dd HH:mm:ss', Date.now())
        logger.debug(`[${user}] 执行时间: ${startTime}`)
        const sec = random(1000, 2000)
        i !== 0 && logger.debug(`[${user}] 随机等待${sec}毫秒`)
        i !== 0 && (await $.wait(sec))
        /**
         * 递增模式
         * 1. 递增模式下, 用户配置的步数为最大步数
         * 2. 递增模式下, 用户配置的次数为总次数
         * 3. 递增模式下, 用户配置的区间为最大步数区间
         */
        if (enable_increment_mode) {
            const id = `${username}_${$.time('yyyyMMdd')}`
            $.cacheIndex = increment_list.findIndex((i) => i.id === id)
            if ($.cacheIndex == -1) {
                // 回收缓存
                increment_list = increment_list.filter((i) => i.id.split('_')[0] !== username)
                // 生成今日步数数组
                const randoms = getRandomSteps(maxStep)
                logger.debug(`[${user}] 生成的随机步数值: ${randoms}`)
                increment_list.push({ id, steps: randoms.map((step) => ({ step, finished: false })), maxStep })
                logger.debug('缓存数组', increment_list)
                $.setdata($.toStr(increment_list), 'xiaomi_step_increment_cache_list')
                // 重新获取下标
                $.cacheIndex = increment_list.findIndex((i) => i.id === id)
            }
            if (increment_list.find((i) => i.id === id).steps.every((i) => i.finished)) {
                logger.error(`[${user}] 今日步数已完成, 无需继续执行`)
                continue
            }
            $.currentRunIndex = increment_list.find((i) => i.id === id).steps.findIndex((i) => !i.finished)
            // 当前需要执行的步数
            $.step = increment_list.find((i) => i.id === id).steps[$.currentRunIndex].step
            logger.debug(
                `[${user}]`,
                `当前执行步数: ${$.step}步`,
                `需要执行总步数: ${increment_list[$.cacheIndex].maxStep}步`,
                `当前次数: ${$.currentRunIndex + 1}`,
                `需要运行总次数: ${run_count}`
            )
        } else {
            $.step = maxStep
            logger.debug(`[${user}] 修改步数: ${$.step}`)
        }
        try {
            var xiaomi = new Xiaomi(username, password, $.step, userType)
            var code = await xiaomi.getCode()
            var { loginToken, userId } = await xiaomi.doLogin(code)
            var appToken = await xiaomi.getAppToken(loginToken)
            await xiaomi.doStep(appToken, userId)
        } catch (e) {
            await SendNotify($.name, '', `❌账号: ${user} 任务执行失败, 请打开调试模式查看日志!`)
            logger.error(`[${user}] 执行失败`, e)
            continue
        }
        $.message.push(`登陆账号: ${user}`)
        $.message.push(`当前模式: ${enable_increment_mode ? '递增' : '常规'}`)
        useSpace && $.message.push(`设置区间: ${spaceArr.length > 1 ? spaceArr[i] : space}`)
        $.message.push(`运行时间: ${startTime}`)
        if (enable_increment_mode) {
            increment_list[$.cacheIndex].steps[$.currentRunIndex].finished = true
            logger.debug('执行完成', increment_list)
            let content = `执行结果: 成功修改步数${$.step}步`
            $.setdata($.toStr(increment_list), 'xiaomi_step_increment_cache_list')
            if ($.currentRunIndex === increment_list[$.cacheIndex].steps.length - 1) {
                logger.debug(`[${user}] 今日步数已完成, 总合计执行:${increment_list[$.cacheIndex].maxStep}步`)
                content += `, 任务完成`
            } else {
                const index = $.currentRunIndex + 1
                const nextStep = increment_list[$.cacheIndex].steps[index].step
                logger.debug(`[${user}] 下次执行步数: ${nextStep}, 总合计执行: ${increment_list[$.cacheIndex].maxStep}步`)
                content += `, 还剩${increment_list[$.cacheIndex].maxStep - $.step}步需要执行`
            }
            $.message.push(content)
        } else {
            $.message.push(`执行结果: 成功修改步数${$.step}步`)
        }
        logger.debug(`[${user}] 执行完成`)
        await SendNotify($.name, '', $.message.join('\n').replace(/\n$/, ''))
        logger.debug(`[${user}] 执行完成`)
        $.message = []
    }
})()
    .catch((e) => logger.error(`执行异常: ${e}`))
    .finally(() => $.done())
// ----------------------------------------
// 工具类
function Xiaomi(user, pwd, step, userType) {
    return new (class {
        constructor(user, pwd) {
            this.username = user
            this.password = pwd
            this.step = Number(step)
            this.userType = userType
        }
        // 获取淘宝时间 -- success
        async getTimeByTaobao() {
            const url = 'http://api.m.taobao.com/rest/api3.do?api=mtop.common.getTimestamp'
            const {
                data: { t: time }
            } = await fetchData(url)
            return time
        }

        async getTimeBySuning() {
            const url = 'https://quan.suning.com/getSysTime.do'
            const {
                sysTime1: timeStr1,
                sysTime2: timeStr2
            } = await fetchData(url)
            const date = new Date(timeStr1);
            return data.getTime();
        }

        // 登录参数 -- success
        async getCode() {
            const username = this.userType === 'email' ? encodeURIComponent(this.username) : `+86${this.username}`
            const options = {
                url: `https://api-user.huami.com/registrations/${username}/tokens`,
                headers: {
                    'User-Agent': 'MiFit/4.6.0 (iPhone; iOS 14.0.1; Scale/2.00)'
                },
                type: 'post',
                body:
                    this.userType == 'phone'
                        ? {
                            client_id: 'HuaMi',
                            password: this.password,
                            redirect_uri: 'https://s3-us-west-2.amazonaws.com/hm-registration/successsignin.html',
                            token: 'access'
                        }
                        : {
                            state: 'REDIRECTION',
                            client_id: 'HuaMi',
                            password: this.password,
                            redirect_uri: 'https://s3-us-west-2.amazonaws.com/hm-registration/successsignin.html',
                            region: 'us-west-2',
                            token: 'access',
                            country_code: 'CN'
                        },
                'auto-redirect': false, // Loon 是否自动处理重定向，默认true（build 660+）,Surge (5.21.0(3052))
                followRedirect: false, // NodeJS禁止重定向
                opts: {
                    redirection: false // 圈X禁止重定向
                },
                resultType: 'response'
            }
            const { statusCode, headers } = await fetchData(options)
            if (statusCode >= 300 && statusCode < 400) {
                const loc = $.isNode() ? headers['location'] : headers['Location']
                logger.debug('获取重定向链接', $.qs.parse(loc))
                if (!/access/.test(loc)) throw new Error('获取登录信息失败')
                const { access } = $.qs.parse(loc)
                return access
            } else {
                throw '获取登录信息失败'
            }
        }
        // 登录 -- success
        async doLogin(code) {
            const options = {
                url: 'https://account.huami.com/v2/client/login',
                type: 'post',
                headers: {
                    'User-Agent': 'MiFit/4.6.0 (iPhone; iOS 14.0.1; Scale/2.00)'
                },
                body: {
                    app_name: 'com.xiaomi.hm.health',
                    app_version: '4.6.0',
                    code,
                    country_code: 'CN',
                    device_id: '2C8B4939-0CCD-4E94-8CBA-CB8EA6E613A1',
                    device_model: 'phone',
                    grant_type: 'access_token',
                    third_name: this.userType === 'phone' ? 'huami_phone' : 'email'
                }
            }
            try {
                const data = await fetchData(options)
                logger.debug('获取登录参数', data)
                const {
                    token_info: { login_token: loginToken, user_id: userId }
                } = data
                return { loginToken, userId }
            } catch (e) {
                throw new Error('获取登录参数失败', e)
            }
        }
        // 获取appToken -- success
        async getAppToken(token) {
            var options = {
                url: `https://account-cn.huami.com/v1/client/app_tokens?app_name=com.xiaomi.hm.health&dn=api-user.huami.com%2Capi-mifit.huami.com%2Capp-analytics.huami.com&login_token=${token}&os_version=4.1.0`,
                headers: {
                    'User-Agent': 'Dalvik/2.1.0 (Linux; U; Android 9; MI 6 MIUI/20.6.18)'
                }
            }
            const data = await fetchData(options)
            logger.debug('获取appToken参数', data)
            const { result, token_info } = data
            if (result === 'ok') {
                var { app_token: appToken } = token_info
                return appToken
            } else {
                throw new Error('获取appToken失败')
            }
        }
        // 刷步 -- success
        async doStep(appToken, userId) {
            let _ts = Date.now();
            try {
                ts = await this.getTimeBySuning();
            } catch (error) {
                logger.debug('获取网络时间报错', error)
            }
            const today = $.time('yyyy-MM-dd')
            let dataJson = $.toObj($.qs.unescape(__json()))[0]
            dataJson.date = today
            // prettier-ignore
            dataJson.summary = dataJson.summary.replace(/ttl\":(.*?),\"dis/, `ttl\":${this.step},\"dis`)
            const dataStr = $.qs.escape($.toStr([dataJson]))
            const options = {
                url: `https://api-mifit-cn.huami.com/v1/data/band_data.json?&t=${_ts}`,
                type: 'post',
                headers: {
                    apptoken: appToken
                },
                body: {
                    userid: userId,
                    last_sync_data_time: 1597306380,
                    device_type: 0,
                    last_deviceid: 'DA932FFFFE8816E7',
                    data_json: dataStr
                }
            }
            try {
                const { code, message } = await fetchData(options)
                logger.debug('刷步结果', { code, message })
                if (code == 1) {
                    return true
                } else {
                    throw new Error(message)
                }
            } catch (e) {
                throw new Error('刷步失败', e)
            }
        }
    })(user, pwd)
}

/**
 * 根据最大步数和次数进行随机步数划分
 * @param {*} maxStep 最大步数
 * @param {*} length 次数/数组长度
 * @returns {Array}
 */
function getRandomSteps(maxValue, length = run_count) {
    const increment = maxValue / length
    const result = []
    for (let i = 1; i <= length; i++) {
        result.push(Math.round(increment * i))
    }
    return result
}
// prettier-ignore
function createLogger(t) { return new class { constructor(t) { this.isDebug = t, this.logs = [] } getVarType(t) { return Object.prototype.toString.call(t).slice(8, -1).toLowerCase() } formatter(t) { const e = this.getVarType(t); switch (e) { case "string": case "number": case "boolean": return t; case "array": case "object": return JSON.stringify(t); default: return String(t) } } log(t, ...e) { const r = e.map(t => this.formatter(t)).join("\n"); this.logs.push(r), console.log(t + "\n" + r) } info(...t) { this.log("----------ℹ️INFO-----------", ...t) } debug(...t) { this.isDebug && this.log("----------🛠️DEBUG----------", ...t) } warn(...t) { this.log("==========⚠️WARN===========", ...t) } error(...t) { this.log("**********❌ERROR**********", ...t) } }(t) }
// prettier-ignore
async function fetchData(e) { if ("string" == typeof e && (e = { url: e }), !e?.url) throw new Error("[发送请求] 缺少 url 参数"); try { const { url: t, type: o, headers: r, body: s, params: i, dataType: a = "form", deviceType: n = "mobile", resultType: p = "data" } = e, c = o ? o.toLowerCase() : "get", l = t.concat("post" === c ? "?" + $.qs.stringify(i) : ""), u = ObjectKeys2LowerCase(r || {}); u?.["user-agent"] || Object.assign(u, { "user-agent": "pc" === n ? "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299" : "Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1" }), "json" === a && Object.assign(u, { "content-type": "application/json;charset=UTF-8" }); const y = e?.timeout ? $.isSurge() ? e.timeout / 1e3 : e.timeout : 5e3, m = "post" === c && s && (("json" === e.dataType ? $.toStr : $.qs.stringify)("object" == typeof s ? s : "") || s), b = { ...e, ...e?.ops ? e.opts : {}, url: l, headers: u, ..."post" === c && { body: m }, ..."get" === c && i && { params: i }, timeout: y }, g = new Promise(((e, t) => { $[c](b, ((o, r, s) => { o ? t(o) : e("response" === p ? r : $.toObj(s) || s) })) })); return $.isQuanX() ? await Promise.race([new Promise(((e, t) => setTimeout((() => t(new Error("网络开小差了~"))), y))), g]) : g } catch (e) { throw new Error(e) } }
// prettier-ignore
async function SendNotify(n, o = "", i = "", t = {}) { const e = "undefined" != typeof $app && "undefined" != typeof $http, s = t["open-url"], f = t["media-url"]; if ($.isQuanX() && $notify(n, o, i, t), $.isSurge()) { const t = f ? `${i}\n多媒体:${f}` : i; $notification.post(n, o, t, { url: s }) } if ($.isLoon()) { const t = {}; s && (t.openUrl = s), f && (t.mediaUrl = f), "{}" === JSON.stringify(t) ? $notification.post(n, o, i) : $notification.post(n, o, i, t) } const c = `${i}${s ? `\n点击跳转: ${s}` : ""}${f ? `\n多媒体: ${f}` : ""}`; if (e) { require("push").schedule({ title: n, body: `${o ? `${o}\n` : ""}${c}` }) } if ($.isNode()) try { const i = require("./sendNotify"); await i.sendNotify(`${n}\n${o}`, c) } catch (n) { console.log("没有找到sendNotify.js文件") } console.log(`${n}\n${o}\n${c}\n\n`) }
// prettier-ignore
function __json() { return '%5B%7B%22data_hr%22%3A%22%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F9L%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2FVv%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F0v%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F9e%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F0n%5C%2Fa%5C%2F%5C%2F%5C%2FS%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F0b%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F1FK%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2FR%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F9PTFFpaf9L%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2FR%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F0j%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F9K%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2FOv%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2Fzf%5C%2F%5C%2F%5C%2F86%5C%2Fzr%5C%2FOv88%5C%2Fzf%5C%2FPf%5C%2F%5C%2F%5C%2F0v%5C%2FS%5C%2F8%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2FSf%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2Fz3%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F0r%5C%2FOv%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2FS%5C%2F9L%5C%2Fzb%5C%2FSf9K%5C%2F0v%5C%2FRf9H%5C%2Fzj%5C%2FSf9K%5C%2F0%5C%2F%5C%2FN%5C%2F%5C%2F%5C%2F%5C%2F0D%5C%2FSf83%5C%2Fzr%5C%2FPf9M%5C%2F0v%5C%2FOv9e%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2FS%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2Fzv%5C%2F%5C%2Fz7%5C%2FO%5C%2F83%5C%2Fzv%5C%2FN%5C%2F83%5C%2Fzr%5C%2FN%5C%2F86%5C%2Fz%5C%2F%5C%2FNv83%5C%2Fzn%5C%2FXv84%5C%2Fzr%5C%2FPP84%5C%2Fzj%5C%2FN%5C%2F9e%5C%2Fzr%5C%2FN%5C%2F89%5C%2F03%5C%2FP%5C%2F89%5C%2Fz3%5C%2FQ%5C%2F9N%5C%2F0v%5C%2FTv9C%5C%2F0H%5C%2FOf9D%5C%2Fzz%5C%2FOf88%5C%2Fz%5C%2F%5C%2FPP9A%5C%2Fzr%5C%2FN%5C%2F86%5C%2Fzz%5C%2FNv87%5C%2F0D%5C%2FOv84%5C%2F0v%5C%2FO%5C%2F84%5C%2Fzf%5C%2FMP83%5C%2FzH%5C%2FNv83%5C%2Fzf%5C%2FN%5C%2F84%5C%2Fzf%5C%2FOf82%5C%2Fzf%5C%2FOP83%5C%2Fzb%5C%2FMv81%5C%2FzX%5C%2FR%5C%2F9L%5C%2F0v%5C%2FO%5C%2F9I%5C%2F0T%5C%2FS%5C%2F9A%5C%2Fzn%5C%2FPf89%5C%2Fzn%5C%2FNf9K%5C%2F07%5C%2FN%5C%2F83%5C%2Fzn%5C%2FNv83%5C%2Fzv%5C%2FO%5C%2F9A%5C%2F0H%5C%2FOf8%5C%2F%5C%2Fzj%5C%2FPP83%5C%2Fzj%5C%2FS%5C%2F87%5C%2Fzj%5C%2FNv84%5C%2Fzf%5C%2FOf83%5C%2Fzf%5C%2FOf83%5C%2Fzb%5C%2FNv9L%5C%2Fzj%5C%2FNv82%5C%2Fzb%5C%2FN%5C%2F85%5C%2Fzf%5C%2FN%5C%2F9J%5C%2Fzf%5C%2FNv83%5C%2Fzj%5C%2FNv84%5C%2F0r%5C%2FSv83%5C%2Fzf%5C%2FMP%5C%2F%5C%2F%5C%2Fzb%5C%2FMv82%5C%2Fzb%5C%2FOf85%5C%2Fz7%5C%2FNv8%5C%2F%5C%2F0r%5C%2FS%5C%2F85%5C%2F0H%5C%2FQP9B%5C%2F0D%5C%2FNf89%5C%2Fzj%5C%2FOv83%5C%2Fzv%5C%2FNv8%5C%2F%5C%2F0f%5C%2FSv9O%5C%2F0ZeXv%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F1X%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F9B%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2FTP%5C%2F%5C%2F%5C%2F1b%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F0%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F9N%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2F%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%5C%2Fv7%2B%22%2C%22date%22%3A%222021-08-07%22%2C%22data%22%3A%5B%7B%22start%22%3A0%2C%22stop%22%3A1439%2C%22value%22%3A% %22%2C%22tz%22%3A32%2C%22did%22%3A%22DA932FFFFE8816E7%22%2C%22src%22%3A24%7D%5D%2C%22summary%22%3A%22%7B%5C%22v%5C%22%3A6%2C%5C%22slp%5C%22%3A%7B%5C%22st%5C%22%3A1628296479%2C%5C%22ed%5C%22%3A1628296479%2C%5C%22dp%5C%22%3A0%2C%5C%22lt%5C%22%3A0%2C%5C%22wk%5C%22%3A0%2C%5C%22usrSt%5C%22%3A-1440%2C%5C%22usrEd%5C%22%3A-1440%2C%5C%22wc%5C%22%3A0%2C%5C%22is%5C%22%3A0%2C%5C%22lb%5C%22%3A0%2C%5C%22to%5C%22%3A0%2C%5C%22dt%5C%22%3A0%2C%5C%22rhr%5C%22%3A0%2C%5C%22ss%5C%22%3A0%7D%2C%5C%22stp%5C%22%3A%7B%5C%22ttl%5C%22%3A18272%2C%5C%22dis%5C%22%3A10627%2C%5C%22cal%5C%22%3A510%2C%5C%22wk%5C%22%3A41%2C%5C%22rn%5C%22%3A50%2C%5C%22runDist%5C%22%3A7654%2C%5C%22runCal%5C%22%3A397%2C%5C%22stage%5C%22%3A%5B%7B%5C%22start%5C%22%3A327%2C%5C%22stop%5C%22%3A341%2C%5C%22mode%5C%22%3A1%2C%5C%22dis%5C%22%3A481%2C%5C%22cal%5C%22%3A13%2C%5C%22step%5C%22%3A680%7D%2C%7B%5C%22start%5C%22%3A342%2C%5C%22stop%5C%22%3A367%2C%5C%22mode%5C%22%3A3%2C%5C%22dis%5C%22%3A2295%2C%5C%22cal%5C%22%3A95%2C%5C%22step%5C%22%3A2874%7D%2C%7B%5C%22start%5C%22%3A368%2C%5C%22stop%5C%22%3A377%2C%5C%22mode%5C%22%3A4%2C%5C%22dis%5C%22%3A1592%2C%5C%22cal%5C%22%3A88%2C%5C%22step%5C%22%3A1664%7D%2C%7B%5C%22start%5C%22%3A378%2C%5C%22stop%5C%22%3A386%2C%5C%22mode%5C%22%3A3%2C%5C%22dis%5C%22%3A1072%2C%5C%22cal%5C%22%3A51%2C%5C%22step%5C%22%3A1245%7D%2C%7B%5C%22start%5C%22%3A387%2C%5C%22stop%5C%22%3A393%2C%5C%22mode%5C%22%3A4%2C%5C%22dis%5C%22%3A1036%2C%5C%22cal%5C%22%3A57%2C%5C%22step%5C%22%3A1124%7D%2C%7B%5C%22start%5C%22%3A394%2C%5C%22stop%5C%22%3A398%2C%5C%22mode%5C%22%3A3%2C%5C%22dis%5C%22%3A488%2C%5C%22cal%5C%22%3A19%2C%5C%22step%5C%22%3A607%7D%2C%7B%5C%22start%5C%22%3A399%2C%5C%22stop%5C%22%3A414%2C%5C%22mode%5C%22%3A4%2C%5C%22dis%5C%22%3A2220%2C%5C%22cal%5C%22%3A120%2C%5C%22step%5C%22%3A2371%7D%2C%7B%5C%22start%5C%22%3A415%2C%5C%22stop%5C%22%3A427%2C%5C%22mode%5C%22%3A3%2C%5C%22dis%5C%22%3A1268%2C%5C%22cal%5C%22%3A59%2C%5C%22step%5C%22%3A1489%7D%2C%7B%5C%22start%5C%22%3A428%2C%5C%22stop%5C%22%3A433%2C%5C%22mode%5C%22%3A1%2C%5C%22dis%5C%22%3A152%2C%5C%22cal%5C%22%3A4%2C%5C%22step%5C%22%3A238%7D%2C%7B%5C%22start%5C%22%3A434%2C%5C%22stop%5C%22%3A444%2C%5C%22mode%5C%22%3A3%2C%5C%22dis%5C%22%3A2295%2C%5C%22cal%5C%22%3A95%2C%5C%22step%5C%22%3A2874%7D%2C%7B%5C%22start%5C%22%3A445%2C%5C%22stop%5C%22%3A455%2C%5C%22mode%5C%22%3A4%2C%5C%22dis%5C%22%3A1592%2C%5C%22cal%5C%22%3A88%2C%5C%22step%5C%22%3A1664%7D%2C%7B%5C%22start%5C%22%3A456%2C%5C%22stop%5C%22%3A466%2C%5C%22mode%5C%22%3A3%2C%5C%22dis%5C%22%3A1072%2C%5C%22cal%5C%22%3A51%2C%5C%22step%5C%22%3A1245%7D%2C%7B%5C%22start%5C%22%3A467%2C%5C%22stop%5C%22%3A477%2C%5C%22mode%5C%22%3A4%2C%5C%22dis%5C%22%3A1036%2C%5C%22cal%5C%22%3A57%2C%5C%22step%5C%22%3A1124%7D%2C%7B%5C%22start%5C%22%3A478%2C%5C%22stop%5C%22%3A488%2C%5C%22mode%5C%22%3A3%2C%5C%22dis%5C%22%3A488%2C%5C%22cal%5C%22%3A19%2C%5C%22step%5C%22%3A607%7D%2C%7B%5C%22start%5C%22%3A489%2C%5C%22stop%5C%22%3A499%2C%5C%22mode%5C%22%3A4%2C%5C%22dis%5C%22%3A2220%2C%5C%22cal%5C%22%3A120%2C%5C%22step%5C%22%3A2371%7D%2C%7B%5C%22start%5C%22%3A500%2C%5C%22stop%5C%22%3A511%2C%5C%22mode%5C%22%3A3%2C%5C%22dis%5C%22%3A1268%2C%5C%22cal%5C%22%3A59%2C%5C%22step%5C%22%3A1489%7D%2C%7B%5C%22start%5C%22%3A512%2C%5C%22stop%5C%22%3A522%2C%5C%22mode%5C%22%3A1%2C%5C%22dis%5C%22%3A152%2C%5C%22cal%5C%22%3A4%2C%5C%22step%5C%22%3A238%7D%5D%7D%2C%5C%22goal%5C%22%3A8000%2C%5C%22tz%5C%22%3A%5C%2228800%5C%22%7D%22%2C%22source%22%3A24%2C%22type%22%3A0%7D%5D' }
// prettier-ignore
function Env(t, e) { class s { constructor(t) { this.env = t } send(t, e = "GET") { t = "string" == typeof t ? { url: t } : t; let s = this.get; return "POST" === e && (s = this.post), new Promise((e, a) => { s.call(this, t, (t, s, r) => { t ? a(t) : e(s) }) }) } get(t) { return this.send.call(this.env, t) } post(t) { return this.send.call(this.env, t, "POST") } } return new class { constructor(t, e) { this.name = t, this.http = new s(this), this.data = null, this.dataFile = "box.dat", this.logs = [], this.isMute = !1, this.isNeedRewrite = !1, this.logSeparator = "\n", this.encoding = "utf-8", this.startTime = (new Date).getTime(), Object.assign(this, e), this.log("", `🔔${this.name}, 开始!`) } getEnv() { return "undefined" != typeof $environment && $environment["surge-version"] ? "Surge" : "undefined" != typeof $environment && $environment["stash-version"] ? "Stash" : "undefined" != typeof module && module.exports ? "Node.js" : "undefined" != typeof $task ? "Quantumult X" : "undefined" != typeof $loon ? "Loon" : "undefined" != typeof $rocket ? "Shadowrocket" : void 0 } isNode() { return "Node.js" === this.getEnv() } isQuanX() { return "Quantumult X" === this.getEnv() } isSurge() { return "Surge" === this.getEnv() } isLoon() { return "Loon" === this.getEnv() } isShadowrocket() { return "Shadowrocket" === this.getEnv() } isStash() { return "Stash" === this.getEnv() } toObj(t, e = null) { try { return JSON.parse(t) } catch { return e } } toStr(t, e = null) { try { return JSON.stringify(t) } catch { return e } } getjson(t, e) { let s = e; const a = this.getdata(t); if (a) try { s = JSON.parse(this.getdata(t)) } catch { } return s } setjson(t, e) { try { return this.setdata(JSON.stringify(t), e) } catch { return !1 } } getScript(t) { return new Promise(e => { this.get({ url: t }, (t, s, a) => e(a)) }) } runScript(t, e) { return new Promise(s => { let a = this.getdata("@chavy_boxjs_userCfgs.httpapi"); a = a ? a.replace(/\n/g, "").trim() : a; let r = this.getdata("@chavy_boxjs_userCfgs.httpapi_timeout"); r = r ? 1 * r : 20, r = e && e.timeout ? e.timeout : r; const [i, o] = a.split("@"), n = { url: `http://${o}/v1/scripting/evaluate`, body: { script_text: t, mock_type: "cron", timeout: r }, headers: { "X-Key": i, Accept: "*/*" }, timeout: r }; this.post(n, (t, e, a) => s(a)) }).catch(t => this.logErr(t)) } loaddata() { if (!this.isNode()) return {}; { this.fs = this.fs ? this.fs : require("fs"), this.path = this.path ? this.path : require("path"); const t = this.path.resolve(this.dataFile), e = this.path.resolve(process.cwd(), this.dataFile), s = this.fs.existsSync(t), a = !s && this.fs.existsSync(e); if (!s && !a) return {}; { const a = s ? t : e; try { return JSON.parse(this.fs.readFileSync(a)) } catch (t) { return {} } } } } writedata() { if (this.isNode()) { this.fs = this.fs ? this.fs : require("fs"), this.path = this.path ? this.path : require("path"); const t = this.path.resolve(this.dataFile), e = this.path.resolve(process.cwd(), this.dataFile), s = this.fs.existsSync(t), a = !s && this.fs.existsSync(e), r = JSON.stringify(this.data); s ? this.fs.writeFileSync(t, r) : a ? this.fs.writeFileSync(e, r) : this.fs.writeFileSync(t, r) } } lodash_get(t, e, s) { const a = e.replace(/\[(\d+)\]/g, ".$1").split("."); let r = t; for (const t of a) if (r = Object(r)[t], void 0 === r) return s; return r } lodash_set(t, e, s) { return Object(t) !== t ? t : (Array.isArray(e) || (e = e.toString().match(/[^.[\]]+/g) || []), e.slice(0, -1).reduce((t, s, a) => Object(t[s]) === t[s] ? t[s] : t[s] = Math.abs(e[a + 1]) >> 0 == +e[a + 1] ? [] : {}, t)[e[e.length - 1]] = s, t) } getdata(t) { let e = this.getval(t); if (/^@/.test(t)) { const [, s, a] = /^@(.*?)\.(.*?)$/.exec(t), r = s ? this.getval(s) : ""; if (r) try { const t = JSON.parse(r); e = t ? this.lodash_get(t, a, "") : e } catch (t) { e = "" } } return e } setdata(t, e) { let s = !1; if (/^@/.test(e)) { const [, a, r] = /^@(.*?)\.(.*?)$/.exec(e), i = this.getval(a), o = a ? "null" === i ? null : i || "{}" : "{}"; try { const e = JSON.parse(o); this.lodash_set(e, r, t), s = this.setval(JSON.stringify(e), a) } catch (e) { const i = {}; this.lodash_set(i, r, t), s = this.setval(JSON.stringify(i), a) } } else s = this.setval(t, e); return s } getval(t) { switch (this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": return $persistentStore.read(t); case "Quantumult X": return $prefs.valueForKey(t); case "Node.js": return this.data = this.loaddata(), this.data[t]; default: return this.data && this.data[t] || null } } setval(t, e) { switch (this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": return $persistentStore.write(t, e); case "Quantumult X": return $prefs.setValueForKey(t, e); case "Node.js": return this.data = this.loaddata(), this.data[e] = t, this.writedata(), !0; default: return this.data && this.data[e] || null } } initGotEnv(t) { this.got = this.got ? this.got : require("got"), this.cktough = this.cktough ? this.cktough : require("tough-cookie"), this.ckjar = this.ckjar ? this.ckjar : new this.cktough.CookieJar, t && (t.headers = t.headers ? t.headers : {}, void 0 === t.headers.Cookie && void 0 === t.cookieJar && (t.cookieJar = this.ckjar)) } get(t, e = (() => { })) { switch (t.headers && (delete t.headers["Content-Type"], delete t.headers["Content-Length"], delete t.headers["content-type"], delete t.headers["content-length"]), t.params && (t.url += "?" + this.queryStr(t.params)), this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": default: this.isSurge() && this.isNeedRewrite && (t.headers = t.headers || {}, Object.assign(t.headers, { "X-Surge-Skip-Scripting": !1 })), $httpClient.get(t, (t, s, a) => { !t && s && (s.body = a, s.statusCode = s.status ? s.status : s.statusCode, s.status = s.statusCode), e(t, s, a) }); break; case "Quantumult X": this.isNeedRewrite && (t.opts = t.opts || {}, Object.assign(t.opts, { hints: !1 })), $task.fetch(t).then(t => { const { statusCode: s, statusCode: a, headers: r, body: i, bodyBytes: o } = t; e(null, { status: s, statusCode: a, headers: r, body: i, bodyBytes: o }, i, o) }, t => e(t && t.error || "UndefinedError")); break; case "Node.js": let s = require("iconv-lite"); this.initGotEnv(t), this.got(t).on("redirect", (t, e) => { try { if (t.headers["set-cookie"]) { const s = t.headers["set-cookie"].map(this.cktough.Cookie.parse).toString(); s && this.ckjar.setCookieSync(s, null), e.cookieJar = this.ckjar } } catch (t) { this.logErr(t) } }).then(t => { const { statusCode: a, statusCode: r, headers: i, rawBody: o } = t, n = s.decode(o, this.encoding); e(null, { status: a, statusCode: r, headers: i, rawBody: o, body: n }, n) }, t => { const { message: a, response: r } = t; e(a, r, r && s.decode(r.rawBody, this.encoding)) }) } } post(t, e = (() => { })) { const s = t.method ? t.method.toLocaleLowerCase() : "post"; switch (t.body && t.headers && !t.headers["Content-Type"] && !t.headers["content-type"] && (t.headers["content-type"] = "application/x-www-form-urlencoded"), t.headers && (delete t.headers["Content-Length"], delete t.headers["content-length"]), this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": default: this.isSurge() && this.isNeedRewrite && (t.headers = t.headers || {}, Object.assign(t.headers, { "X-Surge-Skip-Scripting": !1 })), $httpClient[s](t, (t, s, a) => { !t && s && (s.body = a, s.statusCode = s.status ? s.status : s.statusCode, s.status = s.statusCode), e(t, s, a) }); break; case "Quantumult X": t.method = s, this.isNeedRewrite && (t.opts = t.opts || {}, Object.assign(t.opts, { hints: !1 })), $task.fetch(t).then(t => { const { statusCode: s, statusCode: a, headers: r, body: i, bodyBytes: o } = t; e(null, { status: s, statusCode: a, headers: r, body: i, bodyBytes: o }, i, o) }, t => e(t && t.error || "UndefinedError")); break; case "Node.js": let a = require("iconv-lite"); this.initGotEnv(t); const { url: r, ...i } = t; this.got[s](r, i).then(t => { const { statusCode: s, statusCode: r, headers: i, rawBody: o } = t, n = a.decode(o, this.encoding); e(null, { status: s, statusCode: r, headers: i, rawBody: o, body: n }, n) }, t => { const { message: s, response: r } = t; e(s, r, r && a.decode(r.rawBody, this.encoding)) }) } } time(t, e = null) { const s = e ? new Date(e) : new Date; let a = { "M+": s.getMonth() + 1, "d+": s.getDate(), "H+": s.getHours(), "m+": s.getMinutes(), "s+": s.getSeconds(), "q+": Math.floor((s.getMonth() + 3) / 3), S: s.getMilliseconds() }; /(y+)/.test(t) && (t = t.replace(RegExp.$1, (s.getFullYear() + "").substr(4 - RegExp.$1.length))); for (let e in a) new RegExp("(" + e + ")").test(t) && (t = t.replace(RegExp.$1, 1 == RegExp.$1.length ? a[e] : ("00" + a[e]).substr(("" + a[e]).length))); return t } queryStr(t) { let e = ""; for (const s in t) { let a = t[s]; null != a && "" !== a && ("object" == typeof a && (a = JSON.stringify(a)), e += `${s}=${a}&`) } return e = e.substring(0, e.length - 1), e } msg(e = t, s = "", a = "", r) { const i = t => { switch (typeof t) { case void 0: return t; case "string": switch (this.getEnv()) { case "Surge": case "Stash": default: return { url: t }; case "Loon": case "Shadowrocket": return t; case "Quantumult X": return { "open-url": t }; case "Node.js": return }case "object": switch (this.getEnv()) { case "Surge": case "Stash": case "Shadowrocket": default: { let e = t.url || t.openUrl || t["open-url"]; return { url: e } } case "Loon": { let e = t.openUrl || t.url || t["open-url"], s = t.mediaUrl || t["media-url"]; return { openUrl: e, mediaUrl: s } } case "Quantumult X": { let e = t["open-url"] || t.url || t.openUrl, s = t["media-url"] || t.mediaUrl, a = t["update-pasteboard"] || t.updatePasteboard; return { "open-url": e, "media-url": s, "update-pasteboard": a } } case "Node.js": return }default: return } }; if (!this.isMute) switch (this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": default: $notification.post(e, s, a, i(r)); break; case "Quantumult X": $notify(e, s, a, i(r)); break; case "Node.js": }if (!this.isMuteLog) { let t = ["", "==============📣系统通知📣=============="]; t.push(e), s && t.push(s), a && t.push(a), console.log(t.join("\n")), this.logs = this.logs.concat(t) } } log(...t) { t.length > 0 && (this.logs = [...this.logs, ...t]), console.log(t.join(this.logSeparator)) } logErr(t, e) { switch (this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": case "Quantumult X": default: this.log("", `❗️${this.name}, 错误!`, t); break; case "Node.js": this.log("", `❗️${this.name}, 错误!`, t.stack) } } wait(t) { return new Promise(e => setTimeout(e, t)) } done(t = {}) { const e = (new Date).getTime(), s = (e - this.startTime) / 1e3; switch (this.log("", `🔔${this.name}, 结束! 🕛 ${s} 秒`), this.log(), this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": case "Quantumult X": default: $done(t); break; case "Node.js": process.exit(1) } } }(t, e) }