Full Code of NobyDa/Script for AI

master 7f8b309f8d94 cached
90 files
1.1 MB
423.0k tokens
152 symbols
1 requests
Download .txt
Showing preview only (1,199K chars total). Download the full file or copy to clipboard to get everything.
Repository: NobyDa/Script
Branch: master
Commit: 7f8b309f8d94
Files: 90
Total size: 1.1 MB

Directory structure:
gitextract_h2qjq_wb/

├── 52pojie-DailyBonus/
│   └── 52pojie.js
├── BDTieBa-DailyBonus/
│   └── TieBa.js
├── Bahamut/
│   ├── BahamutAnimeAds.js
│   └── BahamutDailyBonus.js
├── Bilibili-DailyBonus/
│   ├── ExchangePoints.js
│   └── Manga.js
├── Ctrip-DailyBonus/
│   └── Ctrip.js
├── Debug/
│   └── Real-time-debug.js
├── Disney/
│   └── DisneyRating.js
├── IPA-Installer/
│   ├── IPA-Installer-JSBox.js
│   ├── IPA-Installer-Pythonista.py
│   └── IPA-Installer.js
├── JD-DailyBonus/
│   └── JD_DailyBonus.js
├── KuaiKan-DailyBonus/
│   └── KKMH.js
├── LICENSE
├── Loon/
│   ├── Loon_Bahamut_ADS.plugin
│   ├── Loon_Daily_bonus.plugin
│   ├── Loon_GetCookie.plugin
│   ├── Loon_Google_CAPTCHA.plugin
│   ├── Loon_IPA_Installer.plugin
│   ├── Loon_TF_Account.plugin
│   └── Loon_TF_Download.plugin
├── NobyDa_BoxJs.json
├── QuantumultX/
│   ├── AdRule.list
│   ├── AdRuleTest.list
│   ├── Bilibili.list
│   ├── DisneyRating.snippet
│   ├── File/
│   │   ├── 91.js
│   │   ├── Wechat.js
│   │   ├── Zymh.js
│   │   ├── vsco.js
│   │   ├── wnyd.js
│   │   └── xjsp.js
│   ├── IPA-Installer.snippet
│   ├── Js.conf
│   ├── Rewrite_lhie1.conf
│   ├── Snippet/
│   │   ├── BiliComicCookie.snippet
│   │   ├── CtripAuth.snippet
│   │   ├── GoogleCAPTCHA.snippet
│   │   ├── KuaiKanCookie.snippet
│   │   ├── TieBaCookie.snippet
│   │   └── iQiYiCookie.snippet
│   └── TestFlightDownload.conf
├── README.md
├── Rule-Storage/
│   ├── Include-Domain.txt
│   └── Rule-Storage.js
├── Shortcuts/
│   └── PolicySwitch.js
├── Stash/
│   └── IPA-Installer.stoverride
├── Sub-store-parser/
│   └── DataQuery.js
├── Surge/
│   ├── AdRule.list
│   ├── AdRuleTest.list
│   ├── Apple.list
│   ├── Bilibili.list
│   ├── Download.list
│   ├── JS/
│   │   ├── BaiduCloud.js
│   │   ├── Bili_Auto_Regions.js
│   │   ├── CamScanner.js
│   │   ├── Google_CAPTCHA.js
│   │   ├── Kuwo.js
│   │   ├── MIX.js
│   │   ├── NiChi.js
│   │   ├── PicsArt.js
│   │   ├── Polarr.js
│   │   ├── Super.js
│   │   ├── VUE.js
│   │   ├── Wps.js
│   │   ├── jibjab.js
│   │   ├── luqi.js
│   │   └── vivavideo.js
│   ├── Module/
│   │   ├── BahamutAnimeAds.sgmodule
│   │   ├── BahamutDailyBonus.sgmodule
│   │   ├── BiliComicsDailyBonus.sgmodule
│   │   ├── BiliComicsExchangePoints.sgmodule
│   │   ├── CtripDailyBonus.sgmodule
│   │   ├── DisneyRating.sgmodule
│   │   ├── GetCookie.sgmodule
│   │   ├── GoogleCAPTCHA.sgmodule
│   │   ├── HuiJuDongManAds.sgmodule
│   │   ├── IPA_install.sgmodule
│   │   ├── KuaiKanComicsDailyBonus.sgmodule
│   │   ├── NewBing.sgmodule
│   │   ├── RewriteRules.sgmodule
│   │   ├── TestFlightAccount.sgmodule
│   │   ├── TestFlightDownload.sgmodule
│   │   ├── TieBaDailyBonus.sgmodule
│   │   └── iQIYIDailyBonus.sgmodule
│   └── WeChat.list
├── TestFlight/
│   └── TestFlightAccount.js
├── Time-based-One-Time-Password/
│   └── README.md
└── iQIYI-DailyBonus/
    └── iQIYI.js

================================================
FILE CONTENTS
================================================

================================================
FILE: 52pojie-DailyBonus/52pojie.js
================================================
/*
吾爱破解签到脚本

更新时间: 2022.6.18
脚本兼容: QuantumultX, Surge, Loon, Node.js
电报频道: @NobyDa
问题反馈: @NobyDa_bot

************************
QX, Surge, Loon说明:
************************
手动登录 https://www.52pojie.cn/home.php 如通知成功获取cookie, 则可以使用此签到脚本.
获取Cookie后, 请将Cookie脚本禁用并移除主机名, 以免产生不必要的MITM.
脚本将在每天上午9点执行, 您可以修改执行时间.

************************
Node.js说明: 
************************
需自行安装"got"与"iconv-lite"模块. 例: npm install got iconv-lite -g

抓取Cookie说明:
浏览器打开 https://www.52pojie.cn/home.php 登录账号后, 开启抓包软件并刷新页面.
抓取该URL请求头下的Cookie字段, 填入以下CookieWA的单引号内即可. */

const CookieWA = '';

//Bark APP 通知推送Key
const barkKey = '';

/***********************
Surge 4.2.0+ 脚本配置:
************************

[Script]
吾爱签到 = type=cron,cronexp=0 9 * * *,script-path=https://raw.githubusercontent.com/NobyDa/Script/master/52pojie-DailyBonus/52pojie.js

吾爱获取Cookie = type=http-request,pattern=https:\/\/www\.52pojie\.cn\/home\.php\?,script-path=https://raw.githubusercontent.com/NobyDa/Script/master/52pojie-DailyBonus/52pojie.js

[MITM] 
hostname= www.52pojie.cn

************************
QuantumultX 远程脚本配置:
************************

[task_local]
# 吾爱签到
0 9 * * * https://raw.githubusercontent.com/NobyDa/Script/master/52pojie-DailyBonus/52pojie.js

[rewrite_local]
# 获取Cookie
https:\/\/www\.52pojie\.cn\/home\.php\? url script-request-header https://raw.githubusercontent.com/NobyDa/Script/master/52pojie-DailyBonus/52pojie.js

[mitm] 
hostname= www.52pojie.cn

************************
Loon 2.1.0+ 脚本配置:
************************

[Script]
# 吾爱签到
cron "0 9 * * *" script-path=https://raw.githubusercontent.com/NobyDa/Script/master/52pojie-DailyBonus/52pojie.js

# 获取Cookie
http-request https:\/\/www\.52pojie\.cn\/home\.php\? script-path=https://raw.githubusercontent.com/NobyDa/Script/master/52pojie-DailyBonus/52pojie.js

[Mitm] 
hostname= www.52pojie.cn
*/

const $ = API('nobyda_52pojie');
const date = new Date();
const reqData = {
  url: 'https://www.52pojie.cn/home.php?mod=task&do=apply&id=2',
  headers: {
    Cookie: CookieWA || $.read("COOKIE"),
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:101.0) Gecko/20100101 Firefox/101.0",
  }
};
if ($.env.isRequest) {
  GetCookie()
} else if (!reqData.headers.Cookie) {
  $.notify('吾爱破解', ``, `未填写/未获取Cookie!`);
} else if (!reqData.headers.Cookie.includes('_auth=')) {
  $.notify('吾爱破解', ``, `Cookie关键授权字段缺失, 需重新获取!`);
} else {
  $.http.put(reqData)
    .then((resp) => {
      if (resp.body.match(/(ÒÑÍê³É|\u606d\u559c\u60a8|��̳΢�š��ᰮ�ƽ�)/)) {
        $.msgBody = date.getMonth() + 1 + "月" + date.getDate() + "日, 签到成功 🎉"
      } else if (resp.body.match(/(ÄúÒÑ|\u4e0b\u671f\u518d\u6765|>��Ǹ������)/)) {
        $.msgBody = date.getMonth() + 1 + "月" + date.getDate() + "日, 已签过 ⚠️"
      } else if (resp.body.match(/(ÏȵǼ|\u9700\u8981\u5148\u767b\u5f55|�Ҫ�ȵ�¼���ܼ�)/)) {
        $.msgBody = "签到失败, Cookie失效 ‼️‼️"
      } else if (resp.statusCode == 403) {
        $.msgBody = "服务器暂停签到 ⚠️"
      } else {
        $.msgBody = "脚本待更新 ‼️‼️"
      }
    })
    .catch((err) => ($.msgBody = `签到失败 ‼️‼️\n${err || err.message}`))
    .finally(async () => {
      if (barkKey) {
        await BarkNotify($, barkKey, '吾爱破解', $.msgBody);
      }
      $.notify('吾爱破解', ``, $.msgBody);
      $.done();
    })
}

function GetCookie() {
  const TM = $.read("TIME");
  const CK = $request.headers['Cookie'] || $request.headers['cookie'];
  if (CK && CK.includes('_auth=')) {
    $.write(CK, "COOKIE");
    if (!TM || TM && (Date.now() - TM) / 1000 >= 21600) {
      $.notify("吾爱破解", "", `写入Cookie成功 🎉`);
      $.write(JSON.stringify(Date.now()), "TIME");
    } else {
      $.info(`吾爱破解\n写入Cookie成功 🎉`)
    }
  } else {
    $.info(`吾爱破解\n写入Cookie失败, 关键值缺失`)
  }
  $.done()
}

//Bark APP notify
async function BarkNotify(c, k, t, b) { for (let i = 0; i < 3; i++) { console.log(`🔷Bark notify >> Start push (${i + 1})`); const s = await new Promise((n) => { c.post({ url: 'https://api.day.app/push', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ title: t, body: b, device_key: k, ext_params: { group: t } }) }, (e, r, d) => r && r.status == 200 ? n(1) : n(d || e)) }); if (s === 1) { console.log('✅Push success!'); break } else { console.log(`❌Push failed! >> ${s.message || s}`) } } };

//https://github.com/Peng-YM/QuanX/tree/master/Tools/OpenAPI
function ENV() { const e = "function" == typeof require && "undefined" != typeof $jsbox; return { isQX: "undefined" != typeof $task, isLoon: "undefined" != typeof $loon, isSurge: "undefined" != typeof $httpClient && "undefined" == typeof $loon, isBrowser: "undefined" != typeof document, isNode: "function" == typeof require && !e, isJSBox: e, isRequest: "undefined" != typeof $request, isScriptable: "undefined" != typeof importModule } } function HTTP(e = { baseURL: "" }) { function t(t, a) { a = "string" == typeof a ? { url: a } : a; const h = e.baseURL; h && !d.test(a.url || "") && (a.url = h ? h + a.url : a.url), a.body && a.headers && !a.headers["Content-Type"] && (a.headers["Content-Type"] = "application/x-www-form-urlencoded"), a = { ...e, ...a }; const c = a.timeout, l = { onRequest: () => { }, onResponse: e => e, onTimeout: () => { }, ...a.events }; let f, y; if (l.onRequest(t, a), s) f = $task.fetch({ method: t, ...a }); else if (o || n) f = new Promise((e, s) => { $httpClient[t.toLowerCase()](a, (t, o, n) => { t ? s(t) : e({ statusCode: o.status || o.statusCode, headers: o.headers, body: n }) }) }); else if (r) { const e = require("got"), s = require("iconv-lite"); f = new Promise((o, n) => { e[t.toLowerCase()](a).then(e => o({ statusCode: e.statusCode, headers: e.headers, body: s.decode(e.rawBody, "utf-8") })).catch(n) }) } else if (i) { const e = new Request(a.url); e.method = t, e.headers = a.headers, e.body = a.body, f = new Promise((t, s) => { e.loadString().then(s => { t({ statusCode: e.response.statusCode, headers: e.response.headers, body: s }) }).catch(e => s(e)) }) } else u && (f = new Promise((e, s) => { fetch(a.url, { method: t, headers: a.headers, body: a.body }).then(e => e.json()).then(t => e({ statusCode: t.status, headers: t.headers, body: t.data })).catch(s) })); const p = c ? new Promise((e, s) => { y = setTimeout(() => (l.onTimeout(), s(`${t} URL: ${a.url} exceeds the timeout ${c} ms`)), c) }) : null; return (p ? Promise.race([p, f]).then(e => (clearTimeout(y), e)) : f).then(e => l.onResponse(e)) } const { isQX: s, isLoon: o, isSurge: n, isScriptable: i, isNode: r, isBrowser: u } = ENV(), a = ["GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS", "PATCH"], d = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/, h = {}; return a.forEach(e => h[e.toLowerCase()] = (s => t(e, s))), h } function API(e = "untitled", t = !1) { const { isQX: s, isLoon: o, isSurge: n, isNode: i, isJSBox: r, isScriptable: u } = ENV(); return new class { constructor(e, t) { this.name = e, this.debug = t, this.http = HTTP(), this.env = ENV(), this.node = (() => { if (i) { const e = require("fs"); return { fs: e } } return null })(), this.initCache(); const s = (e, t) => new Promise(function (s) { setTimeout(s.bind(null, t), e) }); Promise.prototype.delay = function (e) { return this.then(function (t) { return s(e, t) }) } } initCache() { if (s && (this.cache = JSON.parse($prefs.valueForKey(this.name) || "{}")), (o || n) && (this.cache = JSON.parse($persistentStore.read(this.name) || "{}")), i) { let e = "root.json"; this.node.fs.existsSync(e) || this.node.fs.writeFileSync(e, JSON.stringify({}), { flag: "wx" }, e => console.log(e)), this.root = {}, e = `${this.name}.json`, this.node.fs.existsSync(e) ? this.cache = JSON.parse(this.node.fs.readFileSync(`${this.name}.json`)) : (this.node.fs.writeFileSync(e, JSON.stringify({}), { flag: "wx" }, e => console.log(e)), this.cache = {}) } } persistCache() { const e = JSON.stringify(this.cache, null, 2); s && $prefs.setValueForKey(e, this.name), (o || n) && $persistentStore.write(e, this.name), i && (this.node.fs.writeFileSync(`${this.name}.json`, e, { flag: "w" }, e => console.log(e)), this.node.fs.writeFileSync("root.json", JSON.stringify(this.root, null, 2), { flag: "w" }, e => console.log(e))) } write(e, t) { if (this.log(`SET ${t}`), -1 !== t.indexOf("#")) { if (t = t.substr(1), n || o) return $persistentStore.write(e, t); if (s) return $prefs.setValueForKey(e, t); i && (this.root[t] = e) } else this.cache[t] = e; this.persistCache() } read(e) { return this.log(`READ ${e}`), -1 === e.indexOf("#") ? this.cache[e] : (e = e.substr(1), n || o ? $persistentStore.read(e) : s ? $prefs.valueForKey(e) : i ? this.root[e] : void 0) } delete(e) { if (this.log(`DELETE ${e}`), -1 !== e.indexOf("#")) { if (e = e.substr(1), n || o) return $persistentStore.write(null, e); if (s) return $prefs.removeValueForKey(e); i && delete this.root[e] } else delete this.cache[e]; this.persistCache() } notify(e, t = "", a = "", d = {}) { const h = d["open-url"], c = d["media-url"]; if (s && $notify(e, t, a, d), n && $notification.post(e, t, a + `${c ? "\n多媒体:" + c : ""}`, { url: h }), o) { let s = {}; h && (s.openUrl = h), c && (s.mediaUrl = c), "{}" === JSON.stringify(s) ? $notification.post(e, t, a) : $notification.post(e, t, a, s) } if (i || u) { const s = a + (h ? `\n点击跳转: ${h}` : "") + (c ? `\n多媒体: ${c}` : ""); if (r) { const o = require("push"); o.schedule({ title: e, body: (t ? t + "\n" : "") + s }) } else console.log(`${e}\n${t}\n${s}\n\n`) } } log(e) { this.debug && console.log(`[${this.name}] LOG: ${this.stringify(e)}`) } info(e) { console.log(`[${this.name}] INFO: ${this.stringify(e)}`) } error(e) { console.log(`[${this.name}] ERROR: ${this.stringify(e)}`) } wait(e) { return new Promise(t => setTimeout(t, e)) } done(e = {}) { s || o || n ? $done(e) : i && !r && "undefined" != typeof $context && ($context.headers = e.headers, $context.statusCode = e.statusCode, $context.body = e.body) } stringify(e) { if ("string" == typeof e || e instanceof String) return e; try { return JSON.stringify(e, null, 2) } catch (e) { return "[object Object]" } } }(e, t) }

================================================
FILE: BDTieBa-DailyBonus/TieBa.js
================================================
/*********************************
百度贴吧签到脚本

脚本原作者: @sazs34
平台兼容: QuantumultX, Surge, Loon
更新日期: 2024/06/01

获取Cookie说明:
打开百度贴吧App后(AppStore中国区, 非内部版),点击"我的", 如通知成功获取cookie则可以使用该脚本.

*********************************
Surge(iOS 5.9.0+/macOS 5.5.0+)模块:
https://raw.githubusercontent.com/NobyDa/Script/master/Surge/Module/TieBaDailyBonus.sgmodule

*********************************
QuantumultX 任务仓库(Gallery)订阅:
https://raw.githubusercontent.com/NobyDa/Script/master/NobyDa_BoxJs.json

工具&分析->HTTP请求->右上角添加任务仓库->选择百度贴吧签到脚本添加定时任务和附加组件

*********************************
Loon 脚本订阅(非插件):
https://raw.githubusercontent.com/NobyDa/Script/master/Loon/Loon_Daily_bonus.plugin

添加后请按需启用脚本

*********************************/


var $nobyda = nobyda();
var cookieVal = $nobyda.read("CookieTB");
var useParallel = 0; //0自动切换,1串行,2并行(当贴吧数量大于30个以后,并行可能会导致QX崩溃,所以您可以自动切换)
var singleNotifyCount = 20; //想签到几个汇总到一个通知里,这里就填几个(比如我有13个要签到的,这里填了5,就会分三次消息通知过去)
var process = {
  total: 0,
  result: [
    // {
    //     bar:'',
    //     level:0,
    //     exp:0,
    //     errorCode:0,
    //     errorMsg:''
    // }
  ]
};
var url_fetch_sign = {
  url: "https://tieba.baidu.com/mo/q/newmoindex",
  headers: {
    "Content-Type": "application/octet-stream",
    Referer: "https://tieba.baidu.com/index/tbwise/forum",
    Cookie: cookieVal,
    "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/16A366"
  }
};
var url_fetch_add = {
  url: "https://tieba.baidu.com/sign/add",
  method: "POST",
  headers: {
    "Content-Type": "application/x-www-form-urlencoded",
    Cookie: cookieVal,
    "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 10_1_1 like Mac OS X; zh-CN) AppleWebKit/537.51.1 (KHTML, like Gecko) Mobile/14B100 UCBrowser/10.7.5.650 Mobile"
  },
  body: ""
};
if ($nobyda.isRequest) {
  GetCookie()
} else {
  signTieBa()
}


function signTieBa() {
  useParallel = $nobyda.read("BDTB_DailyBonus_Mode") || useParallel
  singleNotifyCount = $nobyda.read("BDTB_DailyBonus_notify") || singleNotifyCount
  if (!cookieVal) {
    $nobyda.notify("贴吧签到", "签到失败", "未获取到cookie");
    return $nobyda.done()
  }
  $nobyda.get(url_fetch_sign, function(error, response, data) {
    if (error) {
      $nobyda.notify("贴吧签到", "签到失败", "未获取到签到列表");
      $nobyda.done()
    } else {
      // $nobyda.notify("贴吧签到", "贴吧列表", response.body);
      var body = JSON.parse(data);
      var isSuccessResponse = body && body.no == 0 && body.error == "success" && body.data.tbs;
      if (!isSuccessResponse) {
        $nobyda.notify("贴吧签到", "签到失败", (body && body.error) ? body.error : "接口数据获取失败");
        return $nobyda.done()
      }
      process.total = body.data.like_forum.length;
      if (body.data.like_forum && body.data.like_forum.length > 0) {
        if (useParallel == 1 || (useParallel == 0 && body.data.like_forum.length >= 30)) {
          signBars(body.data.like_forum, body.data.tbs, 0);
        } else {
          for (const bar of body.data.like_forum) {
            signBar(bar, body.data.tbs);
          }
        }
      } else {
        $nobyda.notify("贴吧签到", "签到失败", "请确认您有关注的贴吧");
        return $nobyda.done()
      }
    }
  })
}

function signBar(bar, tbs) {
  if (bar.is_sign == 1) { //已签到的,直接不请求接口了
    process.result.push({
      bar: `${bar.forum_name}`,
      level: bar.user_level,
      exp: bar.user_exp,
      errorCode: 9999,
      errorMsg: "已签到"
    });
    checkIsAllProcessed();
  } else {
    url_fetch_add.body = `tbs=${tbs}&kw=${bar.forum_name}&ie=utf-8`;
    $nobyda.post(url_fetch_add, function(error, response, data) {
      if (error) {
        process.result.push({
          bar: bar.forum_name,
          errorCode: 999,
          errorMsg: '接口错误'
        });
        checkIsAllProcessed();
      } else {
        try {
          var addResult = JSON.parse(data);
          if (addResult.no == 0) {
            process.result.push({
              bar: bar.forum_name,
              errorCode: 0,
              errorMsg: `获得${addResult.data.uinfo.cont_sign_num}积分,第${addResult.data.uinfo.user_sign_rank}个签到`
            });
          } else {
            process.result.push({
              bar: bar.forum_name,
              errorCode: addResult.no,
              errorMsg: addResult.error
            });
          }
        } catch (e) {
          $nobyda.notify("贴吧签到", "贴吧签到数据处理异常", JSON.stringify(e));
          $nobyda.done()
        }
        checkIsAllProcessed();
      }
    })
  }
}

function signBars(bars, tbs, index) {
  //$nobyda.notify("贴吧签到", `进度${index}/${bars.length}`, "");
  if (index >= bars.length) {
    //$nobyda.notify("贴吧签到", "签到已满", `${process.result.length}`);
    checkIsAllProcessed();
  } else {
    var bar = bars[index];
    if (bar.is_sign == 1) { //已签到的,直接不请求接口了
      process.result.push({
        bar: `${bar.forum_name}`,
        level: bar.user_level,
        exp: bar.user_exp,
        errorCode: 9999,
        errorMsg: "已签到"
      });
      signBars(bars, tbs, ++index);
    } else {
      url_fetch_add.body = `tbs=${tbs}&kw=${bar.forum_name}&ie=utf-8`;
      $nobyda.post(url_fetch_add, function(error, response, data) {
        if (error) {
          process.result.push({
            bar: bar.forum_name,
            errorCode: 999,
            errorMsg: '接口错误'
          });
          signBars(bars, tbs, ++index);
        } else {
          try {
            var addResult = JSON.parse(data);
            if (addResult.no == 0) {
              process.result.push({
                bar: bar.forum_name,
                errorCode: 0,
                errorMsg: `获得${addResult.data.uinfo.cont_sign_num}积分,第${addResult.data.uinfo.user_sign_rank}个签到`
              });
            } else {
              process.result.push({
                bar: bar.forum_name,
                errorCode: addResult.no,
                errorMsg: addResult.error
              });
            }
          } catch (e) {
            $nobyda.notify("贴吧签到", "贴吧签到数据处理异常", JSON.stringify(e));
            $nobyda.done()
          }
          signBars(bars, tbs, ++index)
        }
      })
    }
  }
}

function checkIsAllProcessed() {
  //$nobyda.notify("贴吧签到", `最终进度${process.result.length}/${process.total}`, "");
  if (process.result.length != process.total) return;
  for (var i = 0; i < Math.ceil(process.total / singleNotifyCount); i++) {
    var notify = "";
    var spliceArr = process.result.splice(0, singleNotifyCount);
    var notifySuccessCount = 0;
    for (const res of spliceArr) {
      if (res.errorCode == 0 || res.errorCode == 9999) {
        notifySuccessCount++;
      }
      if (res.errorCode == 9999) {
        notify += `【${res.bar}】已经签到,当前等级${res.level},经验${res.exp}
`;
      } else {
        notify += `【${res.bar}】${res.errorCode==0?'签到成功':'签到失败'},${res.errorCode==0?res.errorMsg:('原因:'+res.errorMsg)}
`;
      }
    }
    $nobyda.notify("贴吧签到", `签到${spliceArr.length}个,成功${notifySuccessCount}个`, notify);
    $nobyda.done()
  }
}

function GetCookie() {
  let headerCookie = $request.headers["Cookie"] || $request.headers["cookie"];
  if (headerCookie && headerCookie.includes('BDUSS=')) {
      if (!cookieVal) {
        $nobyda.notify("写入百度贴吧Cookie成功 🎉", "", "");
      } else {
          console.log(`写入百度贴吧Cookie成功 🎉`);
      }
      $nobyda.write(headerCookie, "CookieTB")
  } else {
    console.log(`写入Cookie失败, BDUSS值缺失. `);
  }
  return $nobyda.done();
}

function nobyda() {
  const isRequest = typeof $request != "undefined"
  const isSurge = typeof $httpClient != "undefined"
  const isQuanX = typeof $task != "undefined"
  const notify = (title, subtitle, message) => {
    if (isQuanX) $notify(title, subtitle, message)
    if (isSurge) $notification.post(title, subtitle, message)
  }
  const write = (value, key) => {
    if (isQuanX) return $prefs.setValueForKey(value, key)
    if (isSurge) return $persistentStore.write(value, key)
  }
  const read = (key) => {
    if (isQuanX) return $prefs.valueForKey(key)
    if (isSurge) return $persistentStore.read(key)
  }
  const adapterStatus = (response) => {
    if (response) {
      if (response.status) {
        response["statusCode"] = response.status
      } else if (response.statusCode) {
        response["status"] = response.statusCode
      }
    }
    return response
  }
  const get = (options, callback) => {
    if (isQuanX) {
      if (typeof options == "string") options = {
        url: options
      }
      options["method"] = "GET"
      $task.fetch(options).then(response => {
        callback(null, adapterStatus(response), response.body)
      }, reason => callback(reason.error, null, null))
    }
    if (isSurge) $httpClient.get(options, (error, response, body) => {
      callback(error, adapterStatus(response), body)
    })
  }
  const post = (options, callback) => {
    if (isQuanX) {
      if (typeof options == "string") options = {
        url: options
      }
      options["method"] = "POST"
      $task.fetch(options).then(response => {
        callback(null, adapterStatus(response), response.body)
      }, reason => callback(reason.error, null, null))
    }
    if (isSurge) {
      $httpClient.post(options, (error, response, body) => {
        callback(error, adapterStatus(response), body)
      })
    }
  }
  const done = (value = {}) => {
    if (isQuanX) return $done(value)
    if (isSurge) isRequest ? $done(value) : $done()
  }
  return {
    isRequest,
    notify,
    write,
    read,
    get,
    post,
    done
  }
};

================================================
FILE: Bahamut/BahamutAnimeAds.js
================================================
/************************

动画疯,屏蔽播放广告脚本 (黑屏25秒自动播放)
由于动画疯强制验证观看广告时间,无法实现真正意义上的跳过广告。

Surge(4.11+)模块:
https://raw.githubusercontent.com/NobyDa/Script/master/Surge/Module/BahamutAnimeAds.sgmodule

QX(1.0.27+)用户请自行搭配KOP-XIAO资源解析器重写引用Surge模块。

************************/

let [req, rsp] = [$request, JSON.parse($response.body || '{}')];

runs().catch((err) => {
  console.log(`[BahamutAnime] ERROR: ${err.message||err}`)
}).finally(() => $done({
  body: JSON.stringify(rsp)
}));

async function runs() {
  if (req.url.includes('token.php')) {
    if (rsp.ad) {
      rsp.ad.minor = [];
      rsp.ad.major = [];
    }
    if (rsp.data && rsp.data.ad) {
      rsp.data.ad.minor = [];
      rsp.data.ad.major = [];
    }  
  }
  if (req.url.includes('m3u8.php') && (rsp.message || rsp.error)) {
    await adURL('');
    await new Promise(r => setTimeout(r, 25000));
    await adURL('end');
    rsp = await playURL();
  }
}

function adURL(str) {
  return new Promise((res) => {
    get({
      url: `https://api.gamer.com.tw/mobile_app/anime/v1/stat_ad.php?ad=${str}&schedule=0&sn=${req.url.split(/sn=(\d+)/i)[1]}`,
      headers: req.headers
    }, (err, resp, data) => res())
  })
}

function playURL() {
  return new Promise((res) => {
    get({
      url: req.url,
      headers: req.headers
    }, (err, resp, data) => res(JSON.parse(data || '{}')))
  })
}

function get(options, callback) {
  if (typeof $task != "undefined") {
    $task.fetch(options).then(response => {
      response["status"] = response.statusCode
      callback(null, response, response.body)
    }, reason => callback(reason.error, null, null))
  }
  if (typeof $httpClient != "undefined") {
    $httpClient.get(options, callback)
  }
}

================================================
FILE: Bahamut/BahamutDailyBonus.js
================================================
/************************

巴哈姆特签到脚本
包含主站签到、公会签到、动画疯答题等

脚本兼容: Surge、QuantumultX、Loon、Shadowrocket、Node.js
适配过程: https://nobyda.github.io/2021/07/24/Bahamut_daily_bonus_js_example
BoxJs订阅: https://raw.githubusercontent.com/NobyDa/Script/master/NobyDa_BoxJs.json

*************************
【 签到脚本注意事项 】:
*************************

1. 该脚本需要进入BoxJs或脚本内填写账号密码后, 方可使用.
2. 不建议在凌晨执行,因需要获取动画疯题目答案; 默认配置将在每天的早上8:00执行.
3. 如需使用Node.js运行该脚本, 则需安装got、tough-cookie模块

*************************
【 Surge & Loon 脚本配置 】:
*************************

[Script]
cron "0 8 * * *" script-path=https://raw.githubusercontent.com/NobyDa/Script/master/Bahamut/BahamutDailyBonus.js, wake-system=1, timeout=300

*************************
【 QX 1.0.10+ 脚本配置 】 :
*************************

[task_local]
0 8 * * * https://raw.githubusercontent.com/NobyDa/Script/master/Bahamut/BahamutDailyBonus.js, tag=巴哈姆特签到, img-url=https://raw.githubusercontent.com/NobyDa/mini/master/Color/bahamutGame.png

************************/

// 以下全局变量中的持久化接口为BoxJs预留, 以便修改
// 把兼容函数定义到$中, 以便统一调用
const $ = new Env('巴哈姆特');

// 用户名
$.uid = $.getdata('@ND_BAHA.ID') || 'YourUserName';

// 用户密码
$.pwd = $.getdata('@ND_BAHA.PW') || 'YourUserPassword';

// 两步验证Token, 16位数, 未设置请保持默认
$.totp = $.getdata('@ND_BAHA.TOTP') || '';

// 是否开启广告签到,true/false,默认关闭 (该功能耗时过长)
$.needSignAds = $.getdata('@ND_BAHA.ADS') || false;

// 是否自动签到公会,true/false,默认开启
$.needSignGuild = $.getdata('@ND_BAHA.GUILD') || true;

// 是否自动答题动画疯,true/false,默认开启 (不保证100%答题正确)
$.needAnswer = $.getdata('@ND_BAHA.ANSWER') || true;

//Bark APP 通知推送Key
$.barkKey = '';

// 为通知准备的空数组
$.notifyMsg = [];

(async function() { // 立即运行的匿名异步函数
	await BahamutLogin(); // 登录
	await BahamutGuildSign(); //签到巴哈公会
	await BahamutSign(); //签到巴哈
	await BahamutAnswer(); //动画疯答题
})().catch((e) => $.notifyMsg.push(e.message || e)) //捕获登录函数等抛出的异常, 并把原因添加到全局变量(通知)
	.finally(async () => { //finally在catch之后无论有无异常都会执行
		if ($.barkKey) { //如果已填写Bark Key
			await BarkNotify($, $.barkKey, $.name, $.notifyMsg.join('\n')); //推送Bark通知
		};
		$.msg($.name, ``, $.notifyMsg.join('\n'), {
			'open-url': 'crazyanime://', //动画疯url scheme
			'media-url': 'https://cdn.jsdelivr.net/gh/NobyDa/mini@master/Color/bahamutClear.png' //通知图片
		}); //带上总结推送通知
		$.done(); //调用Surge、QX内部特有的函数, 用于退出脚本执行
	});

async function BahamutLogin(retry = 3, interval = 1000) { //登录函数,拿到Set-Cookie

	//登录成功: {"success":true,"userid":"DGIE","nickname":"coco","gold":152769,"gp":0,"avatar":"https:\/\/avatar2.bahamut.com.tw\/avataruserpic\/dgie.png","avatar_s":"https:\/\/avatar2.bahamut.com.tw\/avataruserpic\/dgie_s.png","lv":6}
	//账号错误: {"code":0,"message":"查無此人:SDFOUGB"}
	//密码错误: {"code":0,"message":"帳號、密碼或驗證碼錯誤!"}
	//验证码错误: {"code":0,"message":"驗證碼錯誤"}

	for (let i = 0; i < retry; i++) { //循环登录(默认三次)
		if (i > 0) {
			$.log('', `🔶尝试第${i+1}次登录...`);
			await $.wait(interval); //延迟一秒
		};
		const reqUrl = {
			url: 'https://api.gamer.com.tw/mobile_app/user/v3/do_login.php', //登录接口
			headers: { //请求头
				'Cookie': 'ckAPP_VCODE=6666' //Cookie中的ckAPP_VCODE为必须
			},
			//请求体放入用户名和密码,并把它uri编码
			body: `uid=${encodeURIComponent($.uid)}&passwd=${encodeURIComponent($.pwd)}&vcode=6666${$.totp?`&twoStepAuth=${TOTP($.totp)}`:``}`
		};
		const res = await $.http.post(reqUrl) //使用post请求查询 (兼容函数实际上返回Promise实例对象,以便后续调用时可以实现顺序执行异步函数)
			.then(async (resp) => { //请求成功的处理
				const body = JSON.parse(resp.body); //解析响应体json为对象
				if (body.userid) { //如果成功返回用户信息
					$.BAHARUNE = JSON.stringify(resp.headers).split(/(BAHARUNE=\w+)/)[1];
					return `✅巴哈姆特登录成功`;
				} else { //否则登录失败 (例如密码错误)
					const failMsg = body.error ? body.error.message : null; //判断签到失败原因
					throw new Error(`${body.message||failMsg||'原因未知'}`); //带上原因抛出异常
				}
			}).catch((err) => `❌登录失败\n❌${err.message || err}`);
		$.log('', res.message || res);
		if (res === `✅巴哈姆特登录成功`) {
			break; //登录成功则跳出循环
		} else if (retry == i + 1) { //如果最后一次重试仍登录失败
			throw new Error(res.message || res); //抛出错误, 被调用该函数时的catch捕获, 脚本结束.
		}
	}
}

function BahamutSign() { //查询巴哈姆特签到Token
	return $.http.get({ //使用get方法 (Promise实例对象) 查询签到Token
			url: 'https://www.gamer.com.tw/ajax/get_csrf_token.php', // 查询Token接口
			headers: {} //请求头, 客户端将自动设置Cookie字段
		}).then(async (resp) => { //网络请求成功的处理, 实例函数带有async关键字, 表示里面有异步操作
			if (resp.body) { //如果签到Token获取成功
				$.log('', '✅获取签到令牌成功'); //打印日志
				const sign = await StartSignBahamut(resp.body); //带上Token开始签到
				$.notifyMsg.push(`主页签到: 成功, 已连续签到${sign}天`); //添加到全局变量备用 (通知)
				await StartAdsBonus(resp.body.slice(0, 16), 'start'); //执行广告签到
			} else { //否则抛出异常
				throw new Error('获取签到令牌失败'); //带上原因被下面catch捕获
			}
		})
		.catch(err => {
			$.notifyMsg.push(`主页签到: ${err.message||err}`); //添加到全局变量备用 (通知)
			$.log('', `❌巴哈姆特签到失败`, `❌${err.message||err}`);
		}); // 捕获异常, 打印日志
}

function StartSignBahamut(token) { //巴哈姆特签到

	//签到成功: {"data":{"days":1,"dialog":"","prjSigninDays":0}}
	//已签过: {"error":{"code":0,"message":"今天您已經簽到過了喔","status":"","details":[]}}
	//未登录: {"error":{"code":401,"message":"尚未登入","status":"NO_LOGIN","details":[]}}
	//令牌过期: {"error":{"code":403,"message":"網頁已過期","status":"CSRF_TOKEN_ERROR","details":[]}}

	return $.http.post({ //使用post方法 (Promise实例对象) 进行签到
			url: 'https://www.gamer.com.tw/ajax/signin.php', //巴哈姆特签到接口
			headers: {}, //请求头, 客户端将自动设置Cookie字段
			body: `action=1&token=${token}` //请求体带上查询到的签到Token
		})
		.then(res => { // 网络请求成功的处理
			const body = JSON.parse(res.body); //解析响应体json为对象
			if (body.data) { // 如果签到成功 (判断预期响应格式)
				$.log('', '✅巴哈姆特签到成功', `✅已连续签到${body.data.days}天`); //打印日志
				return body.data.days; //返回签到天数
			} else { //否则签到失败
				const failMsg = body.error ? body.error.message : null; //判断签到失败原因
				throw new Error(failMsg || body.message || '未知'); //带上原因抛出异常
			}
		}); //未写catch,如果签到失败或其他错误,则被调用该函数时的catch捕获
}

function StartAdsBonus(token, type) {
	if ($.needSignAds === false || $.needSignAds === 'false') { //如果用户选择不签到广告
		return; //退出广告签到函数
	}
	return $.http.post({ //使用post方法 (Promise实例对象) 进行签到
			url: 'https://api.gamer.com.tw/mobile_app/bahamut/v1/sign_in_ad_' + type + '.php', //双倍巴币广告奖励接口
			headers: {
				'X-Bahamut-Csrf-Token': token, //前16位签到Token
				'Cookie': `ckBahamutCsrfToken=${token};${$.BAHARUNE}` //前16位签到Token和重新设置的Cookie
			}
		})
		.then(async (res) => { //网络请求成功的处理, 实例函数带有async关键字, 表示里面有异步操作
			const body = JSON.parse(res.body); //解析响应体json为对象
			if (body.data && body.data.finished == 0 && type == 'start') { //如果成功激活广告奖励
				$.log('', '🔶正在执行广告签到 (30s)'); //打印日志
				await $.wait(30000); //等待30秒
				await StartAdsBonus(token, 'finished'); //领取奖励函数
			} else if (body.data && body.data.finished == 1) { //如果广告奖励领取成功
				$.log('', '✅领取广告奖励成功'); //打印日志
				$.notifyMsg.push('广告签到: 成功, 已领取双倍签到奖励'); //添加到全局变量备用 (通知)
			} else {
				const failMsg = body.error ? body.error.message : null; //判断签到失败原因
				throw new Error(failMsg || body.message || '未知'); //带上原因抛出异常
			}
		})
		.catch(err => {
			$.notifyMsg.push(`广告签到: ${err.message||err}`); //添加到全局变量备用 (通知)
			$.log('', `❌广告奖励签到失败`, `❌${err.message||err}`);
		}); // 捕获异常, 打印日志
}

function BahamutGuildSign() { //巴哈姆特查询公会列表
	if ($.needSignGuild === false || $.needSignGuild === 'false') { //如果用户选择不签到公会
		return; //退出公会签到函数
	}
	return $.http.get({ //使用get请求查询公会列表 (Promise实例对象)
			url: 'https://api.gamer.com.tw/ajax/common/topBar.php?type=forum', // 查询公会列表接口
			headers: {} //请求头, 客户端将自动设置Cookie字段
		})
		.then(async (resp) => { //网络请求成功的处理, 实例函数带有async关键字, 表示里面有异步操作
			const list = (resp.body.replace(/\n/g, '').match(/guild\.php\?g?sn=\d.+?<\/p>/g) || []) //正则过滤公会列表大致内容
				.map(n => { //使用map遍历每个大致内容
					return { //返回包含公会ID和公会名称的对象
						sn: n.split(/guild\.php\?g?sn=(\d+)/)[1], //正则进一步提取公会ID
						name: n.split(/<p>(.+?)<\/p>/)[1] //正则进一步提取公会名称
					}
				});
			if (list.length) { //过滤后, 如果包含公会列表
				$.log('', `✅获取公会列表成功`); //打印日志
				//按照公会数量进行并发签到, map结合Promise.all后可以实现并发签到并且都完成后才进行下一行操作
				const sign = await Promise.all(list.map(StartSignGuild));
				const sucs = sign.filter(n => n === 1).length; //过滤后得到成功数量
				const fail = sign.filter(n => n === 0).length; //过滤后得到失败数量
				//添加到全局变量备用 (通知)
				$.notifyMsg.push(`公会签到: ${sucs?`成功${sucs}个`:``}${sucs&&fail?`, `:``}${fail?`失败${fail}个`:``}`);
			} else {
				throw new Error('公会列表为空'); //无公会列表则抛出异常
			}
		})
		.catch(err => { //捕获异常, 打印日志
			$.notifyMsg.push(`公会签到: ${err.message || err}`); //添加到全局变量备用 (通知)
			$.log('', `❌巴哈姆特公会签到失败`, `❌${err.message || err}`); //打印日志
		});
}

function StartSignGuild(v) { //巴哈姆特公会签到

	//签到成功: {"ok":1,"msg":"本日簽到成功!獲得5貢獻度"}
	//已签过: {"error":1,"msg":"您今天已經簽到過了!"}
	//公会ID错误: {"error":1,"msg":"此公會社團不存在。"}
	//未加入公会: {"error":1,"msg":"你還不是成員,歡迎加入!"}
	//未登录: {"error":1,"msg":"請先登入"}

	return $.http.post({ //使用post方法签到公会 (Promise实例对象)
			url: 'https://guild.gamer.com.tw/ajax/guildSign.php', //公会签到接口
			headers: {}, //请求头, 客户端将自动设置Cookie字段
			body: `sn=${v.sn}` //把查询到的公会ID放进请求体
		})
		.then((res) => { //网络请求成功后的处理
			const body = JSON.parse(res.body); //解析响应体json为对象
			$.log('', `🔷<${v.name}>`, `${body.ok?`✅`:`❌`}${body.msg}`); //打印日志, 包含签到结果
			if (body.ok) { //如果签到成功
				return 1; //返回1表示成功
			} else {
				return 0; //返回0表示失败
			}
		})
		.catch(e => { //捕获异常, 打印日志
			$.log('', `🔷<${v.name}>`, `❌签到失败: ${e.message||e}`);
			return 0; //返回0表示失败
		});
}

function BahamutAnswer() { //动画疯答题

	//未答题: {"game":"灌籃高手","question":"流川楓的號碼是下列何者?","a1":"7","a2":"11","a3":"23","a4":"59","userid":"GN32964174","token":"01092fe463ab36ab47cb298e229c4f8fb298e229cc260fa7baf"}
	//已答题: {"error":1,"msg":"今日已經答過題目了,一天僅限一次機會"}
	//未登录: {"error":1,"nologin":1,"msg":"請先登入"}

	if ($.needAnswer === false || $.needAnswer === 'false') { //如果用户关闭动画疯答题
		return; //退出答题函数
	}
	return $.http.get({ //使用get方获取题目 (Promise实例对象)
			url: 'https://ani.gamer.com.tw/ajax/animeGetQuestion.php?t=' + Date.now(), //获取题目接口
			headers: {} //请求头, 客户端将自动设置Cookie字段
		})
		.then(async (res) => { //网络请求成功的处理, 实例函数带有async关键字, 表示里面有异步操作
			const r = JSON.parse(res.body); //解析响应体json为对象
			if (r.token) { //如果有题目
				$.log('', `✅获取动画疯题目成功`, ``, `🔶<${r.game}> ${r.question}`,
					`1️⃣${r.a1}`, `2️⃣${r.a2}`, `3️⃣${r.a3}`, `4️⃣${r.a4}`); //打印日志
				const article = await GetAanswerArticles(); //获取答案文章ID
				const getAnswer = await StartSearchAnswers(article); //传入文章ID, 再从文章内获取答案
				const sendAnswer = await StartBahamutAnswer(getAnswer, r.token); //传入答案和题目令牌, 开始答题
				$.notifyMsg.push(`动画答题: ${sendAnswer}`); //答题后的结果添加到全局变量备用 (通知)
			} else { //未获取到题目
				throw new Error(r.msg || `获取题目失败`); //带上原因抛出异常
			}
		})
		.catch(e => { //捕获异常, 打印日志
			$.notifyMsg.push(`动画答题: ${e.message||e||`失败`}`); //添加到全局变量备用 (通知)
			$.log('', `❌动画疯答题失败`, `❌${e.message||e}`); //打印日志
		});
}

function GetAanswerArticles() { // 从blackxblue的小屋查询含答案的文章ID
	$.log('', `🔶开始获取文章`); //打印日志
	return $.http.get({ //使用get方法获取文章ID (Promise实例对象)
			url: 'https://api.gamer.com.tw/mobile_app/bahamut/v1/home.php?owner=blackXblue&page=1', //获取文章ID接口
			headers: {}
		})
		.then((res) => { //网络请求成功后的处理
			const body = JSON.parse(res.body); //解析响应体json为对象
			const tDate = $.time('MM/dd'); //返回今日日期
			const title = (body.creation || []).filter(t => t.title.includes(tDate)); //过滤后返回今日答案文章
			if (title.length && title[0].sn) { //如果有答案文章
				$.log('', `✅获取文章成功 (${title[0].sn})`); //打印日志
				return title[0].sn; //返回文章ID
			} else { //否则带上原因抛出异常, 被调用该函数时的catch捕获
				throw new Error('今日答案未发表');
			}
		})
}

function StartSearchAnswers(id) { //获取文章内答案
	$.log('', `🔶开始获取答案`); //打印日志
	return $.http.get({ //使用get方法获取答案 (Promise实例对象)
			url: 'https://api.gamer.com.tw/mobile_app/bahamut/v1/home_creation_detail.php?sn=' + id, //获取答案接口
			headers: {}
		})
		.then((res) => { //网络请求成功后的处理
			const body = JSON.parse(res.body); //解析响应体json为对象
			const answers = body.content.split(/A:(\d)/)[1]; //正则提取答案
			if (answers) { //如果成功提取答案
				$.log('', `✅获取答案成功 (${answers})`); //打印日志
				return answers; //返回答案
			} else { //否则带上原因抛出异常, 被调用该函数时的catch捕获
				throw new Error('提取答案失败');
			}
		})
}

function StartBahamutAnswer(answer, token) { //动画疯答题

	//答题正确: {"ok":1,"gift":"恭喜您得到:300 巴幣"}
	//答题错误: {"error":1,"msg":"答題錯誤"}
	//令牌过期: {"error":1,"msg":"很抱歉!本題目已超過時效!"}
	//已答题: {"error":1,"msg":"今日已經答過題目了,一天僅限一次機會"}
	//未登录: {"error":1,"nologin":1,"msg":"請先登入"}

	$.log('', `🔶开始答题`); //打印日志
	return $.http.post({ //使用post方法提交答案 (Promise实例对象)
			url: 'https://ani.gamer.com.tw/ajax/animeAnsQuestion.php', //提交答案接口
			headers: {}, //请求头, 客户端将自动设置Cookie字段
			body: `token=${token}&ans=${answer}&t=${Date.now()}`, //请求体带上答案和答案令牌
		})
		.then((res) => { //网络请求成功后的处理
			const body = JSON.parse(res.body); //解析响应体json为对象
			if (body.ok) { //如果答题成功
				$.log('', `✅${body.gift}`); //打印奖励日志
				return body.gift; //返回奖励内容
			} else { //否则答题失败
				const failMsg = body.error ? body.error.message : null; //提取签到失败原因
				throw new Error(body.msg || failMsg || '未知'); //否则带上原因抛出异常, 被调用该函数时的catch捕获
			}
		})
}

//Bark APP notify
async function BarkNotify(c,k,t,b){for(let i=0;i<3;i++){console.log(`🔷Bark notify >> Start push (${i+1})`);const s=await new Promise((n)=>{c.post({url:'https://api.day.app/push',headers:{'Content-Type':'application/json'},body:JSON.stringify({title:t,body:b,device_key:k,ext_params:{group:t}})},(e,r,d)=>r&&r.status==200?n(1):n(d||e))});if(s===1){console.log('✅Push success!');break}else{console.log(`❌Push failed! >> ${s.message||s}`)}}};

//修改自 https://github.com/chavyleung/scripts/blob/master/Env.js 的兼容函数
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,i)=>{s.call(this,t,(t,s,r)=>{t?i(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.ua="Anime/2.13.9 (tw.com.gamer.anime;build:437;iOS 14.5.0) Alamofire/5.4.1",this.logs=[],this.isMute=!1,this.isNeedRewrite=!0,this.logSeparator="\n",this.startTime=(new Date).getTime(),Object.assign(this,e),this.log("",`\ud83d\udd14${this.name}`)}isNode(){return"undefined"!=typeof module&&!!module.exports}isQuanX(){return"undefined"!=typeof $task}isSurge(){return"undefined"!=typeof $httpClient&&"undefined"==typeof $loon}isLoon(){return"undefined"!=typeof $loon}isShadowrocket(){return"undefined"!=typeof $rocket}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 i=this.getdata(t);if(i)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,i)=>e(i))})}runScript(t,e){return new Promise(s=>{let i=this.getdata("@chavy_boxjs_userCfgs.httpapi");i=i?i.replace(/\n/g,"").trim():i;let r=this.getdata("@chavy_boxjs_userCfgs.httpapi_timeout");r=r?1*r:20,r=e&&e.timeout?e.timeout:r;const[o,h]=i.split("@"),a={url:`http:\/\/${h}/v1/scripting/evaluate`,body:{script_text:t,mock_type:"cron",timeout:r},headers:{"X-Key":o,Accept:"*/*"}};this.post(a,(t,e,i)=>s(i))}).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),i=!s&&this.fs.existsSync(e);if(!s&&!i)return{};{const i=s?t:e;try{return JSON.parse(this.fs.readFileSync(i))}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),i=!s&&this.fs.existsSync(e),r=JSON.stringify(this.data);s?this.fs.writeFileSync(t,r):i?this.fs.writeFileSync(e,r):this.fs.writeFileSync(t,r)}}lodash_get(t,e,s){const i=e.replace(/\[(\d+)\]/g,".$1").split(".");let r=t;for(const t of i)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,i)=>Object(t[s])===t[s]?t[s]:t[s]=Math.abs(e[i+1])>>0==+e[i+1]?[]:{},t)[e[e.length-1]]=s,t)}getdata(t){let e=this.getval(t);if(/^@/.test(t)){const[,s,i]=/^@(.*?)\.(.*?)$/.exec(t),r=s?this.getval(s):"";if(r)try{const t=JSON.parse(r);e=t?this.lodash_get(t,i,""):e}catch(t){e=""}}return e}setdata(t,e){let s=!1;if(/^@/.test(e)){const[,i,r]=/^@(.*?)\.(.*?)$/.exec(e),o=this.getval(i),h=i?"null"===o?null:o||"{}":"{}";try{const e=JSON.parse(h);this.lodash_set(e,r,t),s=this.setval(JSON.stringify(e),i)}catch(e){const o={};this.lodash_set(o,r,t),s=this.setval(JSON.stringify(o),i)}}else s=this.setval(t,e);return s}getval(t){return this.isSurge()||this.isLoon()?$persistentStore.read(t):this.isQuanX()?$prefs.valueForKey(t):this.isNode()?(this.data=this.loaddata(),this.data[t]):this.data&&this.data[t]||null}setval(t,e){return this.isSurge()||this.isLoon()?$persistentStore.write(t,e):this.isQuanX()?$prefs.setValueForKey(t,e):this.isNode()?(this.data=this.loaddata(),this.data[e]=t,this.writedata(),!0):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||/ckAPP_VCODE/.test(t.headers.Cookie))&&void 0===t.cookieJar)&&(t.cookieJar=this.ckjar))}get(t,e=(()=>{})){t.headers&&(t.headers["User-Agent"]=this.ua,delete t.headers["Content-Type"],delete t.headers["Content-Length"]),this.isSurge()||this.isLoon()?(this.isSurge()&&this.isNeedRewrite&&(t.headers=t.headers||{},Object.assign(t.headers,{"X-Surge-Skip-Scripting":!1})),$httpClient.get(t,(t,s,i)=>{!t&&s&&(s.body=i,s.statusCode=s.status),e(t,s,i)})):this.isQuanX()?(this.isNeedRewrite&&(t.opts=t.opts||{},Object.assign(t.opts,{hints:!1})),$task.fetch(t).then(t=>{const{statusCode:s,statusCode:i,headers:r,body:o}=t;e(null,{status:s,statusCode:i,headers:r,body:o},o)},t=>e(t))):this.isNode()&&(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:s,statusCode:i,headers:r,body:o}=t;e(null,{status:s,statusCode:i,headers:r,body:o},o)},t=>{const{message:s,response:i}=t;e(s,i,i&&i.body)}))}post(t,e=(()=>{})){const s=t.method?t.method.toLocaleLowerCase():"post";if(t.body&&t.headers&&!t.headers["Content-Type"]&&(t.headers["Content-Type"]="application/x-www-form-urlencoded"),t.headers&&(t.headers["User-Agent"]=this.ua,delete t.headers["Content-Length"]),this.isSurge()||this.isLoon())this.isSurge()&&this.isNeedRewrite&&(t.headers=t.headers||{},Object.assign(t.headers,{"X-Surge-Skip-Scripting":!1})),$httpClient[s](t,(t,s,i)=>{!t&&s&&(s.body=i,s.statusCode=s.status),e(t,s,i)});else if(this.isQuanX())t.method=s,this.isNeedRewrite&&(t.opts=t.opts||{},Object.assign(t.opts,{hints:!1})),$task.fetch(t).then(t=>{const{statusCode:s,statusCode:i,headers:r,body:o}=t;e(null,{status:s,statusCode:i,headers:r,body:o},o)},t=>e(t));else if(this.isNode()){this.initGotEnv(t);const{url:i,...r}=t;this.got[s](i,r).then(t=>{const{statusCode:s,statusCode:i,headers:r,body:o}=t;e(null,{status:s,statusCode:i,headers:r,body:o},o)},t=>{const{message:s,response:i}=t;e(s,i,i&&i.body)})}}time(t,e=null){const s=e?new Date(e):new Date;let i={"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 i)new RegExp("("+e+")").test(t)&&(t=t.replace(RegExp.$1,1==RegExp.$1.length?i[e]:("00"+i[e]).substr((""+i[e]).length)));return t}msg(e=t,s="",i="",r){const o=t=>{if(!t)return t;if("string"==typeof t)return this.isLoon()?t:this.isQuanX()?{"open-url":t}:this.isSurge()?{url:t}:void 0;if("object"==typeof t){if(this.isLoon()){let e=t.openUrl||t.url||t["open-url"],s=t.mediaUrl||t["media-url"];return{openUrl:e,mediaUrl:s}}if(this.isQuanX()){let e=t["open-url"]||t.url||t.openUrl,s=t["media-url"]||t.mediaUrl;return{"open-url":e,"media-url":s}}if(this.isSurge()){let e=t.url||t.openUrl||t["open-url"];return{url:e}}}};if(this.isMute||(this.isSurge()||this.isLoon()?$notification.post(e,s,i,o(r)):this.isQuanX()&&$notify(e,s,i,o(r))),!this.isMuteLog){let t=["","================================="];t.push(e),s&&t.push(s),i&&t.push(i),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){const s=!this.isSurge()&&!this.isQuanX()&&!this.isLoon();s?this.log("",`\u2757\ufe0f${this.name},\u9519\u8bef!`,t.stack):this.log("",`\u2757\ufe0f${this.name},\u9519\u8bef!`,t)}wait(t){return new Promise(e=>setTimeout(e,t))}done(t={}){const e=(new Date).getTime(),s=(e-this.startTime)/1e3;this.log("",`${s}\u79d2`,`=================================`),(this.isSurge()||this.isQuanX()||this.isLoon())&&$done(t)}}(t,e)};

// 从 https://jsfiddle.net/russau/rbyjk774 魔改的TOTP两部验证算法, 完全使用原生javascript实现
function TOTP(token){function t(e,a,d){var g=0,c=[],b=0,f,k,l,h,m,w,n,y,p=!1,q=[],t=[],v,u=!1;d=d||{};f=d.encoding||"UTF8";v=d.numRounds||1;l=z(a,f);if(v!==parseInt(v,10)||1>v)throw Error("numRounds must a integer >= 1");if("SHA-1"===e)m=512,w=A,n=H,h=160,y=function(a){return a.slice()};else throw Error("Chosen SHA variant is not supported");k=x(e);this.setHMACKey=function(a,b,c){var d;if(!0===p)throw Error("HMAC key already set");if(!0===u)throw Error("Cannot set HMAC key after calling update");f=(c||{}).encoding||"UTF8";b=z(b,f)(a);a=b.binLen;b=b.value;d=m>>>3;c=d/4-1;if(d<a/8){for(b=n(b,a,0,x(e),h);b.length<=c;)b.push(0);b[c]&=4294967040}else if(d>a/8){for(;b.length<=c;)b.push(0);b[c]&=4294967040}for(a=0;a<=c;a+=1)q[a]=b[a]^909522486,t[a]=b[a]^1549556828;k=w(q,k);g=m;p=!0};this.update=function(a){var d,e,f,h=0,n=m>>>5;d=l(a,c,b);a=d.binLen;e=d.value;d=a>>>5;for(f=0;f<d;f+=n)h+m<=a&&(k=w(e.slice(f,f+n),k),h+=m);g+=h;c=e.slice(h>>>5);b=a%m;u=!0};this.getHash=function(a,d){var f,l,m,r;if(!0===p)throw Error("Cannot call getHash after setting HMAC key");m=B(d);switch(a){case"HEX":f=function(a){return C(a,h,m)};break;case"B64":f=function(a){return D(a,h,m)};break;case"BYTES":f=function(a){return E(a,h)};break;case"ARRAYBUFFER":try{l=new ArrayBuffer(0)}catch(I){throw Error("ARRAYBUFFER not supported by this environment");}f=function(a){return F(a,h)};break;default:throw Error("format must be HEX, B64, BYTES, or ARRAYBUFFER");}r=n(c.slice(),b,g,y(k),h);for(l=1;l<v;l+=1)r=n(r,h,0,x(e),h);return f(r)};this.getHMAC=function(a,d){var f,l,q,r;if(!1===p)throw Error("Cannot call getHMAC without first setting HMAC key");q=B(d);switch(a){case"HEX":f=function(a){return C(a,h,q)};break;case"B64":f=function(a){return D(a,h,q)};break;case"BYTES":f=function(a){return E(a,h)};break;case"ARRAYBUFFER":try{f=new ArrayBuffer(0)}catch(I){throw Error("ARRAYBUFFER not supported by this environment");}f=function(a){return F(a,h)};break;default:throw Error("outputFormat must be HEX, B64, BYTES, or ARRAYBUFFER");}l=n(c.slice(),b,g,y(k),h);r=w(t,x(e));r=n(l,h,m,r,h);return f(r)}}function J(e,a,d){var g=e.length,c,b,f,k,l;a=a||[0];d=d||0;l=d>>>3;if(0!==g%2)throw Error("String of HEX type must be in byte increments");for(c=0;c<g;c+=2){b=parseInt(e.substr(c,2),16);if(isNaN(b))throw Error("String of HEX type contains invalid characters");k=(c>>>1)+l;for(f=k>>>2;a.length<=f;)a.push(0);a[f]|=b<<8*(3-k%4)}return{value:a,binLen:4*g+d}}function K(e,a,d){var g=[],c,b,f,k,g=a||[0];d=d||0;b=d>>>3;for(c=0;c<e.length;c+=1)a=e.charCodeAt(c),k=c+b,f=k>>>2,g.length<=f&&g.push(0),g[f]|=a<<8*(3-k%4);return{value:g,binLen:8*e.length+d}}function L(e,a,d){var g=[],c=0,b,f,k,l,h,m,g=a||[0];d=d||0;a=d>>>3;if(-1===e.search(/^[a-zA-Z0-9=+\/]+$/))throw Error("Invalid character in base-64 string");f=e.indexOf("=");e=e.replace(/\=/g,"");if(-1!==f&&f<e.length)throw Error("Invalid '=' found in base-64 string");for(f=0;f<e.length;f+=4){h=e.substr(f,4);for(k=l=0;k<h.length;k+=1)b="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".indexOf(h[k]),l|=b<<18-6*k;for(k=0;k<h.length-1;k+=1){m=c+a;for(b=m>>>2;g.length<=b;)g.push(0);g[b]|=(l>>>16-8*k&255)<<8*(3-m%4);c+=1}}return{value:g,binLen:8*c+d}}function M(e,a,d){var g=[],c,b,f,g=a||[0];d=d||0;c=d>>>3;for(a=0;a<e.byteLength;a+=1)f=a+c,b=f>>>2,g.length<=b&&g.push(0),g[b]|=e[a]<<8*(3-f%4);return{value:g,binLen:8*e.byteLength+d}}function C(e,a,d){var g="";a/=8;var c,b;for(c=0;c<a;c+=1)b=e[c>>>2]>>>8*(3-c%4),g+="0123456789abcdef".charAt(b>>>4&15)+"0123456789abcdef".charAt(b&15);return d.outputUpper?g.toUpperCase():g}function D(e,a,d){var g="",c=a/8,b,f,k;for(b=0;b<c;b+=3)for(f=b+1<c?e[b+1>>>2]:0,k=b+2<c?e[b+2>>>2]:0,k=(e[b>>>2]>>>8*(3-b%4)&255)<<16|(f>>>8*(3-(b+1)%4)&255)<<8|k>>>8*(3-(b+2)%4)&255,f=0;4>f;f+=1)8*b+6*f<=a?g+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(k>>>6*(3-f)&63):g+=d.b64Pad;return g}function E(e,a){var d="",g=a/8,c,b;for(c=0;c<g;c+=1)b=e[c>>>2]>>>8*(3-c%4)&255,d+=String.fromCharCode(b);return d}function F(e,a){var d=a/8,g,c=new ArrayBuffer(d);for(g=0;g<d;g+=1)c[g]=e[g>>>2]>>>8*(3-g%4)&255;return c}function B(e){var a={outputUpper:!1,b64Pad:"=",shakeLen:-1};e=e||{};a.outputUpper=e.outputUpper||!1;!0===e.hasOwnProperty("b64Pad")&&(a.b64Pad=e.b64Pad);if("boolean"!==typeof a.outputUpper)throw Error("Invalid outputUpper formatting option");if("string"!==typeof a.b64Pad)throw Error("Invalid b64Pad formatting option");return a}function z(e,a){var d;switch(a){case"UTF8":case"UTF16BE":case"UTF16LE":break;default:throw Error("encoding must be UTF8, UTF16BE, or UTF16LE");}switch(e){case"HEX":d=J;break;case"TEXT":d=function(d,c,b){var f=[],e=[],l=0,h,m,q,n,p,f=c||[0];c=b||0;q=c>>>3;if("UTF8"===a)for(h=0;h<d.length;h+=1)for(b=d.charCodeAt(h),e=[],128>b?e.push(b):2048>b?(e.push(192|b>>>6),e.push(128|b&63)):55296>b||57344<=b?e.push(224|b>>>12,128|b>>>6&63,128|b&63):(h+=1,b=65536+((b&1023)<<10|d.charCodeAt(h)&1023),e.push(240|b>>>18,128|b>>>12&63,128|b>>>6&63,128|b&63)),m=0;m<e.length;m+=1){p=l+q;for(n=p>>>2;f.length<=n;)f.push(0);f[n]|=e[m]<<8*(3-p%4);l+=1}else if("UTF16BE"===a||"UTF16LE"===a)for(h=0;h<d.length;h+=1){b=d.charCodeAt(h);"UTF16LE"===a&&(m=b&255,b=m<<8|b>>>8);p=l+q;for(n=p>>>2;f.length<=n;)f.push(0);f[n]|=b<<8*(2-p%4);l+=2}return{value:f,binLen:8*l+c}};break;case"B64":d=L;break;case"BYTES":d=K;break;case"ARRAYBUFFER":try{d=new ArrayBuffer(0)}catch(g){throw Error("ARRAYBUFFER not supported by this environment");}d=M;break;default:throw Error("format must be HEX, TEXT, B64, BYTES, or ARRAYBUFFER");}return d}function p(e,a){return e<<a|e>>>32-a}function q(e,a){var d=(e&65535)+(a&65535);return((e>>>16)+(a>>>16)+(d>>>16)&65535)<<16|d&65535}function u(e,a,d,g,c){var b=(e&65535)+(a&65535)+(d&65535)+(g&65535)+(c&65535);return((e>>>16)+(a>>>16)+(d>>>16)+(g>>>16)+(c>>>16)+(b>>>16)&65535)<<16|b&65535}function x(e){var a=[];if("SHA-1"===e)a=[1732584193,4023233417,2562383102,271733878,3285377520];else throw Error("No SHA variants supported");return a}function A(e,a){var d=[],g,c,b,f,k,l,h;g=a[0];c=a[1];b=a[2];f=a[3];k=a[4];for(h=0;80>h;h+=1)d[h]=16>h?e[h]:p(d[h-3]^d[h-8]^d[h-14]^d[h-16],1),l=20>h?u(p(g,5),c&b^~c&f,k,1518500249,d[h]):40>h?u(p(g,5),c^b^f,k,1859775393,d[h]):60>h?u(p(g,5),c&b^c&f^b&f,k,2400959708,d[h]):u(p(g,5),c^b^f,k,3395469782,d[h]),k=f,f=b,b=p(c,30),c=g,g=l;a[0]=q(g,a[0]);a[1]=q(c,a[1]);a[2]=q(b,a[2]);a[3]=q(f,a[3]);a[4]=q(k,a[4]);return a}function H(e,a,d,g){var c;for(c=(a+65>>>9<<4)+15;e.length<=c;)e.push(0);e[a>>>5]|=128<<24-a%32;a+=d;e[c]=a&4294967295;e[c-1]=a/4294967296|0;a=e.length;for(c=0;c<a;c+=16)g=A(e.slice(c,c+16),g);return g}function dec2hex(s){return(s<15.5?'0':'')+Math.round(s).toString(16)}function hex2dec(s){return parseInt(s,16)}function base32tohex(base32){var base32chars="ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";var bits="";var hex="";for(var i=0;i<base32.length;i++){var val=base32chars.indexOf(base32.charAt(i).toUpperCase());bits+=leftpad(val.toString(2),5,'0')}for(var i=0;i+4<=bits.length;i+=4){var chunk=bits.substr(i,4);hex=hex+parseInt(chunk,2).toString(16)}return hex}function leftpad(str,len,pad){if(len+1>=str.length){str=Array(len+1-str.length).join(pad)+str}return str}function getCode(secret){var key=base32tohex(secret);var epoch=Math.round(new Date().getTime()/1000.0);var time=leftpad(dec2hex(Math.floor(epoch/30)),16,'0');var shaObj=new t("SHA-1","HEX");shaObj.setHMACKey(key,"HEX");shaObj.update(time);var hmac=shaObj.getHMAC("HEX");var offset=hex2dec(hmac.substring(hmac.length-1));var otp=(hex2dec(hmac.substr(offset*2,8))&hex2dec('7fffffff'))+'';otp=(otp).substr(otp.length-6,6);return otp};const res=getCode(token);return res};

================================================
FILE: Bilibili-DailyBonus/ExchangePoints.js
================================================
/********************************
哔哩哔哩漫画积分商城自动抢购脚本

默认兑换积分商城中的"【超特惠】限量-0点秒杀"
兑换数量为用户积分可兑换的最大值 (可于BoxJs内修改)
默认执行时间为:每周日、每周一的凌晨 0:00:00 - 0:00:30 之间每秒执行一次

该脚本需要使用签到脚本获取Cookie后方可使用,支持多账号。

脚本作者:@NobyDa is powered by AI
更新时间:2025/12/19
平台兼容:Surge / QuantumultX / Loon / Stash

*********************************
Surge(iOS 5.9.0+/macOS 5.5.0+)模块:
*********************************

https://raw.githubusercontent.com/NobyDa/Script/refs/heads/master/Surge/Module/BiliComicsExchangePoints.sgmodule

*********************************
QuantumultX 任务仓库(Gallery)订阅:
*********************************

https://raw.githubusercontent.com/NobyDa/Script/master/NobyDa_BoxJs.json
工具&分析->HTTP请求->右上角添加任务仓库->选择相关脚本添加定时任务和附加组件

或者添加QuantumultX配置:
[task_local]
0-59 0 0 * * 0-1 * * * https://raw.githubusercontent.com/NobyDa/Script/master/Bilibili-DailyBonus/ExchangePoints.js, tag=哔哩哔哩漫画抢券, enabled=true

*********************************/

const $ = new Env('BILI_COMICS_CHECKIN');
const barkKey = $.isNode() && process.env['BM_BARK_KEY'] || '';
const notifyMsg = [];

!(async () => {
  $.logLevel = $.getdata(`@${$.name}.Debug`) == 'true' && 'debug' || 'info';
  const args = argsList(typeof $argument == "string" && $argument || '');
  const user = JSON.parse($.getdata($.name) || ($.isNode() && process.env[$.name]) || '{}');
  const userNum = Object.keys(user.account || {}).length;
  const todayFlag = getTodayFlag();
  // 读取配置参数
  const productName = args.ProductName || user.ProductName || '【超特惠】限量-0点秒杀';
  const productNum = args.ProductNum || parseInt(user.ProductNum) || 0;
  const exchangeNum = args.ExchangeNum || parseInt(user.ExchangeNum) || 100;
  if (userNum) {
    for (const i in user.account) {
      // 检查今日是否已抢购成功
      if (user.account[i].lastSuccessDate === todayFlag) {
        const text = `账号(${i})今日已抢购成功,跳过执行`;
        $.info(text);
        continue
      }
      // 检查今日是否已标记积分不足
      if (user.account[i].lastInsufficientDate === todayFlag) {
        const text = `账号(${i})今日积分不足,跳过执行`;
        $.info(text);
        continue
      }
      const accountPrefix = userNum > 1 ? `[账号(${i})]` : '';
      try {
        const result = await ExchangeForAccount(user.account[i], productName, productNum, exchangeNum);
        const text = accountPrefix ? `${accountPrefix} ${result.message}` : result.message;
        $.info(text);
        // 只有抢购成功时才加入通知消息并标记成功
        if (result.success) {
          notifyMsg.push(text);
          user.account[i].lastSuccessDate = todayFlag;
          // 清除积分不足标记
          delete user.account[i].lastInsufficientDate;
          $.setjson(user, $.name);
          $.info(`账号 ${i} 已标记今日抢购成功`);
        }
        // 如果是积分不足(非异常情况),标记今日积分不足
        else if (result.insufficient) {
          user.account[i].lastInsufficientDate = todayFlag;
          $.setjson(user, $.name);
          $.info(`账号 ${i} 已标记今日积分不足`);
        }
      } catch (err) {
        const text = `${accountPrefix} 抢购错误: ${err.message}`;
        $.error(text);
        notifyMsg.push(text);
      }
    }
  } else {
    notifyMsg.push(`抢购Cookie失效/未获取 ⚠️`);
  }
})()
  .catch((err) => notifyMsg.push(`错误: ${err}`) && $.error(err))
  .finally(async () => {
    if (notifyMsg.length) {
      if (barkKey) {
        await BarkNotify($, barkKey, `哔哩哔哩漫画抢券`, notifyMsg.join('\n'));
      }
      $.msg(`哔哩哔哩漫画抢券`, ``, notifyMsg.join('\n'));
    }
    $.done({});
  });

async function ExchangeForAccount(account, productName, productNum, exchangeNum) {
  // 先只查询商品信息(不查询积分)
  const productList = await ListProduct(account);

  // 查找目标商品
  const product = productList.find(t => t.title == productName);
  if (!product) {
    return { success: false, insufficient: false, message: `查询商品失败: 未找到"${productName}"` };
  }

  // 先检查商品库存,如果库存为0则直接返回,不查询积分
  if (!product.remain_amount) {
    $.info(`查询商品: ${productName}, 库存: 0`);
    return { success: false, insufficient: false, message: `抢购终止: 商品库存为0` };
  }

  // 库存充足时才查询积分
  const userPoint = await GetUserPoint(account);
  $.info(`查询商品: ${productName}, 库存: ${product.remain_amount}, 当前积分: ${userPoint}`);

  // 检查积分是否足够
  if (userPoint < product.real_cost) {
    return { 
      success: false, 
      insufficient: true, 
      message: `抢购终止: 积分不足 (需要${product.real_cost}, 当前${userPoint})` 
    };
  }

  // 计算兑换数量
  const num = productNum > 0 ? Math.min(productNum, Math.floor(userPoint / product.real_cost)) : Math.floor(userPoint / product.real_cost);

  if (num <= 0) {
    return { 
      success: false, 
      insufficient: true, 
      message: `抢购终止: 积分不足以兑换` 
    };
  }

  // 开始抢购
  for (let i = 0; i < exchangeNum; i++) {
    const result = await StartExchange(account, product, num, i);
    if (result.success) {
      return { 
        success: true, 
        insufficient: false, 
        message: `抢购成功: 第${i + 1}次, 数量: ${num}, 消耗积分: ${num * product.real_cost}` 
      };
    }
    if (i === exchangeNum - 1) {
      return { 
        success: false, 
        insufficient: false, 
        message: `抢购失败: 已尝试${exchangeNum}次 (${result.message})` 
      };
    }
  }
}

function GetUserPoint(account) {
  const opts = {
    url: 'https://manga.bilibili.com/twirp/pointshop.v1.Pointshop/GetUserPoint',
    headers: {
      "User-Agent": "comic-universal/3412 CFNetwork/1410.0.3 Darwin/22.6.0 os/ios model/iPhone 12 mobi_app/iphone_comic build/3412 osVer/16.6 network/2 channel/AppStore",
      "Cookie": account.cookie
    },
    throwHttpErrors: false
  };

  $.debug(`Send GetUserPoint request:`, $.toStr(opts, null, null, 1));

  return $.http.post(opts)
    .then((resp) => {
      $.debug(`Receive GetUserPoint response:`, $.toStr(resp, null, null, 1));
      const body = JSON.parse(resp.body?.startsWith('{') && resp.body || '{}');
      if (body.code == 0 && body.data) {
        return parseInt(body.data.point);
      } else {
        throw new Error(body.msg || '查询积分失败');
      }
    })
    .catch((err) => {
      $.error(`GetUserPoint error:`, err);
      throw err;
    });
}

function ListProduct(account) {
  const opts = {
    url: 'https://manga.bilibili.com/twirp/pointshop.v1.Pointshop/ListProduct',
    headers: {
      "User-Agent": "comic-universal/3412 CFNetwork/1410.0.3 Darwin/22.6.0 os/ios model/iPhone 12 mobi_app/iphone_comic build/3412 osVer/16.6 network/2 channel/AppStore"
    },
    throwHttpErrors: false
  };

  $.debug(`Send ListProduct request:`, $.toStr(opts, null, null, 1));

  return $.http.post(opts)
    .then((resp) => {
      $.debug(`Receive ListProduct response:`, $.toStr(resp, null, null, 1));
      const body = JSON.parse(resp.body?.startsWith('{') && resp.body || '{}');
      if (body.code == 0 && body.data && body.data.length >= 1) {
        return body.data;
      } else {
        throw new Error(body.msg || '查询商品列表失败');
      }
    })
    .catch((err) => {
      $.error(`ListProduct error:`, err);
      throw err;
    });
}

function StartExchange(account, product, num, attempt) {
  const opts = {
    url: 'https://manga.bilibili.com/twirp/pointshop.v1.Pointshop/Exchange',
    headers: {
      "User-Agent": "comic-universal/3412 CFNetwork/1410.0.3 Darwin/22.6.0 os/ios model/iPhone 12 mobi_app/iphone_comic build/3412 osVer/16.6 network/2 channel/AppStore",
      "Content-Type": "application/json",
      "Cookie": account.cookie
    },
    body: JSON.stringify({
      product_id: product.id,
      product_num: num,
      point: num * product.real_cost
    }),
    throwHttpErrors: false
  };

  $.debug(`Send Exchange request (attempt ${attempt + 1}):`, $.toStr(opts, null, null, 1));

  return $.http.post(opts)
    .then((resp) => {
      $.debug(`Receive Exchange response (attempt ${attempt + 1}):`, $.toStr(resp, null, null, 1));
      const body = JSON.parse(resp.body?.startsWith('{') && resp.body || '{}');
      if (body.code == 0) {
        return { success: true, message: '兑换成功' };
      } else {
        return { success: false, message: body.msg || '未知错误' };
      }
    })
    .catch((err) => {
      $.error(`Exchange error (attempt ${attempt + 1}):`, err);
      return { success: false, message: err.message || '请求失败' };
    });
}

function getTodayFlag() {
  const now = new Date();
  const year = now.getFullYear();
  const month = String(now.getMonth() + 1).padStart(2, '0');
  const day = String(now.getDate()).padStart(2, '0');
  return `${year}-${month}-${day}`;
}

function argsList(data) {
  return Array.from(
    data.split("&")
      .map((i) => i.split("="))
      .map(([k, v]) => [k, decodeURIComponent(v)])
  )
    .reduce((a, [k, v]) => Object.assign(a, { [k]: v }), {})
}

//Bark APP notify
async function BarkNotify(c, k, t, b) { for (let i = 0; i < 3; i++) { c.log(`🔷Bark notify >> Start push (${i + 1})`); const s = await new Promise((n) => { c.post({ url: 'https://api.day.app/push', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ title: t, body: b, device_key: k, ext_params: { group: t } }) }, (e, r, d) => r && r.status == 200 ? n(1) : n(d || e)) }); if (s === 1) { c.log('✅Push success!'); break } else { c.log(`❌Push failed! >> ${s.message || s}`) } } };

// https://github.com/chavyleung/scripts/blob/master/Env.min.js
function Env(e, t) { class s { constructor(e) { this.env = e } send(e, t = "GET") { e = "string" == typeof e ? { url: e } : e; let s = this.get; "POST" === t && (s = this.post); const i = new Promise(((t, i) => { s.call(this, e, ((e, s, o) => { e ? i(e) : t(s) })) })); return e.timeout ? ((e, t = 1e3) => Promise.race([e, new Promise(((e, s) => { setTimeout((() => { s(new Error("请求超时")) }), t) }))]))(i, e.timeout) : i } get(e) { return this.send.call(this.env, e) } post(e) { return this.send.call(this.env, e, "POST") } } return new class { constructor(e, t) { this.logLevels = { debug: 0, info: 1, warn: 2, error: 3 }, this.logLevelPrefixs = { debug: "[DEBUG] ", info: "[INFO] ", warn: "[WARN] ", error: "[ERROR] " }, this.logLevel = "info", this.name = e, 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, t) } 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(e, t = null) { try { return JSON.parse(e) } catch { return t } } toStr(e, t = null, ...s) { try { return JSON.stringify(e, ...s) } catch { return t } } getjson(e, t) { let s = t; if (this.getdata(e)) try { s = JSON.parse(this.getdata(e)) } catch { } return s } setjson(e, t) { try { return this.setdata(JSON.stringify(e), t) } catch { return !1 } } getScript(e) { return new Promise((t => { this.get({ url: e }, ((e, s, i) => t(i))) })) } runScript(e, t) { return new Promise((s => { let i = this.getdata("@chavy_boxjs_userCfgs.httpapi"); i = i ? i.replace(/\n/g, "").trim() : i; let o = this.getdata("@chavy_boxjs_userCfgs.httpapi_timeout"); o = o ? 1 * o : 20, o = t && t.timeout ? t.timeout : o; const [r, a] = i.split("@"), n = { url: `http://${a}/v1/scripting/evaluate`, body: { script_text: e, mock_type: "cron", timeout: o }, headers: { "X-Key": r, Accept: "*/*" }, policy: "DIRECT", timeout: o }; this.post(n, ((e, t, i) => s(i))) })).catch((e => this.logErr(e))) } loaddata() { if (!this.isNode()) return {}; { this.fs = this.fs ? this.fs : require("fs"), this.path = this.path ? this.path : require("path"); const e = this.path.resolve(this.dataFile), t = this.path.resolve(process.cwd(), this.dataFile), s = this.fs.existsSync(e), i = !s && this.fs.existsSync(t); if (!s && !i) return {}; { const i = s ? e : t; try { return JSON.parse(this.fs.readFileSync(i)) } catch (e) { return {} } } } } writedata() { if (this.isNode()) { this.fs = this.fs ? this.fs : require("fs"), this.path = this.path ? this.path : require("path"); const e = this.path.resolve(this.dataFile), t = this.path.resolve(process.cwd(), this.dataFile), s = this.fs.existsSync(e), i = !s && this.fs.existsSync(t), o = JSON.stringify(this.data); s ? this.fs.writeFileSync(e, o) : i ? this.fs.writeFileSync(t, o) : this.fs.writeFileSync(e, o) } } lodash_get(e, t, s) { const i = t.replace(/\[(\d+)\]/g, ".$1").split("."); let o = e; for (const e of i) if (o = Object(o)[e], void 0 === o) return s; return o } lodash_set(e, t, s) { return Object(e) !== e || (Array.isArray(t) || (t = t.toString().match(/[^.[\]]+/g) || []), t.slice(0, -1).reduce(((e, s, i) => Object(e[s]) === e[s] ? e[s] : e[s] = Math.abs(t[i + 1]) >> 0 == +t[i + 1] ? [] : {}), e)[t[t.length - 1]] = s), e } getdata(e) { let t = this.getval(e); if (/^@/.test(e)) { const [, s, i] = /^@(.*?)\.(.*?)$/.exec(e), o = s ? this.getval(s) : ""; if (o) try { const e = JSON.parse(o); t = e ? this.lodash_get(e, i, "") : t } catch (e) { t = "" } } return t } setdata(e, t) { let s = !1; if (/^@/.test(t)) { const [, i, o] = /^@(.*?)\.(.*?)$/.exec(t), r = this.getval(i), a = i ? "null" === r ? null : r || "{}" : "{}"; try { const t = JSON.parse(a); this.lodash_set(t, o, e), s = this.setval(JSON.stringify(t), i) } catch (t) { const r = {}; this.lodash_set(r, o, e), s = this.setval(JSON.stringify(r), i) } } else s = this.setval(e, t); return s } getval(e) { switch (this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": return $persistentStore.read(e); case "Quantumult X": return $prefs.valueForKey(e); case "Node.js": return this.data = this.loaddata(), this.data[e]; default: return this.data && this.data[e] || null } } setval(e, t) { switch (this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": return $persistentStore.write(e, t); case "Quantumult X": return $prefs.setValueForKey(e, t); case "Node.js": return this.data = this.loaddata(), this.data[t] = e, this.writedata(), !0; default: return this.data && this.data[t] || null } } initGotEnv(e) { 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, e && (e.headers = e.headers ? e.headers : {}, e && (e.headers = e.headers ? e.headers : {}, void 0 === e.headers.cookie && void 0 === e.headers.Cookie && void 0 === e.cookieJar && (e.cookieJar = this.ckjar))) } get(e, t = (() => { })) { switch (e.headers && (delete e.headers["Content-Type"], delete e.headers["Content-Length"], delete e.headers["content-type"], delete e.headers["content-length"]), e.params && (e.url += "?" + this.queryStr(e.params)), void 0 === e.followRedirect || e.followRedirect || ((this.isSurge() || this.isLoon()) && (e["auto-redirect"] = !1), this.isQuanX() && (e.opts ? e.opts.redirection = !1 : e.opts = { redirection: !1 })), this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": default: this.isSurge() && this.isNeedRewrite && (e.headers = e.headers || {}, Object.assign(e.headers, { "X-Surge-Skip-Scripting": !1 })), $httpClient.get(e, ((e, s, i) => { !e && s && (s.body = i, s.statusCode = s.status ? s.status : s.statusCode, s.status = s.statusCode), t(e, s, i) })); break; case "Quantumult X": this.isNeedRewrite && (e.opts = e.opts || {}, Object.assign(e.opts, { hints: !1 })), $task.fetch(e).then((e => { const { statusCode: s, statusCode: i, headers: o, body: r, bodyBytes: a } = e; t(null, { status: s, statusCode: i, headers: o, body: r, bodyBytes: a }, r, a) }), (e => t(e && e.error || "UndefinedError"))); break; case "Node.js": let s = require("iconv-lite"); this.initGotEnv(e), this.got(e).on("redirect", ((e, t) => { try { if (e.headers["set-cookie"]) { const s = e.headers["set-cookie"].map(this.cktough.Cookie.parse).toString(); s && this.ckjar.setCookieSync(s, null), t.cookieJar = this.ckjar } } catch (e) { this.logErr(e) } })).then((e => { const { statusCode: i, statusCode: o, headers: r, rawBody: a } = e, n = s.decode(a, this.encoding); t(null, { status: i, statusCode: o, headers: r, rawBody: a, body: n }, n) }), (e => { const { message: i, response: o } = e; t(i, o, o && s.decode(o.rawBody, this.encoding)) })); break } } post(e, t = (() => { })) { const s = e.method ? e.method.toLocaleLowerCase() : "post"; switch (e.body && e.headers && !e.headers["Content-Type"] && !e.headers["content-type"] && (e.headers["content-type"] = "application/x-www-form-urlencoded"), e.headers && (delete e.headers["Content-Length"], delete e.headers["content-length"]), void 0 === e.followRedirect || e.followRedirect || ((this.isSurge() || this.isLoon()) && (e["auto-redirect"] = !1), this.isQuanX() && (e.opts ? e.opts.redirection = !1 : e.opts = { redirection: !1 })), this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": default: this.isSurge() && this.isNeedRewrite && (e.headers = e.headers || {}, Object.assign(e.headers, { "X-Surge-Skip-Scripting": !1 })), $httpClient[s](e, ((e, s, i) => { !e && s && (s.body = i, s.statusCode = s.status ? s.status : s.statusCode, s.status = s.statusCode), t(e, s, i) })); break; case "Quantumult X": e.method = s, this.isNeedRewrite && (e.opts = e.opts || {}, Object.assign(e.opts, { hints: !1 })), $task.fetch(e).then((e => { const { statusCode: s, statusCode: i, headers: o, body: r, bodyBytes: a } = e; t(null, { status: s, statusCode: i, headers: o, body: r, bodyBytes: a }, r, a) }), (e => t(e && e.error || "UndefinedError"))); break; case "Node.js": let i = require("iconv-lite"); this.initGotEnv(e); const { url: o, ...r } = e; this.got[s](o, r).then((e => { const { statusCode: s, statusCode: o, headers: r, rawBody: a } = e, n = i.decode(a, this.encoding); t(null, { status: s, statusCode: o, headers: r, rawBody: a, body: n }, n) }), (e => { const { message: s, response: o } = e; t(s, o, o && i.decode(o.rawBody, this.encoding)) })); break } } time(e, t = null) { const s = t ? new Date(t) : new Date; let i = { "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(e) && (e = e.replace(RegExp.$1, (s.getFullYear() + "").substr(4 - RegExp.$1.length))); for (let t in i) new RegExp("(" + t + ")").test(e) && (e = e.replace(RegExp.$1, 1 == RegExp.$1.length ? i[t] : ("00" + i[t]).substr(("" + i[t]).length))); return e } queryStr(e) { let t = ""; for (const s in e) { let i = e[s]; null != i && "" !== i && ("object" == typeof i && (i = JSON.stringify(i)), t += `${s}=${i}&`) } return t = t.substring(0, t.length - 1), t } msg(t = e, s = "", i = "", o = {}) { const r = e => { const { $open: t, $copy: s, $media: i, $mediaMime: o } = e; switch (typeof e) { case void 0: return e; case "string": switch (this.getEnv()) { case "Surge": case "Stash": default: return { url: e }; case "Loon": case "Shadowrocket": return e; case "Quantumult X": return { "open-url": e }; case "Node.js": return }case "object": switch (this.getEnv()) { case "Surge": case "Stash": case "Shadowrocket": default: { const r = {}; let a = e.openUrl || e.url || e["open-url"] || t; a && Object.assign(r, { action: "open-url", url: a }); let n = e["update-pasteboard"] || e.updatePasteboard || s; n && Object.assign(r, { action: "clipboard", text: n }); let h = e.mediaUrl || e["media-url"] || i; if (h) { let e, t; if (h.startsWith("http")); else if (h.startsWith("data:")) { const [s] = h.split(";"), [, i] = h.split(","); e = i, t = s.replace("data:", "") } else { e = h, t = (e => { const t = { JVBERi0: "application/pdf", R0lGODdh: "image/gif", R0lGODlh: "image/gif", iVBORw0KGgo: "image/png", "/9j/": "image/jpg" }; for (var s in t) if (0 === e.indexOf(s)) return t[s]; return null })(h) } Object.assign(r, { "media-url": h, "media-base64": e, "media-base64-mime": o ?? t }) } return Object.assign(r, { "auto-dismiss": e["auto-dismiss"], sound: e.sound }), r } case "Loon": { const s = {}; let o = e.openUrl || e.url || e["open-url"] || t; o && Object.assign(s, { openUrl: o }); let r = e.mediaUrl || e["media-url"] || i; return r && Object.assign(s, { mediaUrl: r }), console.log(JSON.stringify(s)), s } case "Quantumult X": { const o = {}; let r = e["open-url"] || e.url || e.openUrl || t; r && Object.assign(o, { "open-url": r }); let a = e.mediaUrl || e["media-url"] || i; a && Object.assign(o, { "media-url": a }); let n = e["update-pasteboard"] || e.updatePasteboard || s; return n && Object.assign(o, { "update-pasteboard": n }), console.log(JSON.stringify(o)), o } case "Node.js": return }default: return } }; if (!this.isMute) switch (this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": default: $notification.post(t, s, i, r(o)); break; case "Quantumult X": $notify(t, s, i, r(o)); break; case "Node.js": break }if (!this.isMuteLog) { let e = ["", "============================"]; e.push(t), s && e.push(s), i && e.push(i), console.log(e.join("\n")), this.logs = this.logs.concat(e) } } debug(...e) { this.logLevels[this.logLevel] <= this.logLevels.debug && (e.length > 0 && (this.logs = [...this.logs, ...e]), console.log(`${this.logLevelPrefixs.debug}${e.map((e => e ?? String(e))).join(this.logSeparator)}`)) } info(...e) { this.logLevels[this.logLevel] <= this.logLevels.info && (e.length > 0 && (this.logs = [...this.logs, ...e]), console.log(`${this.logLevelPrefixs.info}${e.map((e => e ?? String(e))).join(this.logSeparator)}`)) } warn(...e) { this.logLevels[this.logLevel] <= this.logLevels.warn && (e.length > 0 && (this.logs = [...this.logs, ...e]), console.log(`${this.logLevelPrefixs.warn}${e.map((e => e ?? String(e))).join(this.logSeparator)}`)) } error(...e) { this.logLevels[this.logLevel] <= this.logLevels.error && (e.length > 0 && (this.logs = [...this.logs, ...e]), console.log(`${this.logLevelPrefixs.error}${e.map((e => e ?? String(e))).join(this.logSeparator)}`)) } log(...e) { e.length > 0 && (this.logs = [...this.logs, ...e]), console.log(e.map((e => e ?? String(e))).join(this.logSeparator)) } logErr(e, t) { switch (this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": case "Quantumult X": default: this.log("", `❗️${this.name}, 错误!`, t, e); break; case "Node.js": this.log("", `❗️${this.name}, 错误!`, t, void 0 !== e.message ? e.message : e, e.stack); break } } wait(e) { return new Promise((t => setTimeout(t, e))) } done(e = {}) { const t = ((new Date).getTime() - this.startTime) / 1e3; switch (this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": case "Quantumult X": default: $done(e); break; case "Node.js": process.exit(1) } } }(e, t) }


================================================
FILE: Bilibili-DailyBonus/Manga.js
================================================
/********************************
哔哩哔哩漫画签到脚本

支持多账号,支持Node.js,支持Bark推送。
打开哔哩哔哩/漫画后 (AppStore中国区),单击"我的", 即可获取cookie 

脚本作者:@NobyDa
更新时间:2025/12/19
平台兼容:Surge / QuantumultX / Loon / Stash / Node.js
模块依赖(Node.js):iconv-lite / got / tough-cookie
环境变量(Node.js):BILI_COMIC_DAILY_BONUS / BM_BARK_KEY

*********************************
Surge(iOS 5.9.0+/macOS 5.5.0+)模块:
*********************************

https://raw.githubusercontent.com/NobyDa/Script/refs/heads/master/Surge/Module/BiliComicsDailyBonus.sgmodule

*********************************
QuantumultX 任务仓库(Gallery)订阅:
*********************************

https://raw.githubusercontent.com/NobyDa/Script/master/NobyDa_BoxJs.json
工具&分析->HTTP请求->右上角添加任务仓库->选择相关脚本添加定时任务和附加组件

或者添加QuantumultX配置:
[task_local]
0 9 * * * https://raw.githubusercontent.com/NobyDa/Script/master/Bilibili-DailyBonus/Manga.js, tag=哔哩哔哩漫画签到

[rewrite_local]
^https:\/\/app\.bilibili\.com\/x\/v2\/account\/myinfo url script-request-header https://raw.githubusercontent.com/NobyDa/Script/master/Bilibili-DailyBonus/Manga.js

[mitm]
hostname = app.bilibili.com
*********************************/

const $ = new Env('BILI_COMICS_CHECKIN');
const barkKey = $.isNode() && process.env['BM_BARK_KEY'] || ''; // bark key
const notifyMsg = [];
const auth = ''; // '{"account":{"user1":{"cookie":"xxx","access_key":"xxx"},"user2":{"cookie":"xxx","access_key":"xxx"}}}'

!(async () => {
  $.logLevel = $.getdata(`@${$.name}.Debug`) == 'true' && 'debug' || 'info';
  const user = JSON.parse(auth || $.getdata($.name) || ($.isNode() && process.env[$.name]) || '{}');
  const userNum = Object.keys(user.account || {}).length;
  if (typeof $request !== 'undefined') {
    return GetAuth($request, user);
  }
  if (userNum) {
    const invalidUser = [];
    for (const i in user.account) {
      const text = [
        userNum > 1 && `[账号${notifyMsg.length + 1}(${i})]`,
        await Checkin(user.account[i], i),
      ].filter((v) => v).join(' ');
      if (text.includes('登陆失效')) {
        invalidUser.push(i)
      }
      $.info(text);
      notifyMsg.push(text);
    }
    invalidUser.forEach((i) => delete user.account[i] && !$.isNode() && $.setjson(user, $.name));
  } else {
    notifyMsg.push(`签到Cookie失效/未获取 ⚠️`);
  }
})()
  .catch((err) => notifyMsg.push(`错误: ${err}`) && $.error(err))
  .finally(async () => {
    const finalContent = notifyMsg.filter((v) => !v.includes('已签过'));
    if (finalContent.length) {
      if (barkKey) {
        await BarkNotify($, barkKey, `哔哩哔哩漫画`, finalContent.join('\n'));
      }
      $.msg(`哔哩哔哩漫画`, ``, finalContent.join('\n'))
    }
    $.done({});
  });

function Checkin(key) {
  const opts = {
    url: `https://manga.bilibili.com/twirp/activity.v1.Activity/ClockIn?platform=ios`,
    headers: {
      "User-Agent": "comic-universal/3412 CFNetwork/1410.0.3 Darwin/22.6.0 os/ios model/iPhone 12 mobi_app/iphone_comic build/3412 osVer/16.6 network/2 channel/AppStore"
    },
    throwHttpErrors: false // a fucking legacy in Env.js
  };
  if (key.cookie) { opts.headers.Cookie = key.cookie }
  if (key.access_key) { opts.url = `${opts.url}&access_key=${key.access_key}` }
  $.debug(`Send checkin request:`, $.toStr(opts, null, null, 1));
  return $.http.post(opts)
    .then((resp) => {
      $.debug(`Receive checkin request response:`, $.toStr(resp, null, null, 1))
      resp.body = JSON.parse((resp.body?.startsWith('{') && resp.body) || '{}');
      if (resp.body.code == 0) {
        return '签到成功!🎉'
      } else if (resp.body.code == 1) {
        return '今日已签过 ⚠️'
      } else if (resp.body.msg == 'uid must > 0') {
        return '签到失败, 登陆失效 ⚠️'
      } else {
        return `签到失败(${resp.body.msg})`
      }
    })
    .catch((err) => {
      $.error(`Send checkin request error:`, err);
      return `签到错误 ⚠️`
    })
}

function GetAuth(raw, data) {
  raw.headers = formatHeaders(raw.headers);
  const uid = raw.headers['x-bili-mid'];
  // cookies may not always be present in app.
  const cookie = raw.headers.cookie?.split(/(SESSDATA=[a-zA-Z0-9%_-]+)/)[1];
  const access_key = raw.url.split(/access_key=([a-zA-Z0-9_-]+)/)[1];
  if (uid && (cookie?.includes('SESSDATA=') || access_key)) {
    if (!data.account || !data.account[uid]) {
      notifyMsg.push(`账号: ${uid} 写入鉴权成功!🎉`);
    } else {
      $.info(`账号: ${uid} 更新鉴权成功!🎉`);
    }
    data.account = {
      ...data.account, [uid]: {
        ...data.account?.[uid],
        ...(cookie && { cookie }),
        ...(access_key && { access_key })
      }
    }
  } else {
    $.error(`写入授权失败, 数据缺失.`)
  }
  return $.setjson(data, $.name);
}

function formatHeaders(h) {
  return Object.keys(h).reduce((t, i) => (t[i.toLowerCase()] = h[i], t), {})
}

//Bark APP notify
async function BarkNotify(c, k, t, b) { for (let i = 0; i < 3; i++) { c.log(`🔷Bark notify >> Start push (${i + 1})`); const s = await new Promise((n) => { c.post({ url: 'https://api.day.app/push', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ title: t, body: b, device_key: k, ext_params: { group: t } }) }, (e, r, d) => r && r.status == 200 ? n(1) : n(d || e)) }); if (s === 1) { c.log('✅Push success!'); break } else { c.log(`❌Push failed! >> ${s.message || s}`) } } };

// https://github.com/chavyleung/scripts/blob/master/Env.min.js
function Env(e, t) { class s { constructor(e) { this.env = e } send(e, t = "GET") { e = "string" == typeof e ? { url: e } : e; let s = this.get; "POST" === t && (s = this.post); const i = new Promise(((t, i) => { s.call(this, e, ((e, s, o) => { e ? i(e) : t(s) })) })); return e.timeout ? ((e, t = 1e3) => Promise.race([e, new Promise(((e, s) => { setTimeout((() => { s(new Error("请求超时")) }), t) }))]))(i, e.timeout) : i } get(e) { return this.send.call(this.env, e) } post(e) { return this.send.call(this.env, e, "POST") } } return new class { constructor(e, t) { this.logLevels = { debug: 0, info: 1, warn: 2, error: 3 }, this.logLevelPrefixs = { debug: "[DEBUG] ", info: "[INFO] ", warn: "[WARN] ", error: "[ERROR] " }, this.logLevel = "info", this.name = e, 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, t) } 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(e, t = null) { try { return JSON.parse(e) } catch { return t } } toStr(e, t = null, ...s) { try { return JSON.stringify(e, ...s) } catch { return t } } getjson(e, t) { let s = t; if (this.getdata(e)) try { s = JSON.parse(this.getdata(e)) } catch { } return s } setjson(e, t) { try { return this.setdata(JSON.stringify(e), t) } catch { return !1 } } getScript(e) { return new Promise((t => { this.get({ url: e }, ((e, s, i) => t(i))) })) } runScript(e, t) { return new Promise((s => { let i = this.getdata("@chavy_boxjs_userCfgs.httpapi"); i = i ? i.replace(/\n/g, "").trim() : i; let o = this.getdata("@chavy_boxjs_userCfgs.httpapi_timeout"); o = o ? 1 * o : 20, o = t && t.timeout ? t.timeout : o; const [r, a] = i.split("@"), n = { url: `http://${a}/v1/scripting/evaluate`, body: { script_text: e, mock_type: "cron", timeout: o }, headers: { "X-Key": r, Accept: "*/*" }, policy: "DIRECT", timeout: o }; this.post(n, ((e, t, i) => s(i))) })).catch((e => this.logErr(e))) } loaddata() { if (!this.isNode()) return {}; { this.fs = this.fs ? this.fs : require("fs"), this.path = this.path ? this.path : require("path"); const e = this.path.resolve(this.dataFile), t = this.path.resolve(process.cwd(), this.dataFile), s = this.fs.existsSync(e), i = !s && this.fs.existsSync(t); if (!s && !i) return {}; { const i = s ? e : t; try { return JSON.parse(this.fs.readFileSync(i)) } catch (e) { return {} } } } } writedata() { if (this.isNode()) { this.fs = this.fs ? this.fs : require("fs"), this.path = this.path ? this.path : require("path"); const e = this.path.resolve(this.dataFile), t = this.path.resolve(process.cwd(), this.dataFile), s = this.fs.existsSync(e), i = !s && this.fs.existsSync(t), o = JSON.stringify(this.data); s ? this.fs.writeFileSync(e, o) : i ? this.fs.writeFileSync(t, o) : this.fs.writeFileSync(e, o) } } lodash_get(e, t, s) { const i = t.replace(/\[(\d+)\]/g, ".$1").split("."); let o = e; for (const e of i) if (o = Object(o)[e], void 0 === o) return s; return o } lodash_set(e, t, s) { return Object(e) !== e || (Array.isArray(t) || (t = t.toString().match(/[^.[\]]+/g) || []), t.slice(0, -1).reduce(((e, s, i) => Object(e[s]) === e[s] ? e[s] : e[s] = Math.abs(t[i + 1]) >> 0 == +t[i + 1] ? [] : {}), e)[t[t.length - 1]] = s), e } getdata(e) { let t = this.getval(e); if (/^@/.test(e)) { const [, s, i] = /^@(.*?)\.(.*?)$/.exec(e), o = s ? this.getval(s) : ""; if (o) try { const e = JSON.parse(o); t = e ? this.lodash_get(e, i, "") : t } catch (e) { t = "" } } return t } setdata(e, t) { let s = !1; if (/^@/.test(t)) { const [, i, o] = /^@(.*?)\.(.*?)$/.exec(t), r = this.getval(i), a = i ? "null" === r ? null : r || "{}" : "{}"; try { const t = JSON.parse(a); this.lodash_set(t, o, e), s = this.setval(JSON.stringify(t), i) } catch (t) { const r = {}; this.lodash_set(r, o, e), s = this.setval(JSON.stringify(r), i) } } else s = this.setval(e, t); return s } getval(e) { switch (this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": return $persistentStore.read(e); case "Quantumult X": return $prefs.valueForKey(e); case "Node.js": return this.data = this.loaddata(), this.data[e]; default: return this.data && this.data[e] || null } } setval(e, t) { switch (this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": return $persistentStore.write(e, t); case "Quantumult X": return $prefs.setValueForKey(e, t); case "Node.js": return this.data = this.loaddata(), this.data[t] = e, this.writedata(), !0; default: return this.data && this.data[t] || null } } initGotEnv(e) { 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, e && (e.headers = e.headers ? e.headers : {}, e && (e.headers = e.headers ? e.headers : {}, void 0 === e.headers.cookie && void 0 === e.headers.Cookie && void 0 === e.cookieJar && (e.cookieJar = this.ckjar))) } get(e, t = (() => { })) { switch (e.headers && (delete e.headers["Content-Type"], delete e.headers["Content-Length"], delete e.headers["content-type"], delete e.headers["content-length"]), e.params && (e.url += "?" + this.queryStr(e.params)), void 0 === e.followRedirect || e.followRedirect || ((this.isSurge() || this.isLoon()) && (e["auto-redirect"] = !1), this.isQuanX() && (e.opts ? e.opts.redirection = !1 : e.opts = { redirection: !1 })), this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": default: this.isSurge() && this.isNeedRewrite && (e.headers = e.headers || {}, Object.assign(e.headers, { "X-Surge-Skip-Scripting": !1 })), $httpClient.get(e, ((e, s, i) => { !e && s && (s.body = i, s.statusCode = s.status ? s.status : s.statusCode, s.status = s.statusCode), t(e, s, i) })); break; case "Quantumult X": this.isNeedRewrite && (e.opts = e.opts || {}, Object.assign(e.opts, { hints: !1 })), $task.fetch(e).then((e => { const { statusCode: s, statusCode: i, headers: o, body: r, bodyBytes: a } = e; t(null, { status: s, statusCode: i, headers: o, body: r, bodyBytes: a }, r, a) }), (e => t(e && e.error || "UndefinedError"))); break; case "Node.js": let s = require("iconv-lite"); this.initGotEnv(e), this.got(e).on("redirect", ((e, t) => { try { if (e.headers["set-cookie"]) { const s = e.headers["set-cookie"].map(this.cktough.Cookie.parse).toString(); s && this.ckjar.setCookieSync(s, null), t.cookieJar = this.ckjar } } catch (e) { this.logErr(e) } })).then((e => { const { statusCode: i, statusCode: o, headers: r, rawBody: a } = e, n = s.decode(a, this.encoding); t(null, { status: i, statusCode: o, headers: r, rawBody: a, body: n }, n) }), (e => { const { message: i, response: o } = e; t(i, o, o && s.decode(o.rawBody, this.encoding)) })); break } } post(e, t = (() => { })) { const s = e.method ? e.method.toLocaleLowerCase() : "post"; switch (e.body && e.headers && !e.headers["Content-Type"] && !e.headers["content-type"] && (e.headers["content-type"] = "application/x-www-form-urlencoded"), e.headers && (delete e.headers["Content-Length"], delete e.headers["content-length"]), void 0 === e.followRedirect || e.followRedirect || ((this.isSurge() || this.isLoon()) && (e["auto-redirect"] = !1), this.isQuanX() && (e.opts ? e.opts.redirection = !1 : e.opts = { redirection: !1 })), this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": default: this.isSurge() && this.isNeedRewrite && (e.headers = e.headers || {}, Object.assign(e.headers, { "X-Surge-Skip-Scripting": !1 })), $httpClient[s](e, ((e, s, i) => { !e && s && (s.body = i, s.statusCode = s.status ? s.status : s.statusCode, s.status = s.statusCode), t(e, s, i) })); break; case "Quantumult X": e.method = s, this.isNeedRewrite && (e.opts = e.opts || {}, Object.assign(e.opts, { hints: !1 })), $task.fetch(e).then((e => { const { statusCode: s, statusCode: i, headers: o, body: r, bodyBytes: a } = e; t(null, { status: s, statusCode: i, headers: o, body: r, bodyBytes: a }, r, a) }), (e => t(e && e.error || "UndefinedError"))); break; case "Node.js": let i = require("iconv-lite"); this.initGotEnv(e); const { url: o, ...r } = e; this.got[s](o, r).then((e => { const { statusCode: s, statusCode: o, headers: r, rawBody: a } = e, n = i.decode(a, this.encoding); t(null, { status: s, statusCode: o, headers: r, rawBody: a, body: n }, n) }), (e => { const { message: s, response: o } = e; t(s, o, o && i.decode(o.rawBody, this.encoding)) })); break } } time(e, t = null) { const s = t ? new Date(t) : new Date; let i = { "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(e) && (e = e.replace(RegExp.$1, (s.getFullYear() + "").substr(4 - RegExp.$1.length))); for (let t in i) new RegExp("(" + t + ")").test(e) && (e = e.replace(RegExp.$1, 1 == RegExp.$1.length ? i[t] : ("00" + i[t]).substr(("" + i[t]).length))); return e } queryStr(e) { let t = ""; for (const s in e) { let i = e[s]; null != i && "" !== i && ("object" == typeof i && (i = JSON.stringify(i)), t += `${s}=${i}&`) } return t = t.substring(0, t.length - 1), t } msg(t = e, s = "", i = "", o = {}) { const r = e => { const { $open: t, $copy: s, $media: i, $mediaMime: o } = e; switch (typeof e) { case void 0: return e; case "string": switch (this.getEnv()) { case "Surge": case "Stash": default: return { url: e }; case "Loon": case "Shadowrocket": return e; case "Quantumult X": return { "open-url": e }; case "Node.js": return }case "object": switch (this.getEnv()) { case "Surge": case "Stash": case "Shadowrocket": default: { const r = {}; let a = e.openUrl || e.url || e["open-url"] || t; a && Object.assign(r, { action: "open-url", url: a }); let n = e["update-pasteboard"] || e.updatePasteboard || s; n && Object.assign(r, { action: "clipboard", text: n }); let h = e.mediaUrl || e["media-url"] || i; if (h) { let e, t; if (h.startsWith("http")); else if (h.startsWith("data:")) { const [s] = h.split(";"), [, i] = h.split(","); e = i, t = s.replace("data:", "") } else { e = h, t = (e => { const t = { JVBERi0: "application/pdf", R0lGODdh: "image/gif", R0lGODlh: "image/gif", iVBORw0KGgo: "image/png", "/9j/": "image/jpg" }; for (var s in t) if (0 === e.indexOf(s)) return t[s]; return null })(h) } Object.assign(r, { "media-url": h, "media-base64": e, "media-base64-mime": o ?? t }) } return Object.assign(r, { "auto-dismiss": e["auto-dismiss"], sound: e.sound }), r } case "Loon": { const s = {}; let o = e.openUrl || e.url || e["open-url"] || t; o && Object.assign(s, { openUrl: o }); let r = e.mediaUrl || e["media-url"] || i; return r && Object.assign(s, { mediaUrl: r }), console.log(JSON.stringify(s)), s } case "Quantumult X": { const o = {}; let r = e["open-url"] || e.url || e.openUrl || t; r && Object.assign(o, { "open-url": r }); let a = e.mediaUrl || e["media-url"] || i; a && Object.assign(o, { "media-url": a }); let n = e["update-pasteboard"] || e.updatePasteboard || s; return n && Object.assign(o, { "update-pasteboard": n }), console.log(JSON.stringify(o)), o } case "Node.js": return }default: return } }; if (!this.isMute) switch (this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": default: $notification.post(t, s, i, r(o)); break; case "Quantumult X": $notify(t, s, i, r(o)); break; case "Node.js": break }if (!this.isMuteLog) { let e = ["", "============================"]; e.push(t), s && e.push(s), i && e.push(i), console.log(e.join("\n")), this.logs = this.logs.concat(e) } } debug(...e) { this.logLevels[this.logLevel] <= this.logLevels.debug && (e.length > 0 && (this.logs = [...this.logs, ...e]), console.log(`${this.logLevelPrefixs.debug}${e.map((e => e ?? String(e))).join(this.logSeparator)}`)) } info(...e) { this.logLevels[this.logLevel] <= this.logLevels.info && (e.length > 0 && (this.logs = [...this.logs, ...e]), console.log(`${this.logLevelPrefixs.info}${e.map((e => e ?? String(e))).join(this.logSeparator)}`)) } warn(...e) { this.logLevels[this.logLevel] <= this.logLevels.warn && (e.length > 0 && (this.logs = [...this.logs, ...e]), console.log(`${this.logLevelPrefixs.warn}${e.map((e => e ?? String(e))).join(this.logSeparator)}`)) } error(...e) { this.logLevels[this.logLevel] <= this.logLevels.error && (e.length > 0 && (this.logs = [...this.logs, ...e]), console.log(`${this.logLevelPrefixs.error}${e.map((e => e ?? String(e))).join(this.logSeparator)}`)) } log(...e) { e.length > 0 && (this.logs = [...this.logs, ...e]), console.log(e.map((e => e ?? String(e))).join(this.logSeparator)) } logErr(e, t) { switch (this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": case "Quantumult X": default: this.log("", `❗️${this.name}, 错误!`, t, e); break; case "Node.js": this.log("", `❗️${this.name}, 错误!`, t, void 0 !== e.message ? e.message : e, e.stack); break } } wait(e) { return new Promise((t => setTimeout(t, e))) } done(e = {}) { const t = ((new Date).getTime() - this.startTime) / 1e3; switch (this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": case "Quantumult X": default: $done(e); break; case "Node.js": process.exit(1) } } }(e, t) }

================================================
FILE: Ctrip-DailyBonus/Ctrip.js
================================================
/********************************
携程旅行签到脚本

支持多账号,支持Node.js,支持Bark推送。
配置脚本后登陆"携程旅行"微信小程序或"携程网页版"(https://m.ctrip.com/)即可获取账号授权。多账号请勿"退出登陆"。

脚本作者:@NobyDa
更新时间:2024/05/09
平台兼容:Surge / QuantumultX / Loon / Stash / Node.js
模块依赖(Node.js):iconv-lite / got@11.8.3 / tough-cookie
环境变量(Node.js):CTRIP_AUTH / CTRIP_BARK_KEY

*********************************
Surge(iOS 5.9.0+/macOS 5.5.0+)模块:
https://raw.githubusercontent.com/NobyDa/Script/master/Surge/Module/CtripDailyBonus.sgmodule

*********************************
QuantumultX 任务仓库(Gallery)订阅:
https://raw.githubusercontent.com/NobyDa/Script/master/NobyDa_BoxJs.json

工具&分析->HTTP请求->右上角添加任务仓库->选择携程脚本添加定时任务和附加组件

*********************************/

const $ = new Env('CTRIP_DAILY_BONUS');
const barkKey = $.isNode() && process.env['CTRIP_BARK_KEY'] || ''; // bark key
const notifyMsg = [];
const auth = ''; // '{"account":{"user1":{"auth":"xxx"},"user2":{"auth":"xxx"}}}'

!(async () => {
    $.logLevel = $.getdata(`@${$.name}.Debug`) == 'true' && 'debug' || 'info';
    const user = JSON.parse(auth || $.getdata($.name) || ($.isNode() && process.env['CTRIP_AUTH']) || '{}');
    const userNum = Object.keys(user.account || {}).length;
    if (typeof $response !== 'undefined') {
        const body = JSON.parse($response.body || '{}');
        return GetAuth(body, user);
    }
    if (userNum) {
        const invalidUser = [];
        for (const i in user.account) {
            const text = [
                userNum > 1 && `[账号${notifyMsg.length + 1}(${i.slice(-4)})]`,
                await Checkin(user.account[i].auth),
                await Points(user.account[i].auth)
            ].filter((v) => v).join(', ');
            if (text.includes('登陆失效')) {
                invalidUser.push(i)
            }
            $.info(text);
            notifyMsg.push(text);
        }
        invalidUser.forEach((i) => delete user.account[i] && !$.isNode() && $.setjson(user, $.name));
    } else {
        notifyMsg.push(`未获取授权!`);
    }
})()
    .catch((err) => notifyMsg.push(`错误: ${err}`) && $.error(err))
    .finally(async () => {
        if (notifyMsg.length) {
            $.msg(`携程旅行`, ``, notifyMsg.join('\n'))
        }
        if (barkKey) {
            await BarkNotify($, barkKey, `携程旅行`, notifyMsg.join('\n'));
        }
        $.done({});
    });

function Checkin(key) {
    const opts = {
        url: 'https://m.ctrip.com/restapi/soa2/22769/signToday',
        headers: {
            "Content-Type": "application/json",
            "User-Agent": 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/21E219 MicroMessenger/8.0.49'
        },
        body: JSON.stringify({ head: { auth: key } })
    };
    $.debug(`Send checkin request:`, $.toStr(opts, 'error', null, 1));
    return $.http.post(opts)
        .then((resp) => {
            $.debug(`Receive checkin request response:`, $.toStr(resp))
            resp.body = JSON.parse(resp.body?.startsWith('{') && resp.body || '{}');
            if (resp.body.code == 0) {
                return '签到成功'
            } else if (resp.body.code == 400001) {
                return '已签过'
            } else if (resp.body.code == 404001) {
                return '登陆失效, 尝试移除账号...'
            } else {
                return `签到失败(${resp.body.message})`
            }
        })
        .catch((err) => {
            $.error(`Send checkin request error:`, err);
            return `签到错误`
        })
}

function Points(key) {
    const opts = {
        url: 'https://m.ctrip.com/restapi/soa2/15634/json/getPointsOrderUserInfo',
        headers: {
            "Content-Type": "application/json",
            "User-Agent": 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/21E219 MicroMessenger/8.0.49'
        },
        body: JSON.stringify({ needUserInfo: true, head: { auth: key } })
    };
    $.debug(`Send points request:`, $.toStr(opts, 'error', null, 1));
    return $.http.post(opts)
        .then((resp) => {
            $.debug(`Receive points request response:`, $.toStr(resp))
            resp.body = JSON.parse(resp.body?.startsWith('{') && resp.body || '{}');
            if (resp.body.isLogin) {
                return `总积分: ${resp.body.availableCredits || 0}`
            }
        })
        .catch((err) => {
            $.error(`Send points request error:`, err)
            return `总积分: 查询错误`
        })
}

function GetAuth(body, data) {
    if (body.ticket && body.uid) {
        if (!data.account || !data.account[body.uid]) {
            notifyMsg.push(`账号: ${body.uid}\n写入授权成功!`);
        } else {
            $.info(`账号: ${body.uid}\n更新授权成功!`);
        }
        data.account = { ...data.account, ...{ [body.uid]: { auth: body.ticket } } };
    } else {
        $.error(`写入授权失败, 授权值缺失.`)
    }
    return $.setjson(data, $.name);
}

//Bark APP notify
async function BarkNotify(c, k, t, b) { for (let i = 0; i < 3; i++) { c.log(`🔷Bark notify >> Start push (${i + 1})`); const s = await new Promise((n) => { c.post({ url: 'https://api.day.app/push', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ title: t, body: b, device_key: k, ext_params: { group: t } }) }, (e, r, d) => r && r.status == 200 ? n(1) : n(d || e)) }); if (s === 1) { c.log('✅Push success!'); break } else { c.log(`❌Push failed! >> ${s.message || s}`) } } };

// https://github.com/chavyleung/scripts/blob/master/Env.min.js
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, i) => { s.call(this, t, ((t, s, o) => { t ? i(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.logLevels = { debug: 0, info: 1, warn: 2, error: 3 }, this.logLevelPrefixs = { debug: "[DEBUG] ", info: "[INFO] ", warn: "[WARN] ", error: "[ERROR] " }, this.logLevel = "info", 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, ...s) { try { return JSON.stringify(t, ...s) } catch { return e } } getjson(t, e) { let s = e; if (this.getdata(t)) 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, i) => e(i))) })) } runScript(t, e) { return new Promise((s => { let i = this.getdata("@chavy_boxjs_userCfgs.httpapi"); i = i ? i.replace(/\n/g, "").trim() : i; let o = this.getdata("@chavy_boxjs_userCfgs.httpapi_timeout"); o = o ? 1 * o : 20, o = e && e.timeout ? e.timeout : o; const [r, a] = i.split("@"), n = { url: `http://${a}/v1/scripting/evaluate`, body: { script_text: t, mock_type: "cron", timeout: o }, headers: { "X-Key": r, Accept: "*/*" }, timeout: o }; this.post(n, ((t, e, i) => s(i))) })).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), i = !s && this.fs.existsSync(e); if (!s && !i) return {}; { const i = s ? t : e; try { return JSON.parse(this.fs.readFileSync(i)) } 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), i = !s && this.fs.existsSync(e), o = JSON.stringify(this.data); s ? this.fs.writeFileSync(t, o) : i ? this.fs.writeFileSync(e, o) : this.fs.writeFileSync(t, o) } } lodash_get(t, e, s) { const i = e.replace(/\[(\d+)\]/g, ".$1").split("."); let o = t; for (const t of i) if (o = Object(o)[t], void 0 === o) return s; return o } lodash_set(t, e, s) { return Object(t) !== t || (Array.isArray(e) || (e = e.toString().match(/[^.[\]]+/g) || []), e.slice(0, -1).reduce(((t, s, i) => Object(t[s]) === t[s] ? t[s] : t[s] = Math.abs(e[i + 1]) >> 0 == +e[i + 1] ? [] : {}), t)[e[e.length - 1]] = s), t } getdata(t) { let e = this.getval(t); if (/^@/.test(t)) { const [, s, i] = /^@(.*?)\.(.*?)$/.exec(t), o = s ? this.getval(s) : ""; if (o) try { const t = JSON.parse(o); e = t ? this.lodash_get(t, i, "") : e } catch (t) { e = "" } } return e } setdata(t, e) { let s = !1; if (/^@/.test(e)) { const [, i, o] = /^@(.*?)\.(.*?)$/.exec(e), r = this.getval(i), a = i ? "null" === r ? null : r || "{}" : "{}"; try { const e = JSON.parse(a); this.lodash_set(e, o, t), s = this.setval(JSON.stringify(e), i) } catch (e) { const r = {}; this.lodash_set(r, o, t), s = this.setval(JSON.stringify(r), i) } } 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 : {}, t && (t.headers = t.headers ? t.headers : {}, void 0 === t.headers.cookie && 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)), void 0 === t.followRedirect || t.followRedirect || ((this.isSurge() || this.isLoon()) && (t["auto-redirect"] = !1), this.isQuanX() && (t.opts ? t.opts.redirection = !1 : t.opts = { redirection: !1 })), 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, i) => { !t && s && (s.body = i, s.statusCode = s.status ? s.status : s.statusCode, s.status = s.statusCode), e(t, s, i) })); 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: i, headers: o, body: r, bodyBytes: a } = t; e(null, { status: s, statusCode: i, headers: o, body: r, bodyBytes: a }, r, a) }), (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: i, statusCode: o, headers: r, rawBody: a } = t, n = s.decode(a, this.encoding); e(null, { status: i, statusCode: o, headers: r, rawBody: a, body: n }, n) }), (t => { const { message: i, response: o } = t; e(i, o, o && s.decode(o.rawBody, this.encoding)) })); break } } 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"]), void 0 === t.followRedirect || t.followRedirect || ((this.isSurge() || this.isLoon()) && (t["auto-redirect"] = !1), this.isQuanX() && (t.opts ? t.opts.redirection = !1 : t.opts = { redirection: !1 })), 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, i) => { !t && s && (s.body = i, s.statusCode = s.status ? s.status : s.statusCode, s.status = s.statusCode), e(t, s, i) })); 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: i, headers: o, body: r, bodyBytes: a } = t; e(null, { status: s, statusCode: i, headers: o, body: r, bodyBytes: a }, r, a) }), (t => e(t && t.error || "UndefinedError"))); break; case "Node.js": let i = require("iconv-lite"); this.initGotEnv(t); const { url: o, ...r } = t; this.got[s](o, r).then((t => { const { statusCode: s, statusCode: o, headers: r, rawBody: a } = t, n = i.decode(a, this.encoding); e(null, { status: s, statusCode: o, headers: r, rawBody: a, body: n }, n) }), (t => { const { message: s, response: o } = t; e(s, o, o && i.decode(o.rawBody, this.encoding)) })); break } } time(t, e = null) { const s = e ? new Date(e) : new Date; let i = { "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 i) new RegExp("(" + e + ")").test(t) && (t = t.replace(RegExp.$1, 1 == RegExp.$1.length ? i[e] : ("00" + i[e]).substr(("" + i[e]).length))); return t } queryStr(t) { let e = ""; for (const s in t) { let i = t[s]; null != i && "" !== i && ("object" == typeof i && (i = JSON.stringify(i)), e += `${s}=${i}&`) } return e = e.substring(0, e.length - 1), e } msg(e = t, s = "", i = "", o = {}) { const r = t => { const { $open: e, $copy: s, $media: i, $mediaMime: o } = 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: { const r = {}; let a = t.openUrl || t.url || t["open-url"] || e; a && Object.assign(r, { action: "open-url", url: a }); let n = t["update-pasteboard"] || t.updatePasteboard || s; if (n && Object.assign(r, { action: "clipboard", text: n }), i) { let t, e, s; if (i.startsWith("http")) t = i; else if (i.startsWith("data:")) { const [t] = i.split(";"), [, o] = i.split(","); e = o, s = t.replace("data:", "") } else { e = i, s = (t => { const e = { JVBERi0: "application/pdf", R0lGODdh: "image/gif", R0lGODlh: "image/gif", iVBORw0KGgo: "image/png", "/9j/": "image/jpg" }; for (var s in e) if (0 === t.indexOf(s)) return e[s]; return null })(i) } Object.assign(r, { "media-url": t, "media-base64": e, "media-base64-mime": o ?? s }) } return Object.assign(r, { "auto-dismiss": t["auto-dismiss"], sound: t.sound }), r } case "Loon": { const s = {}; let o = t.openUrl || t.url || t["open-url"] || e; o && Object.assign(s, { openUrl: o }); let r = t.mediaUrl || t["media-url"]; return i?.startsWith("http") && (r = i), r && Object.assign(s, { mediaUrl: r }), console.log(JSON.stringify(s)), s } case "Quantumult X": { const o = {}; let r = t["open-url"] || t.url || t.openUrl || e; r && Object.assign(o, { "open-url": r }); let a = t["media-url"] || t.mediaUrl; i?.startsWith("http") && (a = i), a && Object.assign(o, { "media-url": a }); let n = t["update-pasteboard"] || t.updatePasteboard || s; return n && Object.assign(o, { "update-pasteboard": n }), console.log(JSON.stringify(o)), o } 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, i, r(o)); break; case "Quantumult X": $notify(e, s, i, r(o)); break; case "Node.js": break }if (!this.isMuteLog) { let t = ["", "==============📣系统通知📣=============="]; t.push(e), s && t.push(s), i && t.push(i), console.log(t.join("\n")), this.logs = this.logs.concat(t) } } debug(...t) { this.logLevels[this.logLevel] <= this.logLevels.debug && (t.length > 0 && (this.logs = [...this.logs, ...t]), console.log(`${this.logLevelPrefixs.debug}${t.map((t => t ?? String(t))).join(this.logSeparator)}`)) } info(...t) { this.logLevels[this.logLevel] <= this.logLevels.info && (t.length > 0 && (this.logs = [...this.logs, ...t]), console.log(`${this.logLevelPrefixs.info}${t.map((t => t ?? String(t))).join(this.logSeparator)}`)) } warn(...t) { this.logLevels[this.logLevel] <= this.logLevels.warn && (t.length > 0 && (this.logs = [...this.logs, ...t]), console.log(`${this.logLevelPrefixs.warn}${t.map((t => t ?? String(t))).join(this.logSeparator)}`)) } error(...t) { this.logLevels[this.logLevel] <= this.logLevels.error && (t.length > 0 && (this.logs = [...this.logs, ...t]), console.log(`${this.logLevelPrefixs.error}${t.map((t => t ?? String(t))).join(this.logSeparator)}`)) } log(...t) { t.length > 0 && (this.logs = [...this.logs, ...t]), console.log(t.map((t => t ?? String(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}, 错误!`, e, t); break; case "Node.js": this.log("", `❗️${this.name}, 错误!`, e, void 0 !== t.message ? t.message : t, t.stack); break } } wait(t) { return new Promise((e => setTimeout(e, t))) } done(t = {}) { const e = ((new Date).getTime() - this.startTime) / 1e3; switch (this.log("", `🔔${this.name}, 结束! 🕛 ${e} 秒`), 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) }

================================================
FILE: Debug/Real-time-debug.js
================================================
/*
 * LAN script real-time debug
 *
 * PC: Use "Live Server" plugin in VSCode to create a LAN backend
 * APP: After backend address is modified in script, use this script as script path
 */

!async function() { 
	const _$ = new nobyda();
	const _r = await new Promise(e => {
		_$.get({
			url: 'http://192.168.1.66:5500/debug.js' // LAN backend address
		}, (t, c, o) => {
			if (c && c.status == 200 && o) {
				_$.write(o, 'Real-time-debug');
				e(o);
			}
		});
		setTimeout(e, 100);
	});
	if (_r) {
		console.log("🌐 Run local network script...");
		eval(_r);
	} else {
		console.log("⚠️ Run cache script...");
		eval(_$.read('Real-time-debug'))
	}

	function nobyda() {
		const isSurge = typeof $httpClient != "undefined";
		const isQuanX = typeof $task != "undefined";
		const adapterStatus = (response) => {
			if (response) {
				if (response.status) {
					response["statusCode"] = response.status
				} else if (response.statusCode) {
					response["status"] = response.statusCode
				}
			}
			return response
		};
		this.write = (value, key) => {
			if (isQuanX) return $prefs.setValueForKey(value, key);
			if (isSurge) return $persistentStore.write(value, key);
		};
		this.read = (key) => {
			if (isQuanX) return $prefs.valueForKey(key);
			if (isSurge) return $persistentStore.read(key);
		};
		this.get = (options, callback) => {
			if (isQuanX) {
				$task.fetch(options).then(response => {
					callback(null, adapterStatus(response), response.body)
				}, reason => callback(reason.error, null, null))
			}
			if (isSurge) {
				$httpClient.get(options, (error, response, body) => {
					callback(error, adapterStatus(response), body)
				})
			}
		};
		this.done = (value = {}) => $done(value)
	}
}();

================================================
FILE: Disney/DisneyRating.js
================================================
/*********************************
Disney+ 显示IMDb评分 / 烂番茄评分 / 豆瓣评分

脚本作者: @NobyDa 
脚本兼容: Surge、QuantumultX、Loon
系统兼容: iOS14+
更新时间: 2024/05/04
脚本参考: https://github.com/yichahucha/surge/blob/master/nf_rating.js

Surge模块: 
https://raw.githubusercontent.com/NobyDa/Script/master/Surge/Module/DisneyRating.sgmodule

QuantumultX重写引用: 
https://raw.githubusercontent.com/NobyDa/Script/master/QuantumultX/DisneyRating.snippet

*********************************/

const $tool = new Tool();
const consoleLog = false;
let obj = $response.body;
let IMDbApikeys = IMDbApikeyList();
let IMDbApikey = $tool.read("ImdbApikeyCacheKey");
if (!IMDbApikey) {
    updateIMDbApikey();
}

const requestRatings = async () => {
    if (consoleLog) console.log("Disney Original Body:\n" + obj);
    obj = JSON.parse(obj);
    const sliced = obj?.data?.page?.actions?.[0]?.internalTitle?.split(' - ');
    let title = sliced?.[0] || obj?.data?.page?.visuals?.title;
    if (title) {
        title = title.replace(/.+?:\s|\s?\(.+?\)\s?/g,'');
    } else {
        throw 'NO TITLE';
    }
    const year = obj?.data?.page?.visuals?.metastringParts?.releaseYearRange?.startYear;
    const type = (sliced?.[1]?.startsWith('s') && 'series') || (sliced?.[1] == 'movie' && 'movie');
    const IMDb = await requestIMDbRating(title, year, type);
    const Douban = await requestDoubanRating(IMDb.id);
    const IMDbrating = IMDb.msg.rating;
    const tomatoes = IMDb.msg.tomatoes;
    const country = IMDb.msg.country;
    // const awards = IMDb.msg.awards;
    const doubanRating = Douban.rating;
    // const message = `${awards.length > 0 ? awards + "\n" : ""}${country}\n${IMDbrating}\n${doubanRating}${tomatoes.length > 0 ? "\n" + tomatoes + "\n" : "\n"}`;
    return { country, tomatoes, IMDbrating, doubanRating };
}

requestRatings()
    .then(data => {
        if (obj?.data?.page?.visuals) {
            obj.data.page.visuals.promoLabel = {
                promoLabelType: "generic",
                header: `${data.country}${data.tomatoes ? `\n${data.tomatoes}` : ``}`,
                subheader: `${data.IMDbrating}${data.doubanRating ? `\n${data.doubanRating}` : ``}`
            }
        }
        if (consoleLog) console.log("Disney Modified Body:\n" + JSON.stringify(obj));
    })
    .catch(error => console.log(`ERROR: ${error}`))
    .finally(() => $done({ body: typeof obj == 'object' ? JSON.stringify(obj) : obj }));

function requestDoubanRating(imdbId) {
    return new Promise(function (resolve, reject) {
        const url = `https://www.douban.com/search?cat=1002&q=${imdbId}`;
        if (consoleLog) console.log("Disney Douban Rating URL:\n" + url);
        $tool.get(url, function (error, response, data) {
            if (!error) {
                if (consoleLog) console.log("Disney Douban Rating Data:\n" + data);
                if (response.status == 200) {
                    const rating = get_douban_rating_message(data);
                    resolve({ rating });
                } else {
                    resolve({});
                }
            } else {
                console.log("Disney Douban Rating Error: " + error);
                resolve({});
            }
        });
    });
}

function requestIMDbRating(title, year, type) {
    return new Promise(function (resolve, reject) {
        let url = "https://www.omdbapi.com/?t=" + encodeURIComponent(title) + "&apikey=" + IMDbApikey;
        if (year) url += "&y=" + year;
        if (type) url += "&type=" + type;
        if (consoleLog) console.log("Disney IMDb Rating URL:\n" + url);
        $tool.get(url, function (error, response, data) {
            if (!error) {
                if (consoleLog) console.log("Disney IMDb Rating Data:\n" + data);
                if (response.status == 200) {
                    const obj = JSON.parse(data);
                    if (obj.Response == "True") {
                        const id = obj.imdbID;
                        const msg = get_IMDb_message(obj);
                        resolve({ id, msg });
                    } else {
                        reject(`Title [${title}] IMDb data not found`);
                    }
                } else if (response.status == 401) {
                    if (IMDbApikeys.length > 1) {
                        updateIMDbApikey();
                        requestIMDbRating(title, year, type);
                    } else {
                        reject(`IMDb Key invalid`);
                    }
                } else {
                    reject(`Unknown status: ${response.status}, Data: ${data}`);
                }
            } else {
                reject(`IMDB data response failed: ${error}`);
            }
        });
    });
}

function updateIMDbApikey() {
    if (IMDbApikey) IMDbApikeys.splice(IMDbApikeys.indexOf(IMDbApikey), 1);
    const index = Math.floor(Math.random() * IMDbApikeys.length);
    IMDbApikey = IMDbApikeys[index];
    $tool.write(IMDbApikey, "ImdbApikeyCacheKey");
}

function get_IMDb_message(data) {
    let rating_message = "IMDb:  ⭐️ N/A";
    let tomatoes_message = "";
    let country_message = "";
    let ratings = data.Ratings;
    let awards_message = "";
    if (data.Awards && data.Awards != "N/A") {
        awards_message = "🏆 " + data.Awards;
    }
    if (ratings.length > 0) {
        const imdb_source = ratings[0]["Source"];
        if (imdb_source == "Internet Movie Database") {
            const imdb_votes = data.imdbVotes;
            const imdb_rating = ratings[0]["Value"];
            rating_message = "IMDb:  ⭐️ " + imdb_rating + "   " + imdb_votes;
            if (data.Type == "movie") {
                if (ratings.length > 1) {
                    const source = ratings[1]["Source"];
                    if (source == "Rotten Tomatoes") {
                        const tomatoes = ratings[1]["Value"];
                        tomatoes_message = "Tomatoes:  🍅 " + tomatoes;
                    }
                }
            }
        }
    }
    country_message = get_country_message(data.Country);
    return { rating: rating_message, tomatoes: tomatoes_message, country: country_message, awards: awards_message }
}

function get_douban_rating_message(data) {
    const s = data.replace(/\n| |&#\d{2}/g, '')
        .match(/\[(\u7535\u5f71|\u7535\u89c6\u5267)\].+?subject-cast\">.+?<\/span>/g);
    const average = s ? s[0].split(/">(\d\.\d)</)[1] || '' : '';
    const numRaters = s ? s[0].split(/(\d+)\u4eba\u8bc4\u4ef7/)[1] || '' : '';
    const rating_message = `Douban:  ⭐️ ${average ? average + "/10" : "N/A"}   ${!numRaters ? "" : parseFloat(numRaters).toLocaleString()}`;
    return average && rating_message;
}

function get_country_message(data) {
    const country = data;
    const countrys = country.split(", ");
    let emoji_country = "";
    countrys.forEach(item => {
        emoji_country += countryEmoji(item) + " " + item + ", ";
    });
    return emoji_country.slice(0, -2);
}

// function errorTip() {
//     return { noData: "⭐️ N/A", error: "❌ N/A" }
// }

function IMDbApikeyList() {
    const apikeys = [
        "f75e0253", "d8bb2d6b",
        "ae64ce8d", "7218d678",
        "b2650e38", "8c4a29ab",
        "9bd135c2", "953dbabe",
        "1a66ef12", "3e7ea721",
        "457fc4ff", "d2131426",
        "9cc1a9b7", "e53c2c11",
        "f6dfce0e", "b9db622f",
        "e6bde2b9", "d324dbab",
        "d7904fa3", "aeaf88b9",
        "4e89234e",];
    return apikeys;
}

function countryEmoji(name) { const emojiMap = { "Chequered": "🏁", "Triangular": "🚩", "Crossed": "🎌", "Black": "🏴", "White": "🏳", "Rainbow": "🏳️‍🌈", "Pirate": "🏴‍☠️", "Ascension Island": "🇦🇨", "Andorra": "🇦🇩", "United Arab Emirates": "🇦🇪", "Afghanistan": "🇦🇫", "Antigua & Barbuda": "🇦🇬", "Anguilla": "🇦🇮", "Albania": "🇦🇱", "Armenia": "🇦🇲", "Angola": "🇦🇴", "Antarctica": "🇦🇶", "Argentina": "🇦🇷", "American Samoa": "🇦🇸", "Austria": "🇦🇹", "Australia": "🇦🇺", "Aruba": "🇦🇼", "Åland Islands": "🇦🇽", "Azerbaijan": "🇦🇿", "Bosnia & Herzegovina": "🇧🇦", "Barbados": "🇧🇧", "Bangladesh": "🇧🇩", "Belgium": "🇧🇪", "Burkina Faso": "🇧🇫", "Bulgaria": "🇧🇬", "Bahrain": "🇧🇭", "Burundi": "🇧🇮", "Benin": "🇧🇯", "St. Barthélemy": "🇧🇱", "Bermuda": "🇧🇲", "Brunei": "🇧🇳", "Bolivia": "🇧🇴", "Caribbean Netherlands": "🇧🇶", "Brazil": "🇧🇷", "Bahamas": "🇧🇸", "Bhutan": "🇧🇹", "Bouvet Island": "🇧🇻", "Botswana": "🇧🇼", "Belarus": "🇧🇾", "Belize": "🇧🇿", "Canada": "🇨🇦", "Cocos (Keeling) Islands": "🇨🇨", "Congo - Kinshasa": "🇨🇩", "Congo": "🇨🇩", "Central African Republic": "🇨🇫", "Congo - Brazzaville": "🇨🇬", "Switzerland": "🇨🇭", "Côte d’Ivoire": "🇨🇮", "Cook Islands": "🇨🇰", "Chile": "🇨🇱", "Cameroon": "🇨🇲", "China": "🇨🇳", "Colombia": "🇨🇴", "Clipperton Island": "🇨🇵", "Costa Rica": "🇨🇷", "Cuba": "🇨🇺", "Cape Verde": "🇨🇻", "Curaçao": "🇨🇼", "Christmas Island": "🇨🇽", "Cyprus": "🇨🇾", "Czechia": "🇨🇿", "Czech Republic": "🇨🇿", "Germany": "🇩🇪", "Diego Garcia": "🇩🇬", "Djibouti": "🇩🇯", "Denmark": "🇩🇰", "Dominica": "🇩🇲", "Dominican Republic": "🇩🇴", "Algeria": "🇩🇿", "Ceuta & Melilla": "🇪🇦", "Ecuador": "🇪🇨", "Estonia": "🇪🇪", "Egypt": "🇪🇬", "Western Sahara": "🇪🇭", "Eritrea": "🇪🇷", "Spain": "🇪🇸", "Ethiopia": "🇪🇹", "European Union": "🇪🇺", "Finland": "🇫🇮", "Fiji": "🇫🇯", "Falkland Islands": "🇫🇰", "Micronesia": "🇫🇲", "Faroe Islands": "🇫🇴", "France": "🇫🇷", "Gabon": "🇬🇦", "United Kingdom": "🇬🇧", "UK": "🇬🇧", "Grenada": "🇬🇩", "Georgia": "🇬🇪", "French Guiana": "🇬🇫", "Guernsey": "🇬🇬", "Ghana": "🇬🇭", "Gibraltar": "🇬🇮", "Greenland": "🇬🇱", "Gambia": "🇬🇲", "Guinea": "🇬🇳", "Guadeloupe": "🇬🇵", "Equatorial Guinea": "🇬🇶", "Greece": "🇬🇷", "South Georgia & South Sandwich Is lands": "🇬🇸", "Guatemala": "🇬🇹", "Guam": "🇬🇺", "Guinea-Bissau": "🇬🇼", "Guyana": "🇬🇾", "Hong Kong SAR China": "🇭🇰", "Hong Kong": "🇭🇰", "Heard & McDonald Islands": "🇭🇲", "Honduras": "🇭🇳", "Croatia": "🇭🇷", "Haiti": "🇭🇹", "Hungary": "🇭🇺", "Canary Islands": "🇮🇨", "Indonesia": "🇮🇩", "Ireland": "🇮🇪", "Israel": "🇮🇱", "Isle of Man": "🇮🇲", "India": "🇮🇳", "British Indian Ocean Territory": "🇮🇴", "Iraq": "🇮🇶", "Iran": "🇮🇷", "Iceland": "🇮🇸", "Italy": "🇮🇹", "Jersey": "🇯🇪", "Jamaica": "🇯🇲", "Jordan": "🇯🇴", "Japan": "🇯🇵", "Kenya": "🇰🇪", "Kyrgyzstan": "🇰🇬", "Cambodia": "🇰🇭", "Kiribati": "🇰🇮", "Comoros": "🇰🇲", "St. Kitts & Nevis": "🇰🇳", "North Korea": "🇰🇵", "South Korea": "🇰🇷", "Kuwait": "🇰🇼", "Cayman Islands": "🇰🇾", "Kazakhstan": "🇰🇿", "Laos": "🇱🇦", "Lebanon": "🇱🇧", "St. Lucia": "🇱🇨", "Liechtenstein": "🇱🇮", "Sri Lanka": "🇱🇰", "Liberia": "🇱🇷", "Lesotho": "🇱🇸", "Lithuania": "🇱🇹", "Luxembourg": "🇱🇺", "Latvia": "🇱🇻", "Libya": "🇱🇾", "Morocco": "🇲🇦", "Monaco": "🇲🇨", "Moldova": "🇲🇩", "Montenegro": "🇲🇪", "St. Martin": "🇲🇫", "Madagascar": "🇲🇬", "Marshall Islands": "🇲🇭", "North Macedonia": "🇲🇰", "Mali": "🇲🇱", "Myanmar (Burma)": "🇲🇲", "Mongolia": "🇲🇳", "Macau Sar China": "🇲🇴", "Northern Mariana Islands": "🇲🇵", "Martinique": "🇲🇶", "Mauritania": "🇲🇷", "Montserrat": "🇲🇸", "Malta": "🇲🇹", "Mauritius": "🇲🇺", "Maldives": "🇲🇻", "Malawi": "🇲🇼", "Mexico": "🇲🇽", "Malaysia": "🇲🇾", "Mozambique": "🇲🇿", "Namibia": "🇳🇦", "New Caledonia": "🇳🇨", "Niger": "🇳🇪", "Norfolk Island": "🇳🇫", "Nigeria": "🇳🇬", "Nicaragua": "🇳🇮", "Netherlands": "🇳🇱", "Norway": "🇳🇴", "Nepal": "🇳🇵", "Nauru": "🇳🇷", "Niue": "🇳🇺", "New Zealand": "🇳🇿", "Oman": "🇴🇲", "Panama": "🇵🇦", "Peru": "🇵🇪", "French Polynesia": "🇵🇫", "Papua New Guinea": "🇵🇬", "Philippines": "🇵🇭", "Pakistan": "🇵🇰", "Poland": "🇵🇱", "St. Pierre & Miquelon": "🇵🇲", "Pitcairn Islands": "🇵🇳", "Puerto Rico": "🇵🇷", "Palestinian Territories": "🇵🇸", "Portugal": "🇵🇹", "Palau": "🇵🇼", "Paraguay": "🇵🇾", "Qatar": "🇶🇦", "Réunion": "🇷🇪", "Romania": "🇷🇴", "Serbia": "🇷🇸", "Russia": "🇷🇺", "Rwanda": "🇷🇼", "Saudi Arabia": "🇸🇦", "Solomon Islands": "🇸🇧", "Seychelles": "🇸🇨", "Sudan": "🇸🇩", "Sweden": "🇸🇪", "Singapore": "🇸🇬", "St. Helena": "🇸🇭", "Slovenia": "🇸🇮", "Svalbard & Jan Mayen": "🇸🇯", "Slovakia": "🇸🇰", "Sierra Leone": "🇸🇱", "San Marino": "🇸🇲", "Senegal": "🇸🇳", "Somalia": "🇸🇴", "Suriname": "🇸🇷", "South Sudan": "🇸🇸", "São Tomé & Príncipe": "🇸🇹", "El Salvador": "🇸🇻", "Sint Maarten": "🇸🇽", "Syria": "🇸🇾", "Swaziland": "🇸🇿", "Tristan Da Cunha": "🇹🇦", "Turks & Caicos Islands": "🇹🇨", "Chad": "🇹🇩", "French Southern Territories": "🇹🇫", "Togo": "🇹🇬", "Thailand": "🇹🇭", "Tajikistan": "🇹🇯", "Tokelau": "🇹🇰", "Timor-Leste": "🇹🇱", "Turkmenistan": "🇹🇲", "Tunisia": "🇹🇳", "Tonga": "🇹🇴", "Turkey": "🇹🇷", "Trinidad & Tobago": "🇹🇹", "Tuvalu": "🇹🇻", "Taiwan": "🇨🇳", "Tanzania": "🇹🇿", "Ukraine": "🇺🇦", "Uganda": "🇺🇬", "U.S. Outlying Islands": "🇺🇲", "United Nations": "🇺🇳", "United States": "🇺🇸", "USA": "🇺🇸", "Uruguay": "🇺🇾", "Uzbekistan": "🇺🇿", "Vatican City": "🇻🇦", "St. Vincent & Grenadines": "🇻🇨", "Venezuela": "🇻🇪", "British Virgin Islands": "🇻🇬", "U.S. Virgin Islands": "🇻🇮", "Vietnam": "🇻🇳", "Vanuatu": "🇻🇺", "Wallis & Futuna": "🇼🇫", "Samoa": "🇼🇸", "Kosovo": "🇽🇰", "Yemen": "🇾🇪", "Mayotte": "🇾🇹", "South Africa": "🇿🇦", "Zambia": "🇿🇲", "Zimbabwe": "🇿🇼", "England": "🏴󠁧󠁢󠁥󠁮󠁧󠁿", "Scotland": "🏴󠁧󠁢󠁳󠁣󠁴󠁿", "Wales": "🏴󠁧󠁢󠁷󠁬󠁳󠁿", }; return emojiMap[name] ? emojiMap[name] : emojiMap["Chequered"] }

function Tool() {
    _node = (() => {
        if (typeof require == "function") {
            const request = require('request')
            return ({ request })
        } else {
            return (null)
        }
    })()
    _isSurge = typeof $httpClient != "undefined"
    _isQuanX = typeof $task != "undefined"
    this.isSurge = _isSurge
    this.isQuanX = _isQuanX
    this.isResponse = typeof $response != "undefined"
    this.notify = (title, subtitle, message) => {
        if (_isQuanX) $notify(title, subtitle, message)
        if (_isSurge) $notification.post(title, subtitle, message)
        if (_node) console.log(JSON.stringify({ title, subtitle, message }));
    }
    this.write = (value, key) => {
        if (_isQuanX) return $prefs.setValueForKey(value, key)
        if (_isSurge) return $persistentStore.write(value, key)
    }
    this.read = (key) => {
        if (_isQuanX) return $prefs.valueForKey(key)
        if (_isSurge) return $persistentStore.read(key)
    }
    this.get = (options, callback) => {
        if (_isQuanX) {
            if (typeof options == "string") options = { url: options }
            options["method"] = "GET"
            $task.fetch(options).then(response => { callback(null, _status(response), response.body) }, reason => callback(reason.error, null, null))
        }
        if (_isSurge) $httpClient.get(options, (error, response, body) => { callback(error, _status(response), body) })
        if (_node) _node.request(options, (error, response, body) => { callback(error, _status(response), body) })
    }
    this.post = (options, callback) => {
        if (_isQuanX) {
            if (typeof options == "string") options = { url: options }
            options["method"] = "POST"
            $task.fetch(options).then(response => { callback(null, _status(response), response.body) }, reason => callback(reason.error, null, null))
        }
        if (_isSurge) $httpClient.post(options, (error, response, body) => { callback(error, _status(response), body) })
        if (_node) _node.request.post(options, (error, response, body) => { callback(error, _status(response), body) })
    }
    _status = (response) => {
        if (response) {
            if (response.status) {
                response["statusCode"] = response.status
            } else if (response.statusCode) {
                response["status"] = response.statusCode
            }
        }
        return response
    }
}


================================================
FILE: IPA-Installer/IPA-Installer-JSBox.js
================================================
/*
 * IPA-installer JSBox script. This script is not available stand alone, checkout the demo from TG channel @NobyDa
 * 
 * Modified from https://github.com/axelburks/JSBox/blob/master/IPA%20Installer.js by @NobyDa
 */

var port_number = 8070
var plist_url = `itms-services://?action=download-manifest&url=https://nobyda.app/install%3Fclient%3Djsbox%26url%3Dhttp%253A%252F%252F127.0.0.1%253A${port_number}%252Fdownload%253Fpath%253D%25252Fapp.ipa`

$app.strings = {
  "en": {
    "starterror": "Not support running in this way",
    "ftypeerror": " is not ipa file",
    "installtitle": "Installing...",
    "installmsg": "\n\nYou can check on Homescreen.\nPlease tap \"Done\" button after finished",
    "inerrtitle": "IPA file import error",
    "inerrmsg": "Please rerun the script"
  },
  "zh-Hans": {
    "starterror": "不支持此方式运行!",
    "ftypeerror": " 非 ipa 文件!",
    "installtitle": "正在安装…",
    "installmsg": "\n\n可前往桌面查看安装进度\n完成后请点击\"Done\"按钮",
    "inerrtitle": "IPA文件导入失败",
    "inerrmsg": "请重新运行此脚本"
  }
}

// 从应用内启动
if ($app.env == $env.app) {
  $drive.open({
    handler: function(data) {
      fileCheck(data)
    }
  })
}
// 从 Action Entension 启动
else if ($app.env == $env.action) {
  fileCheck($context.data)
}

else {
  $ui.error($l10n("starterror"))
  delayClose(2)
}


function startServer(port) {
  $http.startServer({
    port: port,
    path: "",
    handler: function(result) {
      console.info(result.url)
    }
  })
}

function fileCheck(data) {
  if (data && data.fileName) {
    var fileName = data.fileName;
    if (fileName.indexOf(".ipa") == -1) {
      $ui.error(fileName + $l10n("ftypeerror"))
      delayClose(2)
    } else {
      install(fileName, data);
    }
  }
}

function install(fileName, file) {
  var result = $file.write({
    data: file,
    path: "app.ipa"
  })
  if (result) {
    startServer(port_number)
    $location.startUpdates({
      handler: function(resp) {
        console.info(resp.lat + " " + resp.lng + " " + resp.alt)
      }
    })
    var preResult = $app.openURL(plist_url);
    if (preResult) {
      $ui.alert({
        title: $l10n("installtitle"),
        message: "\n" + fileName + $l10n("installmsg"),
        actions: [{
          title: "Cancel",
          style: "Cancel",
          handler: function() {
            $http.stopServer()
            $file.delete("app.ipa")
            delayClose(0.2)
          }
        },
        {
          title: "Done",
          handler: function() {
            $http.stopServer()
            $file.delete("app.ipa")
            delayClose(0.2)
          }
        }]
      })
    } else {
      $ui.alert({
        title: "Open itms-services scheme failed",
        message: "Please rerun the script or restart device",
        actions: [
        {
          title: "OK",
          handler: function() {
            delayClose(0.2)
          }
        }]
      })
    }
  } else {
    $ui.alert({
      title: $l10n("inerrtitle"),
      message: $l10n("inerrmsg"),
      actions: [{
        title: "OK",
        style: "Cancel",
        handler: function() {
          delayClose(0.2)
        }
      }]
    })
  }
}

function delayClose(time) {
    $location.stopUpdates()
    $thread.main({
      delay: time,
      handler: function() {
        if ($app.env == $env.action || $app.env == $env.safari) {
          $context.close()
        }
        $app.close()
      }
    })
}


================================================
FILE: IPA-Installer/IPA-Installer-Pythonista.py
================================================
# IPA-installer pythonista script. This script is not available stand alone, checkout the demo from TG channel @NobyDa
#
# Modified from https://github.com/axelburks/Pythonista/blob/master/IPA%20Installer.py by @NobyDa

import os, appex, console, shutil, http.server, webbrowser, time
from os import path
from threading import Thread

port_number = 8090
plist_url = f'itms-services://?action=download-manifest&url=https://nobyda.app/install%3Fclient%3Dpythonista%26url%3Dhttp%253A%252F%252F127.0.0.1%253A{port_number}%252Fipa%252Fapp.ipa'
save_dir = path.expanduser('./ipa')
if not path.exists(save_dir):
	os.makedirs(save_dir)
	
httpd = None
def startServer(port):
    Handler = http.server.SimpleHTTPRequestHandler
    
    global httpd
    httpd = http.server.HTTPServer(("", port), Handler)
    
    print("Start server at port", port)
    httpd.serve_forever()

def start(port):
    thread = Thread(target=startServer, args=[port])
    thread.start()
    
    startTime = int(time.time())
    while not httpd:
        if int(time.time()) > startTime + 60:
            print("Time out")
            break
    return httpd

def stop():
    if httpd:
        httpd.shutdown()

def main():
	if appex.is_running_extension():
		get_path = appex.get_file_path()
		file_name = path.basename(get_path)
		file_ext = path.splitext(file_name)[-1]
		if file_ext == '.ipa':
			dstpath = path.join(save_dir, 'app.ipa')
			try:
				shutil.copy(get_path, dstpath)
				
			except Exception as eer:
				print(eer)
				console.hud_alert('导入失败!','error',1)
			start(port_number)
			if httpd:
				webbrowser.open(plist_url)
			try:
				finish = console.alert(file_name, '\n正在安装...请返回桌面查看进度...\n\n安装完成后请返回点击已完成','已完成', hide_cancel_button=False)
				if finish == 1:
					stop()
					shutil.rmtree('./ipa')
					print("Server stopped")
			except:
				stop()
				shutil.rmtree('./ipa')
				print("Cancelled")
				appex.finish()
		else:
			console.hud_alert('非 ipa 文件无法导入安装', 'error', 2)
		appex.finish()
	else:
		console.hud_alert('请在分享扩展中打开本脚本','error',2)

if __name__ == '__main__':
	main()

================================================
FILE: IPA-Installer/IPA-Installer.js
================================================
/*
 * iOS IPA应用辅助安装脚本.
 * 
 * 兼容: QuantumultX、Surge5,Loon、Shadowrocket、Stash
 * 作者: @NobyDa
 * 
 * 快捷指令 + Shu配合安装:
 * 导入IPA文件至Shu -> Shu长按IPA文件 -> 导出文件 -> WiFi传输 -> 本机 -> 系统共享 -> 分享至IPA-Installer快捷指令
 * 
 * 快捷指令 + JSBox/Pythonista配合安装:
 * IPA文件长按分享至IPA-Installer快捷指令(iOS14跳过),完成后再分享至Jsbox/pythonista分享扩展. 
 * 
 * 
 * QuanX重写: https://raw.githubusercontent.com/NobyDa/Script/master/QuantumultX/IPA-Installer.snippet
 * 
 * Surge模块: https://raw.githubusercontent.com/NobyDa/Script/master/Surge/Module/IPA_install.sgmodule
 * 
 * loon插件: https://raw.githubusercontent.com/NobyDa/Script/master/Loon/Loon_IPA_Installer.plugin
 * 
 * Stash覆写: https://raw.githubusercontent.com/NobyDa/Script/master/Stash/IPA-Installer.stoverride
 * 
 * 快捷指令(iOS15+): https://www.icloud.com/shortcuts/4a121aa54cae4619a952baa29e044e30
 * 
 * 快捷指令(iOS14): https://www.icloud.com/shortcuts/179dfcd7505e44f89207086d2b1a32ea
 * 
 * JSBox脚本: https://xteko.com/redir?url=https%3A%2F%2Fraw.githubusercontent.com%2FNobyDa%2FScript%2Fmaster%2FIPA-Installer%2FIPA-Installer-JSBox.js&name=IPA%20Installer%20%28NobyDa%29
 * 
 * Pythonista脚本: https://github.com/NobyDa/Script/blob/master/IPA-Installer/IPA-Installer-Pythonista.py
 */

const $ = new compatible_tool();

(async function () {
	const args = urlArgs($request.url);
	const plist = `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>items</key>
	<array>
		<dict>
			<key>assets</key>
			<array>
				<dict>
					<key>kind</key>
					<string>software-package</string>
					<key>url</key>
					<string>https://nobyda.app/download?url=${encodeURIComponent(args.url)}</string>
				</dict>
			</array>
			<key>metadata</key>
			<dict>
				<key>bundle-identifier</key>
				<string>${args.bundleId || $.read("nobyda_ipa_bundle_id") || "*"}</string>
				<key>bundle-version</key>
				<string>1.0</string>
				<key>kind</key>
				<string>software</string>
				<key>title</key>
				<string>IPA</string>
			</dict>
		</dict>
	</array>
</dict>
</plist>`;
	if ($request.url.includes("/install?")) {
		if (args.bundleId) {
			$.write(args.bundleId, "nobyda_ipa_bundle_id");
		};
		$.resp = { response: { status: 200, body: args.client && plist || "{}" } };
	} else {
		if ($request.method == "GET") {
			const size = await ipaSize(args.url);
			$.notify(`IPA Installer`, ``, size && `Installing IPA, Size: ${size} MB` || `HTTP local server read failed!`);
		}
		$.resp = { response: { status: 307, headers: { Location: args.url }, body: "{}" } };
	}
})()
	.catch((e) => $.notify(`IPA Installer`, ``, `ERROR: ${e.message || e}\nPATH: ${e.stack}`))
	.finally(() => $.done($.resp))


function ipaSize(url) {
	return new Promise((r, e) => {
		$.http({ method: "head", url: url, policy: "DIRECT", }, (e, h, d) => {
			r(h && h.status == 200 && `${((h.headers["Content-Length"] || 0) / 1000 / 1000).toFixed(2)}`)
		});
		setTimeout(() => r(), 1000)
	});
}

function urlArgs(str) {
	return Object.fromEntries(
		(str.startsWith("http") && str.split("?")[1] || str).split("&")
			.map((item) => item.split("="))
			.map(([k, v]) => [k, decodeURIComponent(v)])
	);
}

function compatible_tool() {
	const isSurge = typeof $httpClient != "undefined";
	const isQuanX = typeof $task != "undefined";
	const isStash = typeof $environment == "object" && $environment["stash-version"];
	const adapterStatus = (response) => {
		if (response && response.statusCode) {
			response.status = response.statusCode;
		}
		return response
	};
	this.read = (key) => {
		if (isQuanX) return $prefs.valueForKey(key);
		if (isSurge) return $persistentStore.read(key);
	};
	this.write = (value, key) => {
		if (isQuanX) return $prefs.setValueForKey(value, key);
		if (isSurge) return $persistentStore.write(value, key);
	};
	this.notify = (title, subtitle, message) => {
		if (isQuanX) $notify(title, subtitle, message);
		if (isSurge) $notification.post(title, subtitle, message);
	};
	this.http = (options, callback) => {
		if (options.policy) {
			options.node = options.policy;
			options.opts = { policy: options.policy };
			if (isStash) options.headers = {
				...options.headers,
				...{ "X-Stash-Selected-Proxy": encodeURIComponent(options.policy) }
			};
		}
		if (isQuanX) {
			$task.fetch(options).then(response => {
				callback(null, adapterStatus(response), response.body)
			}, reason => callback(reason.error, null, null))
		}
		if (isSurge) {
			$httpClient[options.method](options, (error, response, body) => {
				callback(error, adapterStatus(response), body)
			})
		}
	};
	this.done = (value = {}) => {
		if (value.response && isQuanX) {
			value.response.status = `HTTP/1.1 ${value.response.status}`;
		}
		$done((value.response && isQuanX) ? value.response : value)
	}
};

================================================
FILE: JD-DailyBonus/JD_DailyBonus.js
================================================
/*************************

京东多合一签到脚本

更新时间: 2021.09.09 20:20 v2.1.3
有效接口: 20+
脚本兼容: QuantumultX, Surge, Loon, JSBox, Node.js
电报频道: @NobyDa 
问题反馈: @NobyDa_bot 
如果转载: 请注明出处

*************************
【 QX, Surge, Loon 说明 】 :
*************************

初次使用时, app配置文件添加脚本配置, 并启用Mitm后:

Safari浏览器打开登录 https://home.m.jd.com/myJd/newhome.action 点击"我的"页面
或者使用旧版网址 https://bean.m.jd.com/bean/signIndex.action 点击签到并且出现签到日历
如果通知获取Cookie成功, 则可以使用此签到脚本. 注: 请勿在京东APP内获取!!!

获取京东金融签到Body说明: 正确添加脚本配置后, 进入"京东金融"APP, 在"首页"点击"签到"并签到一次, 待通知提示成功即可.

由于cookie的有效性(经测试网页Cookie有效周期最长31天),如果脚本后续弹出cookie无效的通知,则需要重复上述步骤。 
签到脚本将在每天的凌晨0:05执行, 您可以修改执行时间。 因部分接口京豆限量领取, 建议调整为凌晨签到。

BoxJs或QX Gallery订阅地址: https://raw.githubusercontent.com/NobyDa/Script/master/NobyDa_BoxJs.json

*************************
【 配置多京东账号签到说明 】 : 
*************************

正确配置QX、Surge、Loon后, 并使用此脚本获取"账号1"Cookie成功后, 请勿点击退出账号(可能会导致Cookie失效), 需清除浏览器资料或更换浏览器登录"账号2"获取即可; 账号3或以上同理.
注: 如需清除所有Cookie, 您可开启脚本内"DeleteCookie"选项 (第114行)

*************************
【 JSbox, Node.js 说明 】 :
*************************

开启抓包app后, Safari浏览器登录 https://home.m.jd.com/myJd/newhome.action 点击个人中心页面后, 返回抓包app搜索关键字 info/GetJDUserInfoUnion 复制请求头Cookie字段填入json串数据内即可

如需获取京东金融签到Body, 可进入"京东金融"APP (iOS), 在"首页"点击"签到"并签到一次, 返回抓包app搜索关键字 h5/m/appSign 复制请求体填入json串数据内即可
*/

var Key = ''; //该参数已废弃; 仅用于下游脚本的兼容, 请使用json串数据 ↓

var DualKey = ''; //该参数已废弃; 仅用于下游脚本的兼容, 请使用json串数据  ↓

var OtherKey = ``; //无限账号Cookie json串数据, 请严格按照json格式填写, 具体格式请看以下样例:

/*以下样例为双账号("cookie"为必须,其他可选), 第一个账号仅包含Cookie, 第二个账号包含Cookie和金融签到Body: 

var OtherKey = `[{
  "cookie": "pt_key=xxx;pt_pin=yyy;"
}, {
  "cookie": "pt_key=yyy;pt_pin=xxx;",
  "jrBody": "reqData=xxx"
}]`

   注1: 以上选项仅针对于JsBox或Node.js, 如果使用QX,Surge,Loon, 请使用脚本获取Cookie.
   注2: 多账号用户抓取"账号1"Cookie后, 请勿点击退出账号(可能会导致Cookie失效), 需清除浏览器资料或更换浏览器登录"账号2"抓取.
   注3: 如果使用Node.js, 需自行安装'request'模块. 例: npm install request -g
   注4: Node.js或JSbox环境下已配置数据持久化, 填写Cookie运行一次后, 后续更新脚本无需再次填写, 待Cookie失效后重新抓取填写即可.
   注5: 脚本将自动处理"持久化数据"和"手动填写cookie"之间的重复关系, 例如填写多个账号Cookie后, 后续其中一个失效, 仅需填写该失效账号的新Cookie即可, 其他账号不会被清除.

*************************
【Surge 4.2+ 脚本配置】:
*************************

[Script]
京东多合一签到 = type=cron,cronexp=5 0 * * *,wake-system=1,timeout=60,script-path=https://raw.githubusercontent.com/NobyDa/Script/master/JD-DailyBonus/JD_DailyBonus.js

获取京东Cookie = type=http-request,requires-body=1,pattern=^https:\/\/(api\.m|me-api|ms\.jr)\.jd\.com\/(client\.action\?functionId=signBean|user_new\/info\/GetJDUserInfoUnion\?|gw\/generic\/hy\/h5\/m\/appSign\?),script-path=https://raw.githubusercontent.com/NobyDa/Script/master/JD-DailyBonus/JD_DailyBonus.js

[MITM]
hostname = ms.jr.jd.com, me-api.jd.com, api.m.jd.com

*************************
【Loon 2.1+ 脚本配置】:
*************************

[Script]
cron "5 0 * * *" tag=京东多合一签到, script-path=https://raw.githubusercontent.com/NobyDa/Script/master/JD-DailyBonus/JD_DailyBonus.js

http-request ^https:\/\/(api\.m|me-api|ms\.jr)\.jd\.com\/(client\.action\?functionId=signBean|user_new\/info\/GetJDUserInfoUnion\?|gw\/generic\/hy\/h5\/m\/appSign\?) tag=获取京东Cookie, requires-body=true, script-path=https://raw.githubusercontent.com/NobyDa/Script/master/JD-DailyBonus/JD_DailyBonus.js

[MITM]
hostname = ms.jr.jd.com, me-api.jd.com, api.m.jd.com

*************************
【 QX 1.0.10+ 脚本配置 】 :
*************************

[task_local]
# 京东多合一签到
5 0 * * * https://raw.githubusercontent.com/NobyDa/Script/master/JD-DailyBonus/JD_DailyBonus.js, tag=京东多合一签到, img-url=https://raw.githubusercontent.com/NobyDa/mini/master/Color/jd.png,enabled=true

[rewrite_local]
# 获取京东Cookie. 
^https:\/\/(api\.m|me-api)\.jd\.com\/(client\.action\?functionId=signBean|user_new\/info\/GetJDUserInfoUnion\?) url script-request-header https://raw.githubusercontent.com/NobyDa/Script/master/JD-DailyBonus/JD_DailyBonus.js

# 获取钢镚签到body. 
^https:\/\/ms\.jr\.jd\.com\/gw\/generic\/hy\/h5\/m\/appSign\? url script-request-body https://raw.githubusercontent.com/NobyDa/Script/master/JD-DailyBonus/JD_DailyBonus.js

[mitm]
hostname = ms.jr.jd.com, me-api.jd.com, api.m.jd.com

*************************/

var LogDetails = false; //是否开启响应日志, true则开启

var stop = '0'; //自定义延迟签到, 单位毫秒. 默认分批并发无延迟; 该参数接受随机或指定延迟(例: '2000'则表示延迟2秒; '2000-5000'则表示延迟最小2秒,最大5秒内的随机延迟), 如填入延迟则切换顺序签到(耗时较长), Surge用户请注意在SurgeUI界面调整脚本超时; 注: 该参数Node.js或JSbox环境下已配置数据持久化, 留空(var stop = '')即可清除.

var DeleteCookie = false; //是否清除所有Cookie, true则开启.

var boxdis = true; //是否开启自动禁用, false则关闭. 脚本运行崩溃时(如VPN断连), 下次运行时将自动禁用相关崩溃接口(仅部分接口启用), 崩溃时可能会误禁用正常接口. (该选项仅适用于QX,Surge,Loon)

var ReDis = false; //是否移除所有禁用列表, true则开启. 适用于触发自动禁用后, 需要再次启用接口的情况. (该选项仅适用于QX,Surge,Loon)

var out = 0; //接口超时退出, 用于可能发生的网络不稳定, 0则关闭. 如QX日志出现大量"JS Context timeout"后脚本中断时, 建议填写6000

var $nobyda = nobyda();

var merge = {};

var KEY = '';

async function all(cookie, jrBody) {
  KEY = cookie;
  merge = {};
  $nobyda.num++;
  switch (stop) {
    case 0:
      await Promise.all([
        JingDongBean(stop), //京东京豆
        JingDongStore(stop), //京东超市
        JingRongSteel(stop, jrBody), //金融钢镚
        JingDongTurn(stop), //京东转盘
        JDFlashSale(stop), //京东闪购
        JingDongCash(stop), //京东现金红包
        JDMagicCube(stop, 2), //京东小魔方
        JingDongSubsidy(stop), //京东金贴
        JingDongGetCash(stop), //京东领现金
        JingDongShake(stop), //京东摇一摇
        JDSecKilling(stop), //京东秒杀
        // JingRongDoll(stop, 'JRDoll', '京东金融-签壹', '4D25A6F482'),
        // JingRongDoll(stop, 'JRThreeDoll', '京东金融-签叁', '69F5EC743C'),
        // JingRongDoll(stop, 'JRFourDoll', '京东金融-签肆', '30C4F86264'),
        // JingRongDoll(stop, 'JRFiveDoll', '京东金融-签伍', '1D06AA3B0F')
      ]);
      await Promise.all([
        JDUserSignPre(stop, 'JDUndies', '京东商城-内衣', '4PgpL1xqPSW1sVXCJ3xopDbB1f69'), //京东内衣馆
        JDUserSignPre(stop, 'JDCard', '京东商城-卡包', '7e5fRnma6RBATV9wNrGXJwihzcD'), //京东卡包
        // JDUserSignPre(stop, 'JDCustomized', '京东商城-定制', '2BJK5RBdvc3hdddZDS1Svd5Esj3R'), //京东定制
        JDUserSignPre(stop, 'JDaccompany', '京东商城-陪伴', 'kPM3Xedz1PBiGQjY4ZYGmeVvrts'), //京东陪伴
        JDUserSignPre(stop, 'JDShoes', '京东商城-鞋靴', '4RXyb1W4Y986LJW8ToqMK14BdTD'), //京东鞋靴
        JDUserSignPre(stop, 'JDChild', '京东商城-童装', '3Af6mZNcf5m795T8dtDVfDwWVNhJ'), //京东童装馆
        JDUserSignPre(stop, 'JDBaby', '京东商城-母婴', '3BbAVGQPDd6vTyHYjmAutXrKAos6'), //京东母婴馆
        JDUserSignPre(stop, 'JD3C', '京东商城-数码', '4SWjnZSCTHPYjE5T7j35rxxuMTb6'), //京东数码电器馆
        JDUserSignPre(stop, 'JDWomen', '京东商城-女装', 'DpSh7ma8JV7QAxSE2gJNro8Q2h9'), //京东女装馆
        JDUserSignPre(stop, 'JDBook', '京东商城-图书', '3SC6rw5iBg66qrXPGmZMqFDwcyXi'), //京东图书
        // JDUserSignPre(stop, 'ReceiveJD', '京东商城-领豆', 'Ni5PUSK7fzZc4EKangHhqPuprn2'), //京东-领京豆
        JingRongDoll(stop, 'JTDouble', '京东金贴-双签', '1DF13833F7'), //京东金融 金贴双签
        // JingRongDoll(stop, 'XJDouble', '金融现金-双签', 'F68B2C3E71', '', '', '', 'xianjin') //京东金融 现金双签
      ]);
      await Promise.all([
        JDUserSignPre(stop, 'JDStory', '京东失眠-补贴', 'UcyW9Znv3xeyixW1gofhW2DAoz4'), //失眠补贴
        JDUserSignPre(stop, 'JDPhone', '京东手机-小时', '4Vh5ybVr98nfJgros5GwvXbmTUpg'), //手机小时达
        JDUserSignPre(stop, 'JDEsports', '京东商城-电竞', 'CHdHQhA5AYDXXQN9FLt3QUAPRsB'), //京东电竞
        JDUserSignPre(stop, 'JDClothing', '京东商城-服饰', '4RBT3H9jmgYg1k2kBnHF8NAHm7m8'), //京东服饰
        JDUserSignPre(stop, 'JDSuitcase', '京东商城-箱包', 'ZrH7gGAcEkY2gH8wXqyAPoQgk6t'), //京东箱包馆
        JDUserSignPre(stop, 'JDSchool', '京东商城-校园', '2QUxWHx5BSCNtnBDjtt5gZTq7zdZ'), //京东校园
        JDUserSignPre(stop, 'JDHealth', '京东商城-健康', 'w2oeK5yLdHqHvwef7SMMy4PL8LF'), //京东健康
        JDUserSignPre(stop, 'JDShand', '京东拍拍-二手', '3S28janPLYmtFxypu37AYAGgivfp'), //京东拍拍二手
        JDUserSignPre(stop, 'JDClean', '京东商城-清洁', '2Tjm6ay1ZbZ3v7UbriTj6kHy9dn6'), //京东清洁馆
        JDUserSignPre(stop, 'JDCare', '京东商城-个护', '2tZssTgnQsiUqhmg5ooLSHY9XSeN'), //京东个人护理馆
        JDUserSignPre(stop, 'JDJiaDian', '京东商城-家电', '3uvPyw1pwHARGgndatCXddLNUxHw'), // 京东小家电
        // JDUserSignPre(stop, 'JDJewels', '京东商城-珠宝', 'zHUHpTHNTaztSRfNBFNVZscyFZU'), //京东珠宝馆
        // JDUserSignPre(stop, 'JDMakeup', '京东商城-美妆', '2smCxzLNuam5L14zNJHYu43ovbAP'), //京东美妆馆
        JDUserSignPre(stop, 'JDVege', '京东商城-菜场', 'Wcu2LVCFMkBP3HraRvb7pgSpt64'), //京东菜场
        // JDUserSignPre(stop, 'JDLive', '京东智能-生活', 'KcfFqWvhb5hHtaQkS4SD1UU6RcQ') //京东智能生活
      ]);
      await JingRongDoll(stop, 'JDDouble', '金融京豆-双签', 'F68B2C3E71', '', '', '', 'jingdou'); //京东金融 京豆双签
      break;
    default:
      await JingDongBean(0); //京东京豆
      await JingDongStore(Wait(stop)); //京东超市
      await JingRongSteel(Wait(stop), jrBody); //金融钢镚
      await JingDongTurn(Wait(stop)); //京东转盘
      await JDFlashSale(Wait(stop)); //京东闪购
      await JingDongCash(Wait(stop)); //京东现金红包
      await JDMagicCube(Wait(stop), 2); //京东小魔方
      await JingDongGetCash(Wait(stop)); //京东领现金
      await JingDongSubsidy(Wait(stop)); //京东金贴
      await JingDongShake(Wait(stop)); //京东摇一摇
      await JDSecKilling(Wait(stop)); //京东秒杀
      // await JingRongDoll(Wait(stop), 'JRThreeDoll', '京东金融-签叁', '69F5EC743C');
      // await JingRongDoll(Wait(stop), 'JRFourDoll', '京东金融-签肆', '30C4F86264');
      // await JingRongDoll(Wait(stop), 'JRFiveDoll', '京东金融-签伍', '1D06AA3B0F');
      // await JingRongDoll(Wait(stop), 'JRDoll', '京东金融-签壹', '4D25A6F482');
      // await JingRongDoll(Wait(stop), 'XJDouble', '金融现金-双签', 'F68B2C3E71', '', '', '', 'xianjin'); //京东金融 现金双签
      await JingRongDoll(Wait(stop), 'JTDouble', '京东金贴-双签', '1DF13833F7'); //京东金融 金贴双签
      await JDUserSignPre(Wait(stop), 'JDStory', '京东失眠-补贴', 'UcyW9Znv3xeyixW1gofhW2DAoz4'); //失眠补贴
      await JDUserSignPre(Wait(stop), 'JDPhone', '京东手机-小时', '4Vh5ybVr98nfJgros5GwvXbmTUpg'); //手机小时达
      await JDUserSignPre(Wait(stop), 'JDCard', '京东商城-卡包', '7e5fRnma6RBATV9wNrGXJwihzcD'); //京东卡包
      await JDUserSignPre(Wait(stop), 'JDUndies', '京东商城-内衣', '4PgpL1xqPSW1sVXCJ3xopDbB1f69'); //京东内衣馆
      await JDUserSignPre(Wait(stop), 'JDEsports', '京东商城-电竞', 'CHdHQhA5AYDXXQN9FLt3QUAPRsB'); //京东电竞
      // await JDUserSignPre(Wait(stop), 'JDCustomized', '京东商城-定制', '2BJK5RBdvc3hdddZDS1Svd5Esj3R'); //京东定制
      await JDUserSignPre(Wait(stop), 'JDSuitcase', '京东商城-箱包', 'ZrH7gGAcEkY2gH8wXqyAPoQgk6t'); //京东箱包馆
      await JDUserSignPre(Wait(stop), 'JDClothing', '京东商城-服饰', '4RBT3H9jmgYg1k2kBnHF8NAHm7m8'); //京东服饰
      await JDUserSignPre(Wait(stop), 'JDSchool', '京东商城-校园', '2QUxWHx5BSCNtnBDjtt5gZTq7zdZ'); //京东校园 
      await JDUserSignPre(Wait(stop), 'JDHealth', '京东商城-健康', 'w2oeK5yLdHqHvwef7SMMy4PL8LF'); //京东健康
      await JDUserSignPre(Wait(stop), 'JDShoes', '京东商城-鞋靴', '4RXyb1W4Y986LJW8ToqMK14BdTD'); //京东鞋靴
      await JDUserSignPre(Wait(stop), 'JDChild', '京东商城-童装', '3Af6mZNcf5m795T8dtDVfDwWVNhJ'); //京东童装馆
      await JDUserSignPre(Wait(stop), 'JDBaby', '京东商城-母婴', '3BbAVGQPDd6vTyHYjmAutXrKAos6'); //京东母婴馆
      await JDUserSignPre(Wait(stop), 'JD3C', '京东商城-数码', '4SWjnZSCTHPYjE5T7j35rxxuMTb6'); //京东数码电器馆
      await JDUserSignPre(Wait(stop), 'JDWomen', '京东商城-女装', 'DpSh7ma8JV7QAxSE2gJNro8Q2h9'); //京东女装馆
      await JDUserSignPre(Wait(stop), 'JDBook', '京东商城-图书', '3SC6rw5iBg66qrXPGmZMqFDwcyXi'); //京东图书
      await JDUserSignPre(Wait(stop), 'JDShand', '京东拍拍-二手', '3S28janPLYmtFxypu37AYAGgivfp'); //京东拍拍二手
      // await JDUserSignPre(Wait(stop), 'JDMakeup', '京东商城-美妆', '2smCxzLNuam5L14zNJHYu43ovbAP'); //京东美妆馆
      await JDUserSignPre(Wait(stop), 'JDVege', '京东商城-菜场', 'Wcu2LVCFMkBP3HraRvb7pgSpt64'); //京东菜场
      await JDUserSignPre(Wait(stop), 'JDaccompany', '京东商城-陪伴', 'kPM3Xedz1PBiGQjY4ZYGmeVvrts'); //京东陪伴
      // await JDUserSignPre(Wait(stop), 'JDLive', '京东智能-生活', 'KcfFqWvhb5hHtaQkS4SD1UU6RcQ'); //京东智能生活
      await JDUserSignPre(Wait(stop), 'JDClean', '京东商城-清洁', '2Tjm6ay1ZbZ3v7UbriTj6kHy9dn6'); //京东清洁馆
      await JDUserSignPre(Wait(stop), 'JDCare', '京东商城-个护', '2tZssTgnQsiUqhmg5ooLSHY9XSeN'); //京东个人护理馆
      await JDUserSignPre(Wait(stop), 'JDJiaDian', '京东商城-家电', '3uvPyw1pwHARGgndatCXddLNUxHw'); // 京东小家电馆
      // await JDUserSignPre(Wait(stop), 'ReceiveJD', '京东商城-领豆', 'Ni5PUSK7fzZc4EKangHhqPuprn2'); //京东-领京豆
      // await JDUserSignPre(Wait(stop), 'JDJewels', '京东商城-珠宝', 'zHUHpTHNTaztSRfNBFNVZscyFZU'); //京东珠宝馆
      await JingRongDoll(Wait(stop), 'JDDouble', '金融京豆-双签', 'F68B2C3E71', '', '', '', 'jingdou'); //京东金融 京豆双签
      break;
  }
  await Promise.all([
    TotalSteel(), //总钢镚查询
    TotalCash(), //总红包查询
    TotalBean(), //总京豆查询
    TotalSubsidy(), //总金贴查询
    TotalMoney() //总现金查询
  ]);
  await notify(); //通知模块
}

function notify() {
  return new Promise(resolve => {
    try {
      var bean = 0;
      var steel = 0;
      var cash = 0;
      var money = 0;
      var subsidy = 0;
      var success = 0;
      var fail = 0;
      var err = 0;
      var notify = '';
      for (var i in merge) {
        bean += merge[i].bean ? Number(merge[i].bean) : 0
        steel += merge[i].steel ? Number(merge[i].steel) : 0
        cash += merge[i].Cash ? Number(merge[i].Cash) : 0
        money += merge[i].Money ? Number(merge[i].Money) : 0
        subsidy += merge[i].subsidy ? Number(merge[i].subsidy) : 0
        success += merge[i].success ? Number(merge[i].success) : 0
        fail += merge[i].fail ? Number(merge[i].fail) : 0
        err += merge[i].error ? Number(merge[i].error) : 0
        notify += merge[i].notify ? "\n" + merge[i].notify : ""
      }
      var Cash = merge.TotalCash && merge.TotalCash.TCash ? `${merge.TotalCash.TCash}红包` : ""
      var Steel = merge.TotalSteel && merge.TotalSteel.TSteel ? `${merge.TotalSteel.TSteel}钢镚` : ``
      var beans = merge.TotalBean && merge.TotalBean.Qbear ? `${merge.TotalBean.Qbear}京豆${Steel?`, `:``}` : ""
      var Money = merge.TotalMoney && merge.TotalMoney.TMoney ? `${merge.TotalMoney.TMoney}现金${Cash?`, `:``}` : ""
      var Subsidy = merge.TotalSubsidy && merge.TotalSubsidy.TSubsidy ? `${merge.TotalSubsidy.TSubsidy}金贴${Money||Cash?", ":""}` : ""
      var Tbean = bean ? `${bean.toFixed(0)}京豆${steel?", ":""}` : ""
      var TSteel = steel ? `${steel.toFixed(2)}钢镚` : ""
      var TCash = cash ? `${cash.toFixed(2)}红包${subsidy||money?", ":""}` : ""
      var TSubsidy = subsidy ? `${subsidy.toFixed(2)}金贴${money?", ":""}` : ""
      var TMoney = money ? `${money.toFixed(2)}现金` : ""
      var Ts = success ? `成功${success}个${fail||err?`, `:``}` : ``
      var Tf = fail ? `失败${fail}个${err?`, `:``}` : ``
      var Te = err ? `错误${err}个` : ``
      var one = `【签到概览】:  ${Ts+Tf+Te}${Ts||Tf||Te?`\n`:`获取失败\n`}`
      var two = Tbean || TSteel ? `【签到奖励】:  ${Tbean+TSteel}\n` : ``
      var three = TCash || TSubsidy || TMoney ? `【其他奖励】:  ${TCash+TSubsidy+TMoney}\n` : ``
      var four = `【账号总计】:  ${beans+Steel}${beans||Steel?`\n`:`获取失败\n`}`
      var five = `【其他总计】:  ${Subsidy+Money+Cash}${Subsidy||Money||Cash?`\n`:`获取失败\n`}`
      var DName = merge.TotalBean && merge.TotalBean.nickname ? merge.TotalBean.nickname : "获取失败"
      var cnNum = ["零", "一", "二", "三", "四", "五", "六", "七", "八", "九", "十"];
      const Name = DualKey || OtherKey.length > 1 ? `【签到号${cnNum[$nobyda.num]||$nobyda.num}】:  ${DName}\n` : ``
      const disables = $nobyda.read("JD_DailyBonusDisables")
      const amount = disables ? disables.split(",").length : 0
      const disa = !notify || amount ? `【温馨提示】:  检测到${$nobyda.disable?`上次执行意外崩溃, `:``}已禁用${notify?`${amount}个`:`所有`}接口, 如需开启请前往BoxJs或查看脚本内第118行注释.\n` : ``
      $nobyda.notify("", "", Name + one + two + three + four + five + disa + notify, {
        'media-url': $nobyda.headUrl || 'https://cdn.jsdelivr.net/gh/NobyDa/mini@master/Color/jd.png'
      });
      $nobyda.headUrl = null;
      if ($nobyda.isJSBox) {
        $nobyda.st = (typeof($nobyda.st) == 'undefined' ? '' : $nobyda.st) + Name + one + two + three + four + five + "\n"
      }
    } catch (eor) {
      $nobyda.notify("通知模块 " + eor.name + "‼️", JSON.stringify(eor), eor.message)
    } finally {
      resolve()
    }
  });
}

(async function ReadCookie() {
  const EnvInfo = $nobyda.isJSBox ? "JD_Cookie" : "CookieJD";
  const EnvInfo2 = $nobyda.isJSBox ? "JD_Cookie2" : "CookieJD2";
  const EnvInfo3 = $nobyda.isJSBox ? "JD_Cookies" : "CookiesJD";
  const move = CookieMove($nobyda.read(EnvInfo) || Key, $nobyda.read(EnvInfo2) || DualKey, EnvInfo, EnvInfo2, EnvInfo3);
  const cookieSet = $nobyda.read(EnvInfo3);
  if (DeleteCookie) {
    const write = $nobyda.write("", EnvInfo3);
    throw new Error(`Cookie清除${write?`成功`:`失败`}, 请手动关闭脚本内"DeleteCookie"选项`);
  } else if ($nobyda.isRequest) {
    GetCookie()
  } else if (Key || DualKey || (OtherKey || cookieSet || '[]') != '[]') {
    if (($nobyda.isJSBox || $nobyda.isNode) && stop !== '0') $nobyda.write(stop, "JD_DailyBonusDelay");
    out = parseInt($nobyda.read("JD_DailyBonusTimeOut")) || out;
    stop = Wait($nobyda.read("JD_DailyBonusDelay"), true) || Wait(stop, true);
    boxdis = $nobyda.read("JD_Crash_disable") === "false" || $nobyda.isNode || $nobyda.isJSBox ? false : boxdis;
    LogDetails = $nobyda.read("JD_DailyBonusLog") === "true" || LogDetails;
    ReDis = ReDis ? $nobyda.write("", "JD_DailyBonusDisables") : "";
    $nobyda.num = 0;
    if (Key) await all(Key);
    if (DualKey && DualKey !== Key) await all(DualKey);
    if ((OtherKey || cookieSet || '[]') != '[]') {
      try {
        OtherKey = checkFormat([...JSON.parse(OtherKey || '[]'), ...JSON.parse(cookieSet || '[]')]);
        const updateSet = OtherKey.length ? $nobyda.write(JSON.stringify(OtherKey, null, 2), EnvInfo3) : '';
        for (let i = 0; i < OtherKey.length; i++) {
          const ck = OtherKey[i].cookie;
          const jr = OtherKey[i].jrBody;
          if (ck != Key && ck != DualKey) {
            await all(ck, jr)
          }
        }
      } catch (e) {
        throw new Error(`账号Cookie读取失败, 请检查Json格式. \n${e.message}`)
      }
    }
    $nobyda.time();
  } else {
    throw new Error('脚本终止, 未获取Cookie ‼️')
  }
})().catch(e => {
  $nobyda.notify("京东签到", "", e.message || JSON.stringify(e))
}).finally(() => {
  if ($nobyda.isJSBox) $intents.finish($nobyda.st);
  $nobyda.done();
})

function JingDongBean(s) {
  merge.JDBean = {};
  return new Promise(resolve => {
    if (disable("JDBean")) return resolve()
    setTimeout(() => {
      const JDBUrl = {
        url: 'https://api.m.jd.com/client.action',
        headers: {
          Cookie: KEY
        },
        body: 'functionId=signBeanIndex&appid=ld'
      };
      $nobyda.post(JDBUrl, function(error, response, data) {
        try {
          if (error) {
            throw new Error(error)
          } else {
            const cc = JSON.parse(data)
            const Details = LogDetails ? "response:\n" + data : '';
            if (cc.code == 3) {
              console.log("\n" + "京东商城-京豆Cookie失效 " + Details)
              merge.JDBean.notify = "京东商城-京豆: 失败, 原因: Cookie失效‼️"
              merge.JDBean.fail = 1
            } else if (data.match(/跳转至拼图/)) {
              merge.JDBean.notify = "京东商城-京豆: 失败, 需要拼图验证 ⚠️"
              merge.JDBean.fail = 1
            } else if (data.match(/\"status\":\"?1\"?/)) {
              console.log("\n" + "京东商城-京豆签到成功 " + Details)
              if (data.match(/dailyAward/)) {
                merge.JDBean.notify = "京东商城-京豆: 成功, 明细: " + cc.data.dailyAward.beanAward.beanCount + "京豆 🐶"
                merge.JDBean.bean = cc.data.dailyAward.beanAward.beanCount
              } else if (data.match(/continuityAward/)) {
                merge.JDBean.notify = "京东商城-京豆: 成功, 明细: " + cc.data.continuityAward.beanAward.beanCount + "京豆 🐶"
                merge.JDBean.bean = cc.data.continuityAward.beanAward.beanCount
              } else if (data.match(/新人签到/)) {
                const quantity = data.match(/beanCount\":\"(\d+)\".+今天/)
                merge.JDBean.bean = quantity ? quantity[1] : 0
                merge.JDBean.notify = "京东商城-京豆: 成功, 明细: " + (quantity ? quantity[1] : "无") + "京豆 🐶"
              } else {
                merge.JDBean.notify = "京东商城-京豆: 成功, 明细: 无京豆 🐶"
              }
              merge.JDBean.success = 1
            } else {
              merge.JDBean.fail = 1
              console.log("\n" + "京东商城-京豆签到失败 " + Details)
              if (data.match(/(已签到|新人签到)/)) {
                merge.JDBean.notify = "京东商城-京豆: 失败, 原因: 已签过 ⚠️"
              } else if (data.match(/人数较多|S101/)) {
                merge.JDBean.notify = "京东商城-京豆: 失败, 签到人数较多 ⚠️"
              } else {
                merge.JDBean.notify = "京东商城-京豆: 失败, 原因: 未知 ⚠️"
              }
            }
          }
        } catch (eor) {
          $nobyda.AnError("京东商城-京豆", "JDBean", eor, response, data)
        } finally {
          resolve()
        }
      })
    }, s)
    if (out) setTimeout(resolve, out + s)
  });
}

// function JingDongTurn(s) {
//   merge.JDTurn = {}, merge.JDTurn.notify = "", merge.JDTurn.success = 0, merge.JDTurn.bean = 0;
//   return new Promise((resolve, reject) => {
//     if (disable("JDTurn")) return reject()
//     const JDTUrl = {
//       url: 'https://api.m.jd.com/client.action?functionId=wheelSurfIndex&body=%7B%22actId%22%3A%22jgpqtzjhvaoym%22%2C%22appSource%22%3A%22jdhome%22%7D&appid=ld',
//       headers: {
//         Cookie: KEY,
//       }
//     };
//     $nobyda.get(JDTUrl, async function(error, response, data) {
//       try {
//         if (error) {
//           throw new Error(error)
//         } else {
//           const cc = JSON.parse(data)
//           const Details = LogDetails ? "response:\n" + data : '';
//           if (cc.data && cc.data.lotteryCode) {
//             console.log("\n" + "京东商城-转盘查询成功 " + Details)
//             return resolve(cc.data.lotteryCode)
//           } else {
//             merge.JDTurn.notify = "京东商城-转盘: 失败, 原因: 查询错误 ⚠️"
//             merge.JDTurn.fail = 1
//             console.log("\n" + "京东商城-转盘查询失败 " + Details)
//           }
//         }
//       } catch (eor) {
//         $nobyda.AnError("京东转盘-查询", "JDTurn", eor, response, data)
//       } finally {
//         reject()
//       }
//     })
//     if (out) setTimeout(reject, out + s)
//   }).then(data => {
//     return JingDongTurnSign(s, data);
//   }, () => {});
// }

function JingDongTurn(s) {
  if (!merge.JDTurn) merge.JDTurn = {}, merge.JDTurn.notify = "", merge.JDTurn.success = 0, merge.JDTurn.bean = 0;
  return new Promise(resolve => {
    if (disable("JDTurn")) return resolve();
    setTimeout(() => {
      const JDTUrl = {
        url: `https://api.m.jd.com/client.action?functionId=babelGetLottery`,
        headers: {
          Cookie: KEY
        },
        body: 'body=%7B%22enAwardK%22%3A%2295d235f2a09578c6613a1a029b26d12d%22%2C%22riskParam%22%3A%7B%7D%7D&client=wh5'
      };
      $nobyda.post(JDTUrl, async function(error, response, data) {
        try {
          if (error) {
            throw new Error(error)
          } else {
            const cc = JSON.parse(data)
            const Details = LogDetails ? "response:\n" + data : '';
            const also = merge.JDTurn.notify ? true : false
            if (cc.code == 3) {
              console.log("\n" + "京东转盘Cookie失效 " + Details)
              merge.JDTurn.notify = "京东商城-转盘: 失败, 原因: Cookie失效‼️"
              merge.JDTurn.fail = 1
            } else if (data.match(/(\"T216\"|活动结束)/)) {
              merge.JDTurn.notify = "京东商城-转盘: 失败, 原因: 活动结束 ⚠️"
              merge.JDTurn.fail = 1
            } else if (data.match(/\d+京豆/)) {
              console.log("\n" + "京东商城-转盘签到成功 " + Details)
              merge.JDTurn.bean += (cc.prizeName && cc.prizeName.split(/(\d+)/)[1]) || 0
              merge.JDTurn.notify += `${also?`\n`:``}京东商城-转盘: ${also?`多次`:`成功`}, 明细: ${merge.JDTurn.bean||`无`}京豆 🐶`
              merge.JDTurn.success += 1
              if (cc.chances > 0) {
                await JingDongTurnSign(2000)
              }
            } else if (data.match(/未中奖|擦肩而过/)) {
              merge.JDTurn.notify += `${also?`\n`:``}京东商城-转盘: ${also?`多次`:`成功`}, 状态: 未中奖 🐶`
              merge.JDTurn.success += 1
              if (cc.chances > 0) {
                await JingDongTurnSign(2000)
              }
            } else {
              console.log("\n" + "京东商城-转盘签到失败 " + Details)
              merge.JDTurn.fail = 1
              if (data.match(/(机会已用完|次数为0)/)) {
                merge.JDTurn.notify = "京东商城-转盘: 失败, 原因: 已转过 ⚠️"
              } else if (data.match(/(T210|密码)/)) {
                merge.JDTurn.notify = "京东商城-转盘: 失败, 原因: 无支付密码 ⚠️"
              } else {
                merge.JDTurn.notify += `${also?`\n`:``}京东商城-转盘: 失败, 原因: 未知 ⚠️${also?` (多次)`:``}`
              }
            }
          }
        } catch (eor) {
          $nobyda.AnError("京东商城-转盘", "JDTurn", eor, response, data)
        } finally {
          resolve()
        }
      })
    }, s)
    if (out) setTimeout(resolve, out + s)
  });
}

function JingRongSteel(s, body) {
  merge.JRSteel = {};
  return new Promise(resolve => {
    if (disable("JRSteel")) return resolve();
    if (!body) {
      merge.JRSteel.fail = 1;
      merge.JRSteel.notify = "京东金融-钢镚: 失败, 未获取签到Body ⚠️";
      return resolve();
    }
    setTimeout(() => {
      const JRSUrl = {
        url: 'https://ms.jr.jd.com/gw/generic/hy/h5/m/appSign',
        headers: {
          Cookie: KEY
        },
        body: body || ''
      };
      $nobyda.post(JRSUrl, function(error, response, data) {
        try {
          if (error) throw new Error(error)
          const cc = JSON.parse(data)
          const Details = LogDetails ? "response:\n" + data : '';
          if (cc.resultCode == 0 && cc.resultData && cc.resultData.resBusiCode == 0) {
            console.log("\n" + "京东金融-钢镚签到成功 " + Details)
            merge.JRSteel.notify = `京东金融-钢镚: 成功, 获得钢镚奖励 💰`
            merge.JRSteel.success = 1
          } else {
            console.log("\n" + "京东金融-钢镚签到失败 " + Details)
            merge.JRSteel.fail = 1
            if (cc.resultCode == 0 && cc.resultData && cc.resultData.resBusiCode == 15) {
              merge.JRSteel.notify = "京东金融-钢镚: 失败, 原因: 已签过 ⚠️"
            } else if (data.match(/未实名/)) {
              merge.JRSteel.notify = "京东金融-钢镚: 失败, 账号未实名 ⚠️"
            } else if (cc.resultCode == 3) {
              merge.JRSteel.notify = "京东金融-钢镚: 失败, 原因: Cookie失效‼️"
            } else {
              const ng = (cc.resultData && cc.resultData.resBusiMsg) || cc.resultMsg
              merge.JRSteel.notify = `京东金融-钢镚: 失败, ${`原因: ${ng||`未知`}`} ⚠️`
            }
          }
        } catch (eor) {
          $nobyda.AnError("京东金融-钢镚", "JRSteel", eor, response, data)
        } finally {
          resolve()
        }
      })
    }, s)
    if (out) setTimeout(resolve, out + s)
  });
}

function JingDongShake(s) {
  if (!merge.JDShake) merge.JDShake = {}, merge.JDShake.success = 0, merge.JDShake.bean = 0, merge.JDShake.notify = '';
  return new Promise(resolve => {
    if (disable("JDShake")) return resolve()
    setTimeout(() => {
      const JDSh = {
        url: 'https://api.m.jd.com/client.action?appid=vip_h5&functionId=vvipclub_shaking',
        headers: {
          Cookie: KEY,
        }
      };
      $nobyda.get(JDSh, async function(error, response, data) {
        try {
          if (error) {
            throw new Error(error)
          } else {
            const Details = LogDetails ? "response:\n" + data : '';
            const cc = JSON.parse(data)
            const also = merge.JDShake.notify ? true : false
            if (data.match(/prize/)) {
              console.log("\n" + "京东商城-摇一摇签到成功 " + Details)
              merge.JDShake.success += 1
              if (cc.data.prizeBean) {
                merge.JDShake.bean += cc.data.prizeBean.count || 0
                merge.JDShake.notify += `${also?`\n`:``}京东商城-摇摇: ${also?`多次`:`成功`}, 明细: ${merge.JDShake.bean || `无`}京豆 🐶`
              } else if (cc.data.prizeCoupon) {
                merge.JDShake.notify += `${also?`\n`:``}京东商城-摇摇: ${also?`多次, `:``}获得满${cc.data.prizeCoupon.quota}减${cc.data.prizeCoupon.discount}优惠券→ ${cc.data.prizeCoupon.limitStr}`
              } else {
                merge.JDShake.notify += `${also?`\n`:``}京东商城-摇摇: 成功, 明细: 未知 ⚠️${also?` (多次)`:``}`
              }
              if (cc.data.luckyBox.freeTimes != 0) {
                await JingDongShake(s)
              }
            } else {
              console.log("\n" + "京东商城-摇一摇签到失败 " + Details)
              if (data.match(/true/)) {
                merge.JDShake.notify += `${also?`\n`:``}京东商城-摇摇: 成功, 明细: 无奖励 🐶${also?` (多次)`:``}`
                merge.JDShake.success += 1
                if (cc.data.luckyBox.freeTimes != 0) {
                  await JingDongShake(s)
                }
              } else {
                merge.JDShake.fail = 1
                if (data.match(/(无免费|8000005|9000005)/)) {
                  merge.JDShake.notify = "京东商城-摇摇: 失败, 原因: 已摇过 ⚠️"
                } else if (data.match(/(未登录|101)/)) {
                  merge.JDShake.notify = "京东商城-摇摇: 失败, 原因: Cookie失效‼️"
                } else {
                  merge.JDShake.notify += `${also?`\n`:``}京东商城-摇摇: 失败, 原因: 未知 ⚠️${also?` (多次)`:``}`
                }
              }
            }
          }
        } catch (eor) {
          $nobyda.AnError("京东商城-摇摇", "JDShake", eor, response, data)
        } finally {
          resolve()
        }
      })
    }, s)
    if (out) setTimeout(resolve, out + s)
  });
}

function JDUserSignPre(s, key, title, ac) {
  merge[key] = {};
  if ($nobyda.isJSBox) {
    return JDUserSignPre2(s, key, title, ac);
  } else {
    return JDUserSignPre1(s, key, title, ac);
  }
}

function JDUserSignPre1(s, key, title, acData, ask) {
  return new Promise((resolve, reject) => {
    if (disable(key, title, 1)) return reject()
    const JDUrl = {
      url: 'https://api.m.jd.com/?client=wh5&functionId=qryH5BabelFloors',
      headers: {
        Cookie: KEY
      },
      opts: {
        'filter': 'try{var od=JSON.parse(body);var params=(od.floatLayerList||[]).filter(o=>o.params&&o.params.match(/enActK/)).map(o=>o.params).pop()||(od.floorList||[]).filter(o=>o.template=="signIn"&&o.signInfos&&o.signInfos.params&&o.signInfos.params.match(/enActK/)).map(o=>o.signInfos&&o.signInfos.params).pop();var tId=(od.floorList||[]).filter(o=>o.boardParams&&o.boardParams.turnTableId).map(o=>o.boardParams.turnTableId).pop();var page=od.paginationFlrs;return JSON.stringify({qxAct:params||null,qxTid:tId||null,qxPage:page||null})}catch(e){return `=> 过滤器发生错误: ${e.message}`}'
      },
      body: `body=${encodeURIComponent(`{"activityId":"${acData}"${ask?`,"paginationParam":"2","paginationFlrs":"${ask}"`:``}}`)}`
    };
    $nobyda.post(JDUrl, async function(error, response, data) {
      try {
        if (error) {
          throw new Error(error)
        } else {
          const od = JSON.parse(data || '{}');
          const turnTableId = od.qxTid || (od.floorList || []).filter(o => o.boardParams && o.boardParams.turnTableId).map(o => o.boardParams.turnTableId).pop();
          const page = od.qxPage || od.paginationFlrs;
          if (data.match(/enActK/)) { // 含有签到活动数据
            let params = od.qxAct || (od.floatLayerList || []).filter(o => o.params && o.params.match(/enActK/)).map(o => o.params).pop()
            if (!params) { // 第一处找到签到所需数据
              // floatLayerList未找到签到所需数据,从floorList中查找
              let signInfo = (od.floorList || []).filter(o => o.template == 'signIn' && o.signInfos && o.signInfos.params && o.signInfos.params.match(/enActK/))
                .map(o => o.signInfos).pop();
              if (signInfo) {
                if (signInfo.signStat == '1') {
                  console.log(`\n${title}重复签到`)
                  merge[key].notify = `${title}: 失败, 原因: 已签过 ⚠️`
                  merge[key].fail = 1
                } else {
                  params = signInfo.params;
                }
              } else {
                merge[key].notify = `${title}: 失败, 活动查找异常 ⚠️`
                merge[key].fail = 1
              }
            }
            if (params) {
              return resolve({
                params: params
              }); // 执行签到处理
            }
          } else if (turnTableId) { // 无签到数据, 但含有关注店铺签到
            const boxds = $nobyda.read("JD_Follow_disable") === "false" ? false : true
            if (boxds) {
              console.log(`\n${title}关注店铺`)
              return resolve(parseInt(turnTableId))
            } else {
              merge[key].notify = `${title}: 失败, 需要关注店铺 ⚠️`
              merge[key].fail = 1
            }
          } else if (page && !ask) { // 无签到数据, 尝试带参查询
            const boxds = $nobyda.read("JD_Retry_disable") === "false" ? false : true
            if (boxds) {
              console.log(`\n${title}二次查询`)
              return resolve(page)
            } else {
              merge[key].notify = `${title}: 失败, 请尝试开启增强 ⚠️`
              merge[key].fail = 1
            }
          } else {
            merge[key].notify = `${title}: 失败, ${!data ? `需要手动执行` : `不含活动数据`} ⚠️`
            merge[key].fail = 1
          }
        }
        reject()
      } catch (eor) {
        $nobyda.AnError(title, key, eor, response, data)
        reject()
      }
    })
    if (out) setTimeout(reject, out + s)
  }).then(data => {
    disable(key, title, 2)
    if (typeof(data) == "object") return JDUserSign1(s, key, title, encodeURIComponent(JSON.stringify(data)));
    if (typeof(data) == "number") return JDUserSign2(s, key, title, data);
    if (typeof(data) == "string") return JDUserSignPre1(s, key, title, acData, data);
  }, () => disable(key, title, 2))
}

function JDUserSignPre2(s, key, title, acData) {
  return new Promise((resolve, reject) => {
    if (disable(key, title, 1)) return reject()
    const JDUrl = {
      url: `https://pro.m.jd.com/mall/active/${acData}/index.html`,
      headers: {
        Cookie: KEY,
      }
    };
    $nobyda.get(JDUrl, async function(error, response, data) {
      try {
        if (error) {
          throw new Error(error)
        } else {
          const act = data.match(/\"params\":\"\{\\\"enActK.+?\\\"\}\"/)
          const turnTable = data.match(/\"turnTableId\":\"(\d+)\"/)
          const page = data.match(/\"paginationFlrs\":\"(\[\[.+?\]\])\"/)
          if (act) { // 含有签到活动数据
            return resolve(act)
          } else if (turnTable) { // 无签到数据, 但含有关注店铺签到
            const boxds = $nobyda.read("JD_Follow_disable") === "false" ? false : true
            if (boxds) {
              console.log(`\n${title}关注店铺`)
              return resolve(parseInt(turnTable[1]))
            } else {
              merge[key].notify = `${title}: 失败, 需要关注店铺 ⚠️`
              merge[key].fail = 1
            }
          } else if (page) { // 无签到数据, 尝试带参查询
            const boxds = $nobyda.read("JD_Retry_disable") === "false" ? false : true
            if (boxds) {
              console.log(`\n${title}二次查询`)
              return resolve(page[1])
            } else {
              merge[key].notify = `${title}: 失败, 请尝试开启增强 ⚠️`
              merge[key].fail = 1
            }
          } else {
            merge[key].notify = `${title}: 失败, ${!data ? `需要手动执行` : `不含活动数据`} ⚠️`
            merge[key].fail = 1
          }
        }
        reject()
      } catch (eor) {
        $nobyda.AnError(title, key, eor, response, data)
        reject()
      }
    })
    if (out) setTimeout(reject, out + s)
  }).then(data => {
    disable(key, title, 2)
    if (typeof(data) == "object") return JDUserSign1(s, key, title, encodeURIComponent(`{${data}}`));
    if (typeof(data) == "number") return JDUserSign2(s, key, title, data)
    if (typeof(data) == "string") return JDUserSignPre1(s, key, title, acData, data)
  }, () => disable(key, title, 2))
}

function JDUserSign1(s, key, title, body) {
  return new Promise(resolve => {
    setTimeout(() => {
      const JDUrl = {
        url: 'https://api.m.jd.com/client.action?functionId=userSign',
        headers: {
          Cookie: KEY
        },
        body: `body=${body}&client=wh5`
      };
      $nobyda.post(JDUrl, function(error, response, data) {
        try {
          if (error) {
            throw new Error(error)
          } else {
            const Details = LogDetails ? `response:\n${data}` : '';
            if (data.match(/签到成功/)) {
              console.log(`\n${title}签到成功(1)${Details}`)
              if (data.match(/\"text\":\"\d+京豆\"/)) {
                merge[key].bean = data.match(/\"text\":\"(\d+)京豆\"/)[1]
              }
              merge[key].notify = `${title}: 成功, 明细: ${merge[key].bean || '无'}京豆 🐶`
              merge[key].success = 1
            } else {
              console.log(`\n${title}签到失败(1)${Details}`)
              if (data.match(/(已签到|已领取)/)) {
                merge[key].notify = `${title}: 失败, 原因: 已签过 ⚠️`
              } else if (data.match(/(不存在|已结束|未开始)/)) {
                merge[key].notify = `${title}: 失败, 原因: 活动已结束 ⚠️`
              } else if (data.match(/\"code\":\"?3\"?/)) {
                merge[key].notify = `${title}: 失败, 原因: Cookie失效‼️`
              } else {
                const ng = data.match(/\"(errorMessage|subCodeMsg)\":\"(.+?)\"/)
                merge[key].notify = `${title}: 失败, ${ng?ng[2]:`原因: 未知`} ⚠️`
              }
              merge[key].fail = 1
            }
          }
        } catch (eor) {
          $nobyda.AnError(title, key, eor, response, data)
        } finally {
          resolve()
        }
      })
    }, s)
    if (out) setTimeout(resolve, out + s)
  });
}

async function JDUserSign2(s, key, title, tid) {
  return console.log(`\n${title} >> 可能需要拼图验证, 跳过签到 ⚠️`);
  await new Promise(resolve => {
    $nobyda.get({
      url: `https://jdjoy.jd.com/api/turncard/channel/detail?turnTableId=${tid}&invokeKey=ztmFUCxcPMNyUq0P`,
      headers: {
        Cookie: KEY
      }
    }, function(error, response, data) {
      resolve()
    })
    if (out) setTimeout(resolve, out + s)
  });
  return new Promise(resolve => {
    setTimeout(() => {
      const JDUrl = {
        url: 'https://jdjoy.jd.com/api/turncard/channel/sign?invokeKey=ztmFUCxcPMNyUq0P',
        headers: {
          lkt: '1629984131120',
          lks: 'd7db92cf40ad5a8d54b9da2b561c5f84',
          Cookie: KEY
        },
        body: `turnTableId=${tid}`
      };
      $nobyda.post(JDUrl, function(error, response, data) {
        try {
          if (error) {
            throw new Error(error)
          } else {
            const Details = LogDetails ? `response:\n${data}` : '';
            if (data.match(/\"success\":true/)) {
              console.log(`\n${title}签到成功(2)${Details}`)
              if (data.match(/\"jdBeanQuantity\":\d+/)) {
                merge[key].bean = data.match(/\"jdBeanQuantity\":(\d+)/)[1]
              }
              merge[key].notify = `${title}: 成功, 明细: ${merge[key].bean || '无'}京豆 🐶`
              merge[key].success = 1
            } else {
              const captcha = /请进行验证/.test(data);
              if (data.match(/(已经签到|已经领取)/)) {
                merge[key].notify = `${title}: 失败, 原因: 已签过 ⚠️`
              } else if (data.match(/(不存在|已结束|未开始)/)) {
                merge[key].notify = `${title}: 失败, 原因: 活动已结束 ⚠️`
              } else if (data.match(/(没有登录|B0001)/)) {
                merge[key].notify = `${title}: 失败, 原因: Cookie失效‼️`
              } else if (!captcha) {
                const ng = data.match(/\"(errorMessage|subCodeMsg)\":\"(.+?)\"/)
                merge[key].notify = `${title}: 失败, ${ng?ng[2]:`原因: 未知`} ⚠️`
              }
              if (!captcha) merge[key].fail = 1;
              console.log(`\n${title}签到失败(2)${captcha?`\n需要拼图验证, 跳过通知记录 ⚠️`:``}${Details}`)
            }
          }
        } catch (eor) {
          $nobyda.AnError(title, key, eor, response, data)
        } finally {
          resolve()
        }
      })
    }, 200 + s)
    if (out) setTimeout(resolve, out + s + 200)
  });
}

function JDFlashSale(s) {
  merge.JDFSale = {};
  return new Promise(resolve => {
    if (disable("JDFSale")) return resolve()
    setTimeout(() => {
      const JDPETUrl = {
        url: 'https://api.m.jd.com/client.action?functionId=partitionJdSgin',
        headers: {
          Cookie: KEY
        },
        body: "body=%7B%22version%22%3A%22v2%22%7D&client=apple&clientVersion=9.0.8&openudid=1fce88cd05c42fe2b054e846f11bdf33f016d676&sign=6768e2cf625427615dd89649dd367d41&st=1597248593305&sv=121"
      };
      $nobyda.post(JDPETUrl, async function(error, response, data) {
        try {
          if (error) {
            throw new Error(error)
          } else {
            const Details = LogDetails ? "response:\n" + data : '';
            const cc = JSON.parse(data)
            if (cc.result && cc.result.code == 0) {
              console.log("\n" + "京东商城-闪购签到成功 " + Details)
              merge.JDFSale.bean = cc.result.jdBeanNum || 0
              merge.JDFSale.notify = "京东商城-闪购: 成功, 明细: " + (merge.JDFSale.bean || "无") + "京豆 🐶"
              merge.JDFSale.success = 1
            } else {
              console.log("\n" + "京东商城-闪购签到失败 " + Details)
              if (data.match(/(已签到|已领取|\"2005\")/)) {
                merge.JDFSale.notify = "京东商城-闪购: 失败, 原因: 已签过 ⚠️"
              } else if (data.match(/不存在|已结束|\"2008\"|\"3001\"/)) {
                await FlashSaleDivide(s); //瓜分京豆
                return
              } else if (data.match(/(\"code\":\"3\"|\"1003\")/)) {
                merge.JDFSale.notify = "京东商城-闪购: 失败, 原因: Cookie失效‼️"
              } else {
                const msg = data.match(/\"msg\":\"([\u4e00-\u9fa5].+?)\"/)
                merge.JDFSale.notify = `京东商城-闪购: 失败, ${msg ? msg[1] : `原因: 未知`} ⚠️`
              }
              merge.JDFSale.fail = 1
            }
          }
        } catch (eor) {
          $nobyda.AnError("京东商城-闪购", "JDFSale", eor, response, data)
        } finally {
          resolve()
        }
      })
    }, s)
    if (out) setTimeout(resolve, out + s)
  });
}

function FlashSaleDivide(s) {
  return new Promise(resolve => {
    setTimeout(() => {
      const Url = {
        url: 'https://api.m.jd.com/client.action?functionId=partitionJdShare',
        headers: {
          Cookie: KEY
        },
        body: "body=%7B%22version%22%3A%22v2%22%7D&client=apple&clientVersion=9.0.8&openudid=1fce88cd05c42fe2b054e846f11bdf33f016d676&sign=49baa3b3899b02bbf06cdf41fe191986&st=1597682588351&sv=111"
      };
      $nobyda.post(Url, function(error, response, data) {
        try {
          if (error) {
            throw new Error(error)
          } else {
            const Details = LogDetails ? "response:\n" + data : '';
            const cc = JSON.parse(data)
            if (cc.result.code == 0) {
              merge.JDFSale.success = 1
              merge.JDFSale.bean = cc.result.jdBeanNum || 0
              merge.JDFSale.notify = "京东闪购-瓜分: 成功, 明细: " + (merge.JDFSale.bean || "无") + "京豆 🐶"
              console.log("\n" + "京东闪购-瓜分签到成功 " + Details)
            } else {
              merge.JDFSale.fail = 1
              console.log("\n" + "京东闪购-瓜分签到失败 " + Details)
              if (data.match(/已参与|已领取|\"2006\"/)) {
                merge.JDFSale.notify = "京东闪购-瓜分: 失败, 原因: 已瓜分 ⚠️"
              } else if (data.match(/不存在|已结束|未开始|\"2008\"|\"2012\"/)) {
                merge.JDFSale.notify = "京东闪购-瓜分: 失败, 原因: 活动已结束 ⚠️"
              } else if (data.match(/\"code\":\"1003\"|未获取/)) {
                merge.JDFSale.notify = "京东闪购-瓜分: 失败, 原因: Cookie失效‼️"
              } else {
                const msg = data.match(/\"msg\":\"([\u4e00-\u9fa5].+?)\"/)
                merge.JDFSale.notify = `京东闪购-瓜分: 失败, ${msg ? msg[1] : `原因: 未知`} ⚠️`
              }
            }
          }
        } catch (eor) {
          $nobyda.AnError("京东闪购-瓜分", "JDFSale", eor, response, data)
        } finally {
          resolve()
        }
      })
    }, s)
    if (out) setTimeout(resolve, out + s)
  });
}

function JingDongCash(s) {
  merge.JDCash = {};
  return new Promise(resolve => {
    if (disable("JDCash")) return resolve()
    setTimeout(() => {
      const JDCAUrl = {
        url: 'https://api.m.jd.com/client.action?functionId=ccSignInNew',
        headers: {
          Cookie: KEY
        },
        body: "body=%7B%22pageClickKey%22%3A%22CouponCenter%22%2C%22eid%22%3A%22O5X6JYMZTXIEX4VBCBWEM5PTIZV6HXH7M3AI75EABM5GBZYVQKRGQJ5A2PPO5PSELSRMI72SYF4KTCB4NIU6AZQ3O6C3J7ZVEP3RVDFEBKVN2RER2GTQ%22%2C%22shshshfpb%22%3A%22v1%5C%2FzMYRjEWKgYe%2BUiNwEvaVlrHBQGVwqLx4CsS9PH1s0s0Vs9AWk%2B7vr9KSHh3BQd5NTukznDTZnd75xHzonHnw%3D%3D%22%2C%22childActivityUrl%22%3A%22openapp.jdmobile%253a%252f%252fvirtual%253fparams%253d%257b%255c%2522category%255c%2522%253a%255c%2522jump%255c%2522%252c%255c%2522des%255c%2522%253a%255c%2522couponCenter%255c%2522%257d%22%2C%22monitorSource%22%3A%22cc_sign_ios_index_config%22%7D&client=apple&clientVersion=8.5.0&d_brand=apple&d_model=iPhone8%2C2&openudid=1fce88cd05c42fe2b054e846f11bdf33f016d676&scope=11&screen=1242%2A2208&sign=1cce8f76d53fc6093b45a466e93044da&st=1581084035269&sv=102"
      };
      $nobyda.post(JDCAUrl, function(error, response, data) {
        try {
          if (error) {
            throw new Error(error)
          } else {
            const Details = LogDetails ? "response:\n" + data : '';
            const cc = JSON.parse(data)
            if (cc.busiCode == "0") {
              console.log("\n" + "京东现金-红包签到成功 " + Details)
              merge.JDCash.success = 1
              merge.JDCash.Cash = cc.result.signResult.signData.amount || 0
              merge.JDCash.notify = `京东现金-红包: 成功, 明细: ${merge.JDCash.Cash || `无`}红包 🧧`
            } else {
              console.log("\n" + "京东现金-红包签到失败 " + Details)
              merge.JDCash.fail = 1
              if (data.match(/(\"busiCode\":\"1002\"|完成签到)/)) {
                merge.JDCash.notify = "京东现金-红包: 失败, 原因: 已签过 ⚠️"
              } else if (data.match(/(不存在|已结束)/)) {
                merge.JDCash.notify = "京东现金-红包: 失败, 原因: 活动已结束 ⚠️"
              } else if (data.match(/(\"busiCode\":\"3\"|未登录)/)) {
                merge.JDCash.notify = "京东现金-红包: 失败, 原因: Cookie失效‼️"
              } else {
                const msg = data.split(/\"msg\":\"([\u4e00-\u9fa5].+?)\"/)[1];
                merge.JDCash.notify = `京东现金-红包: 失败, ${msg||`原因: 未知`} ⚠️`
              }
            }
          }
        } catch (eor) {
          $nobyda.AnError("京东现金-红包", "JDCash", eor, response, data)
        } finally {
          resolve()
        }
      })
    }, s)
    if (out) setTimeout(resolve, out + s)
  });
}

function JDMagicCube(s, sign) {
  merge.JDCube = {};
  return new Promise((resolve, reject) => {
    if (disable("JDCube")) return reject()
    const JDUrl = {
      url: `https://api.m.jd.com/client.action?functionId=getNewsInteractionInfo&appid=smfe${sign?`&body=${encodeURIComponent(`{"sign":${sign}}`)}`:``}`,
      headers: {
        Cookie: KEY,
      }
    };
    $nobyda.get(JDUrl, async (error, response, data) => {
      try {
        if (error) throw new Error(error)
        const Details = LogDetails ? "response:\n" + data : '';
        console.log(`\n京东魔方-尝试查询活动(${sign}) ${Details}`)
        if (data.match(/\"interactionId\":\d+/)) {
          resolve({
            id: data.match(/\"interactionId\":(\d+)/)[1],
            sign: sign || null
          })
        } else if (data.match(/配置异常/) && sign) {
          await JDMagicCube(s, sign == 2 ? 1 : null)
          reject()
        } else {
          resolve(null)
        }
      } catch (eor) {
        $nobyda.AnError("京东魔方-查询", "JDCube", eor, response, data)
        reject()
      }
    })
    if (out) setTimeout(reject, out + s)
  }).then(data => {
    return JDMagicCubeSign(s, data)
  }, () => {});
}

function JDMagicCubeSign(s, id) {
  return new Promise(resolve => {
    setTimeout(() => {
      const JDMCUrl = {
        url: `https://api.m.jd.com/client.action?functionId=getNewsInteractionLotteryInfo&appid=smfe${id?`&body=${encodeURIComponent(`{${id.sign?`"sign":${id.sign},`:``}"interactionId":${id.id}}`)}`:``}`,
        headers: {
          Cookie: KEY,
        }
      };
      $nobyda.get(JDMCUrl, function(error, response, data) {
        try {
          if (error) {
            throw new Error(error)
          } else {
            const Details = LogDetails ? "response:\n" + data : '';
            const cc = JSON.parse(data)
            if (data.match(/(\"name\":)/)) {
              console.log("\n" + "京东商城-魔方签到成功 " + Details)
              merge.JDCube.success = 1
              if (data.match(/(\"name\":\"京豆\")/)) {
                merge.JDCube.bean = cc.result.lotteryInfo.quantity || 0
                merge.JDCube.notify = `京东商城-魔方: 成功, 明细: ${merge.JDCube.bean || `无`}京豆 🐶`
              } else {
                merge.JDCube.notify = `京东商城-魔方: 成功, 明细: ${cc.result.lotteryInfo.name || `未知`} 🎉`
              }
            } else {
              console.log("\n" + "京东商城-魔方签到失败 " + Details)
              merge.JDCube.fail = 1
              if (data.match(/(一闪而过|已签到|已领取)/)) {
                merge.JDCube.notify = "京东商城-魔方: 失败, 原因: 无机会 ⚠️"
              } else if (data.match(/(不存在|已结束)/)) {
                merge.JDCube.notify = "京东商城-魔方: 失败, 原因: 活动已结束 ⚠️"
              } else if (data.match(/(\"code\":3)/)) {
                merge.JDCube.notify = "京东商城-魔方: 失败, 原因: Cookie失效‼️"
              } else {
                merge.JDCube.notify = "京东商城-魔方: 失败, 原因: 未知 ⚠️"
              }
            }
          }
        } catch (eor) {
          $nobyda.AnError("京东商城-魔方", "JDCube", eor, response, data)
        } finally {
          resolve()
        }
      })
    }, s)
    if (out) setTimeout(resolve, out + s)
  });
}

function JingDongSubsidy(s) {
  merge.subsidy = {};
  return new Promise(resolve => {
    if (disable("subsidy")) return resolve()
    setTimeout(() => {
      const subsidyUrl = {
        url: 'https://ms.jr.jd.com/gw/generic/uc/h5/m/signIn7',
        headers: {
          Referer: "https://active.jd.com/forever/cashback/index",
          Cookie: KEY
        }
      };
      $nobyda.get(subsidyUrl, function(error, response, data) {
        try {
          if (error) {
            throw new Error(error)
          } else {
            const Details = LogDetails ? "response:\n" + data : '';
            const cc = JSON.parse(data)
            if (cc.resultCode == 0 && cc.resultData.data && cc.resultData.data.thisAmount) {
              console.log("\n" + "京东商城-金贴签到成功 " + Details)
              merge.subsidy.subsidy = cc.resultData.data.thisAmountStr
              merge.subsidy.notify = `京东商城-金贴: 成功, 明细: ${merge.subsidy.subsidy||`无`}金贴 💰`
              merge.subsidy.success = 1
            } else {
              console.log("\n" + "京东商城-金贴签到失败 " + Details)
              merge.subsidy.fail = 1
              if (data.match(/已存在|"thisAmount":0/)) {
                merge.subsidy.notify = "京东商城-金贴: 失败, 原因: 无金贴 ⚠️"
              } else if (data.match(/请先登录/)) {
                merge.subsidy.notify = "京东商城-金贴: 失败, 原因: Cookie失效‼️"
              } else {
                const msg = data.split(/\"msg\":\"([\u4e00-\u9fa5].+?)\"/)[1];
                merge.subsidy.notify = `京东商城-金贴: 失败, ${msg||`原因: 未知`} ⚠️`
              }
            }
          }
        } catch (eor) {
          $nobyda.AnError("京东商城-金贴", "subsidy", eor, response, data)
        } finally {
          resolve()
        }
      })
    }, s)
    if (out) setTimeout(resolve, out + s)
  });
}

function JingRongDoll(s, key, title, code, type, num, award, belong) {
  merge[key] = {};
  return new Promise(resolve => {
    if (disable(key)) return resolve()
    setTimeout(() => {
      const DollUrl = {
        url: "https://nu.jr.jd.com/gw/generic/jrm/h5/m/process",
        headers: {
          Cookie: KEY
        },
        body: `reqData=${encodeURIComponent(`{"actCode":"${code}","type":${type?type:`3`}${code=='F68B2C3E71'?`,"frontParam":{"belong":"${belong}"}`:code==`1DF13833F7`?`,"frontParam":{"channel":"JR","belong":4}`:``}}`)}`
      };
      $nobyda.post(DollUrl, async function(error, response, data) {
        try {
          if (error) {
            throw new Error(error)
          } else {
            var cc = JSON.parse(data)
            const Details = LogDetails ? "response:\n" + data : '';
            if (cc.resultCode == 0) {
              if (cc.resultData.data.businessData != null) {
                console.log(`\n${title}查询成功 ${Details}`)
                if (cc.resultData.data.businessData.pickStatus == 2) {
                  if (data.match(/\"rewardPrice\":\"\d.*?\"/)) {
                    const JRDoll_bean = data.match(/\"rewardPrice\":\"(\d.*?)\"/)[1]
                    const JRDoll_type = data.match(/\"rewardName\":\"金贴奖励\"/) ? true : false
                    await JingRongDoll(s, key, title, code, '4', JRDoll_bean, JRDoll_type)
                  } else {
                    merge[key].success = 1
                    merge[key].notify = `${title}: 成功, 明细: 无奖励 🐶`
                  }
                } else if (code == 'F68B2C3E71' || code == '1DF13833F7') {
                  if (!data.match(/"businessCode":"30\dss?q"/)) {
                    merge[key].success = 1
                    const ct = data.match(/\"count\":\"?(\d.*?)\"?,/)
                    if (code == 'F68B2C3E71' && belong == 'xianjin') {
                      merge[key].Money = ct ? ct[1] > 9 ? `0.${ct[1]}` : `0.0${ct[1]}` : 0
                      merge[key].notify = `${title}: 成功, 明细: ${merge[key].Money||`无`}现金 💰`
                    } else if (code == 'F68B2C3E71' && belong == 'jingdou') {
                      merge[key].bean = ct ? ct[1] : 0;
                      merge[key].notify = `${title}: 成功, 明细: ${merge[key].bean||`无`}京豆 🐶`
                    } else if (code == '1DF13833F7') {
                      merge[key].subsidy = ct ? ct[1] : 0;
                      merge[key].notify = `${title}: 成功, 明细: ${merge[key].subsidy||`无`}金贴 💰`
                    }
                  } else {
                    const es = cc.resultData.data.businessMsg
                    const ep = cc.resultData.data.businessData.businessMsg
                    const tp = data.match(/已领取|300ss?q/) ? `已签过` : `${ep||es||cc.resultMsg||`未知`}`
                    merge[key].notify = `${title}: 失败, 原因: ${tp} ⚠️`
                    merge[key].fail = 1
                  }
                } else {
                  merge[key].notify = `${title}: 失败, 原因: 已签过 ⚠️`;
                  merge[key].fail = 1
                }
              } else if (cc.resultData.data.businessCode == 200) {
                console.log(`\n${title}签到成功 ${Details}`)
                if (!award) {
                  merge[key].bean = num ? num.match(/\d+/)[0] : 0
                } else {
                  merge[key].subsidy = num || 0
                }
                merge[key].success = 1
                merge[key].notify = `${title}: 成功, 明细: ${(award?num:merge[key].bean)||`无`}${award?`金贴 💰`:`京豆 🐶`}`
              } else {
                console.log(`\n${title}领取异常 ${Details}`)
                if (num) console.log(`\n${title} 请尝试手动领取, 预计可得${num}${award?`金贴`:`京豆`}: \nhttps://uf1.jr.jd.com/up/redEnvelopes/index.html?actCode=${code}\n`);
                merge[key].fail = 1;
                merge[key].notify = `${title}: 失败, 原因: 领取异常 ⚠️`;
              }
            } else {
              console.log(`\n${title}签到失败 ${Details}`)
              const redata = typeof(cc.resultData) == 'string' ? cc.resultData : ''
              merge[key].notify = `${title}: 失败, ${cc.resultCode==3?`原因: Cookie失效‼️`:`${redata||'原因: 未知 ⚠️'}`}`
              merge[key].fail = 1;
            }
          }
        } catch (eor) {
          $nobyda.AnError(title, key, eor, response, data)
        } finally {
          resolve()
        }
      })
    }, s)
    if (out) setTimeout(resolve, out + s)
  });
}

function JingDongGetCash(s) {
  merge.JDGetCash = {};
  return new Promise(resolve => {
    if (disable("JDGetCash")) return resolve()
    setTimeout(() => {
      const GetCashUrl = {
        url: 'https://api.m.jd.com/client.action?functionId=cash_sign&body=%7B%22remind%22%3A0%2C%22inviteCode%22%3A%22%22%2C%22type%22%3A0%2C%22breakReward%22%3A0%7D&client=apple&clientVersion=9.0.8&openudid=1fce88cd05c42fe2b054e846f11bdf33f016d676&sign=7e2f8bcec13978a691567257af4fdce9&st=1596954745073&sv=111',
        headers: {
          Cookie: KEY,
        }
      };
      $nobyda.get(GetCashUrl, function(error, response, data) {
        try {
          if (error) {
            throw new Error(error)
          } else {
            const cc = JSON.parse(data);
            const Details = LogDetails ? "response:\n" + data : '';
            if (cc.data.success && cc.data.result) {
              console.log("\n" + "京东商城-现金签到成功 " + Details)
              merge.JDGetCash.success = 1
              merge.JDGetCash.Money = cc.data.result.signCash || 0
              merge.JDGetCash.notify = `京东商城-现金: 成功, 明细: ${cc.data.result.signCash||`无`}现金 💰`
            } else {
              console.log("\n" + "京东商城-现金签到失败 " + Details)
              merge.JDGetCash.fail = 1
              if (data.match(/\"bizCode\":201|已经签过/)) {
                merge.JDGetCash.notify = "京东商城-现金: 失败, 原因: 已签过 ⚠️"
              } else if (data.match(/\"code\":300|退出登录/)) {
                merge.JDGetCash.notify = "京东商城-现金: 失败, 原因: Cookie失效‼️"
              } else {
                merge.JDGetCash.notify = "京东商城-现金: 失败, 原因: 未知 ⚠️"
              }
            }
          }
        } catch (eor) {
          $nobyda.AnError("京东商城-现金", "JDGetCash", eor, response, data)
        } finally {
          resolve()
        }
      })
    }, s)
    if (out) setTimeout(resolve, out + s)
  });
}

function JingDongStore(s) {
  merge.JDGStore = {};
  return new Promise(resolve => {
    if (disable("JDGStore")) return resolve()
    setTimeout(() => {
      $nobyda.get({
        url: 'https://api.m.jd.com/api?appid=jdsupermarket&functionId=smtg_sign&clientVersion=8.0.0&client=m&body=%7B%7D',
        headers: {
          Cookie: KEY,
          Origin: `https://jdsupermarket.jd.com`
        }
      }, (error, response, data) => {
        try {
          if (error) throw new Error(error);
          const cc = JSON.parse(data);
          const Details = LogDetails ? "response:\n" + data : '';
          if (cc.data && cc.data.success === true && cc.data.bizCode === 0) {
            console.log(`\n京东商城-超市签到成功 ${Details}`)
            merge.JDGStore.success = 1
            merge.JDGStore.bean = cc.data.result.jdBeanCount || 0
            merge.JDGStore.notify = `京东商城-超市: 成功, 明细: ${merge.JDGStore.bean||`无`}京豆 🐶`
          } else {
            if (!cc.data) cc.data = {}
            console.log(`\n京东商城-超市签到失败 ${Details}`)
            const tp = cc.data.bizCode == 811 ? `已签过` : cc.data.bizCode == 300 ? `Cookie失效` : `${cc.data.bizMsg||`未知`}`
            merge.JDGStore.notify = `京东商城-超市: 失败, 原因: ${tp}${cc.data.bizCode==300?`‼️`:` ⚠️`}`
            merge.JDGStore.fail = 1
          }
        } catch (eor) {
          $nobyda.AnError("京东商城-超市", "JDGStore", eor, response, data)
        } finally {
          resolve()
        }
      })
    }, s)
    if (out) setTimeout(resolve, out + s)
  });
}

function JDSecKilling(s) { //领券中心
  merge.JDSecKill = {};
  return new Promise((resolve, reject) => {
    if (disable("JDSecKill")) return reject();
    setTimeout(() => {
      $nobyda.post({
        url: 'https://api.m.jd.com/client.action',
        headers: {
          Cookie: KEY,
          Origin: 'https://h5.m.jd.com'
        },
        body: 'functionId=homePageV2&appid=SecKill2020'
      }, (error, response, data) => {
        try {
          if (error) throw new Error(error);
          const Details = LogDetails ? "response:\n" + data : '';
          const cc = JSON.parse(data);
          if (cc.code == 203 || cc.code == 3 || cc.code == 101) {
            merge.JDSecKill.notify = `京东秒杀-红包: 失败, 原因: Cookie失效‼️`;
          } else if (cc.result && cc.result.projectId && cc.resul
Download .txt
gitextract_h2qjq_wb/

├── 52pojie-DailyBonus/
│   └── 52pojie.js
├── BDTieBa-DailyBonus/
│   └── TieBa.js
├── Bahamut/
│   ├── BahamutAnimeAds.js
│   └── BahamutDailyBonus.js
├── Bilibili-DailyBonus/
│   ├── ExchangePoints.js
│   └── Manga.js
├── Ctrip-DailyBonus/
│   └── Ctrip.js
├── Debug/
│   └── Real-time-debug.js
├── Disney/
│   └── DisneyRating.js
├── IPA-Installer/
│   ├── IPA-Installer-JSBox.js
│   ├── IPA-Installer-Pythonista.py
│   └── IPA-Installer.js
├── JD-DailyBonus/
│   └── JD_DailyBonus.js
├── KuaiKan-DailyBonus/
│   └── KKMH.js
├── LICENSE
├── Loon/
│   ├── Loon_Bahamut_ADS.plugin
│   ├── Loon_Daily_bonus.plugin
│   ├── Loon_GetCookie.plugin
│   ├── Loon_Google_CAPTCHA.plugin
│   ├── Loon_IPA_Installer.plugin
│   ├── Loon_TF_Account.plugin
│   └── Loon_TF_Download.plugin
├── NobyDa_BoxJs.json
├── QuantumultX/
│   ├── AdRule.list
│   ├── AdRuleTest.list
│   ├── Bilibili.list
│   ├── DisneyRating.snippet
│   ├── File/
│   │   ├── 91.js
│   │   ├── Wechat.js
│   │   ├── Zymh.js
│   │   ├── vsco.js
│   │   ├── wnyd.js
│   │   └── xjsp.js
│   ├── IPA-Installer.snippet
│   ├── Js.conf
│   ├── Rewrite_lhie1.conf
│   ├── Snippet/
│   │   ├── BiliComicCookie.snippet
│   │   ├── CtripAuth.snippet
│   │   ├── GoogleCAPTCHA.snippet
│   │   ├── KuaiKanCookie.snippet
│   │   ├── TieBaCookie.snippet
│   │   └── iQiYiCookie.snippet
│   └── TestFlightDownload.conf
├── README.md
├── Rule-Storage/
│   ├── Include-Domain.txt
│   └── Rule-Storage.js
├── Shortcuts/
│   └── PolicySwitch.js
├── Stash/
│   └── IPA-Installer.stoverride
├── Sub-store-parser/
│   └── DataQuery.js
├── Surge/
│   ├── AdRule.list
│   ├── AdRuleTest.list
│   ├── Apple.list
│   ├── Bilibili.list
│   ├── Download.list
│   ├── JS/
│   │   ├── BaiduCloud.js
│   │   ├── Bili_Auto_Regions.js
│   │   ├── CamScanner.js
│   │   ├── Google_CAPTCHA.js
│   │   ├── Kuwo.js
│   │   ├── MIX.js
│   │   ├── NiChi.js
│   │   ├── PicsArt.js
│   │   ├── Polarr.js
│   │   ├── Super.js
│   │   ├── VUE.js
│   │   ├── Wps.js
│   │   ├── jibjab.js
│   │   ├── luqi.js
│   │   └── vivavideo.js
│   ├── Module/
│   │   ├── BahamutAnimeAds.sgmodule
│   │   ├── BahamutDailyBonus.sgmodule
│   │   ├── BiliComicsDailyBonus.sgmodule
│   │   ├── BiliComicsExchangePoints.sgmodule
│   │   ├── CtripDailyBonus.sgmodule
│   │   ├── DisneyRating.sgmodule
│   │   ├── GetCookie.sgmodule
│   │   ├── GoogleCAPTCHA.sgmodule
│   │   ├── HuiJuDongManAds.sgmodule
│   │   ├── IPA_install.sgmodule
│   │   ├── KuaiKanComicsDailyBonus.sgmodule
│   │   ├── NewBing.sgmodule
│   │   ├── RewriteRules.sgmodule
│   │   ├── TestFlightAccount.sgmodule
│   │   ├── TestFlightDownload.sgmodule
│   │   ├── TieBaDailyBonus.sgmodule
│   │   └── iQIYIDailyBonus.sgmodule
│   └── WeChat.list
├── TestFlight/
│   └── TestFlightAccount.js
├── Time-based-One-Time-Password/
│   └── README.md
└── iQIYI-DailyBonus/
    └── iQIYI.js
Download .txt
SYMBOL INDEX (152 symbols across 21 files)

FILE: 52pojie-DailyBonus/52pojie.js
  function GetCookie (line 112) | function GetCookie() {
  function BarkNotify (line 130) | async function BarkNotify(c, k, t, b) { for (let i = 0; i < 3; i++) { co...
  function ENV (line 133) | function ENV() { const e = "function" == typeof require && "undefined" !...
  function HTTP (line 133) | function HTTP(e = { baseURL: "" }) { function t(t, a) { a = "string" == ...
  function API (line 133) | function API(e = "untitled", t = !1) { const { isQX: s, isLoon: o, isSur...

FILE: BDTieBa-DailyBonus/TieBa.js
  function signTieBa (line 72) | function signTieBa() {
  function signBar (line 108) | function signBar(bar, tbs) {
  function signBars (line 154) | function signBars(bars, tbs, index) {
  function checkIsAllProcessed (line 207) | function checkIsAllProcessed() {
  function GetCookie (line 231) | function GetCookie() {
  function nobyda (line 246) | function nobyda() {

FILE: Bahamut/BahamutAnimeAds.js
  function runs (line 21) | async function runs() {
  function adURL (line 40) | function adURL(str) {
  function playURL (line 49) | function playURL() {
  function get (line 58) | function get(options, callback) {

FILE: Bahamut/BahamutDailyBonus.js
  function BahamutLogin (line 79) | async function BahamutLogin(retry = 3, interval = 1000) { //登录函数,拿到Set-C...
  function BahamutSign (line 119) | function BahamutSign() { //查询巴哈姆特签到Token
  function StartSignBahamut (line 139) | function StartSignBahamut(token) { //巴哈姆特签到
  function StartAdsBonus (line 163) | function StartAdsBonus(token, type) {
  function BahamutGuildSign (line 194) | function BahamutGuildSign() { //巴哈姆特查询公会列表
  function StartSignGuild (line 228) | function StartSignGuild(v) { //巴哈姆特公会签到
  function BahamutAnswer (line 256) | function BahamutAnswer() { //动画疯答题
  function GetAanswerArticles (line 288) | function GetAanswerArticles() { // 从blackxblue的小屋查询含答案的文章ID
  function StartSearchAnswers (line 307) | function StartSearchAnswers(id) { //获取文章内答案
  function StartBahamutAnswer (line 325) | function StartBahamutAnswer(answer, token) { //动画疯答题
  function BarkNotify (line 352) | async function BarkNotify(c,k,t,b){for(let i=0;i<3;i++){console.log(`🔷Ba...
  function Env (line 355) | function Env(t,e){class s{constructor(t){this.env=t}send(t,e="GET"){t="s...
  function TOTP (line 358) | function TOTP(token){function t(e,a,d){var g=0,c=[],b=0,f,k,l,h,m,w,n,y,...

FILE: Bilibili-DailyBonus/ExchangePoints.js
  function ExchangeForAccount (line 102) | async function ExchangeForAccount(account, productName, productNum, exch...
  function GetUserPoint (line 162) | function GetUserPoint(account) {
  function ListProduct (line 190) | function ListProduct(account) {
  function StartExchange (line 217) | function StartExchange(account, product, num, attempt) {
  function getTodayFlag (line 251) | function getTodayFlag() {
  function argsList (line 259) | function argsList(data) {
  function BarkNotify (line 269) | async function BarkNotify(c, k, t, b) { for (let i = 0; i < 3; i++) { c....
  function Env (line 272) | function Env(e, t) { class s { constructor(e) { this.env = e } send(e, t...

FILE: Bilibili-DailyBonus/Manga.js
  function Checkin (line 79) | function Checkin(key) {
  function GetAuth (line 110) | function GetAuth(raw, data) {
  function formatHeaders (line 135) | function formatHeaders(h) {
  function BarkNotify (line 140) | async function BarkNotify(c, k, t, b) { for (let i = 0; i < 3; i++) { c....
  function Env (line 143) | function Env(e, t) { class s { constructor(e) { this.env = e } send(e, t...

FILE: Ctrip-DailyBonus/Ctrip.js
  function Checkin (line 68) | function Checkin(key) {
  function Points (line 98) | function Points(key) {
  function GetAuth (line 122) | function GetAuth(body, data) {
  function BarkNotify (line 137) | async function BarkNotify(c, k, t, b) { for (let i = 0; i < 3; i++) { c....
  function Env (line 140) | function Env(t, e) { class s { constructor(t) { this.env = t } send(t, e...

FILE: Debug/Real-time-debug.js
  function nobyda (line 29) | function nobyda() {

FILE: Disney/DisneyRating.js
  function requestDoubanRating (line 64) | function requestDoubanRating(imdbId) {
  function requestIMDbRating (line 85) | function requestIMDbRating(title, year, type) {
  function updateIMDbApikey (line 120) | function updateIMDbApikey() {
  function get_IMDb_message (line 127) | function get_IMDb_message(data) {
  function get_douban_rating_message (line 157) | function get_douban_rating_message(data) {
  function get_country_message (line 166) | function get_country_message(data) {
  function IMDbApikeyList (line 180) | function IMDbApikeyList() {
  function countryEmoji (line 196) | function countryEmoji(name) { const emojiMap = { "Chequered": "🏁", "Tria...
  function Tool (line 198) | function Tool() {

FILE: IPA-Installer/IPA-Installer-JSBox.js
  function startServer (line 48) | function startServer(port) {
  function fileCheck (line 58) | function fileCheck(data) {
  function install (line 70) | function install(fileName, file) {
  function delayClose (line 133) | function delayClose(time) {

FILE: IPA-Installer/IPA-Installer-Pythonista.py
  function startServer (line 16) | def startServer(port):
  function start (line 25) | def start(port):
  function stop (line 36) | def stop():
  function main (line 40) | def main():

FILE: IPA-Installer/IPA-Installer.js
  function ipaSize (line 83) | function ipaSize(url) {
  function urlArgs (line 92) | function urlArgs(str) {
  function compatible_tool (line 100) | function compatible_tool() {

FILE: JD-DailyBonus/JD_DailyBonus.js
  function all (line 128) | async function all(cookie, jrBody) {
  function notify (line 242) | function notify() {
  function JingDongBean (line 351) | function JingDongBean(s) {
  function JingDongTurn (line 454) | function JingDongTurn(s) {
  function JingRongSteel (line 518) | function JingRongSteel(s, body) {
  function JingDongShake (line 569) | function JingDongShake(s) {
  function JDUserSignPre (line 633) | function JDUserSignPre(s, key, title, ac) {
  function JDUserSignPre1 (line 642) | function JDUserSignPre1(s, key, title, acData, ask) {
  function JDUserSignPre2 (line 725) | function JDUserSignPre2(s, key, title, acData) {
  function JDUserSign1 (line 782) | function JDUserSign1(s, key, title, body) {
  function JDUserSign2 (line 831) | async function JDUserSign2(s, key, title, tid) {
  function JDFlashSale (line 895) | function JDFlashSale(s) {
  function FlashSaleDivide (line 946) | function FlashSaleDivide(s) {
  function JingDongCash (line 994) | function JingDongCash(s) {
  function JDMagicCube (line 1044) | function JDMagicCube(s, sign) {
  function JDMagicCubeSign (line 1081) | function JDMagicCubeSign(s, id) {
  function JingDongSubsidy (line 1131) | function JingDongSubsidy(s) {
  function JingRongDoll (line 1179) | function JingRongDoll(s, key, title, code, type, num, award, belong) {
  function JingDongGetCash (line 1268) | function JingDongGetCash(s) {
  function JingDongStore (line 1314) | function JingDongStore(s) {
  function JDSecKilling (line 1353) | function JDSecKilling(s) { //领券中心
  function TotalSteel (line 1426) | function TotalSteel() {
  function TotalBean (line 1456) | function TotalBean() {
  function TotalCash (line 1490) | function TotalCash() {
  function TotalSubsidy (line 1521) | function TotalSubsidy() {
  function TotalMoney (line 1552) | function TotalMoney() {
  function disable (line 1582) | function disable(Val, name, way) {
  function Wait (line 1615) | function Wait(readDelay, ini) {
  function CookieMove (line 1630) | function CookieMove(oldCk1, oldCk2, oldKey1, oldKey2, newKey) {
  function checkFormat (line 1646) | function checkFormat(value) { //check format and delete duplicates
  function CookieUpdate (line 1667) | function CookieUpdate(oldValue, newValue, path = 'cookie') {
  function GetCookie (line 1702) | function GetCookie() {
  function nobyda (line 1736) | function nobyda() {

FILE: KuaiKan-DailyBonus/KKMH.js
  function Checkin (line 85) | function Checkin() {
  function GiftPack (line 135) | function GiftPack(type) {
  function GetCookie (line 174) | function GetCookie() {
  function BarkNotify (line 201) | async function BarkNotify(c,k,t,b){for(let i=0;i<3;i++){console.log(`🔷Ba...
  function Env (line 204) | function Env(t,e){class s{constructor(t){this.env=t}send(t,e="GET"){t="s...

FILE: Rule-Storage/Rule-Storage.js
  function saveDecision (line 51) | function saveDecision(host_name, content = {}) {
  function evalRules (line 82) | function evalRules(host_name, rule_list) {
  function formatRules (line 102) | function formatRules(list, type) {
  function eTLD (line 111) | async function eTLD(content = {}) {
  function shortenDomain (line 138) | function shortenDomain(host_name, eTLD_list) {
  function argsList (line 152) | function argsList(data) {

FILE: Shortcuts/PolicySwitch.js
  function nobyda (line 38) | function nobyda() {

FILE: Sub-store-parser/DataQuery.js
  function operator (line 18) | async function operator(proxies, client) {
  function parseInfo (line 51) | function parseInfo(resp) { //reference to https://github.com/KOP-XIAO/Qu...

FILE: Surge/JS/Bili_Auto_Regions.js
  function SwitchRegion (line 78) | async function SwitchRegion(title, url, body) {
  function EnvInfo (line 133) | function EnvInfo() {
  function QueryRating (line 156) | async function QueryRating(body, play) {
  function ExtractMovieInfo (line 191) | function ExtractMovieInfo(ret, fv) {
  function GetRawInfo (line 216) | function GetRawInfo(t) {
  function nobyda (line 253) | function nobyda() {
  function zhHans (line 366) | function zhHans() {

FILE: Surge/JS/Google_CAPTCHA.js
  function NobyDa_Tools (line 75) | function NobyDa_Tools() {

FILE: TestFlight/TestFlightAccount.js
  function runs (line 72) | async function runs() {
  function SaveAccount (line 90) | function SaveAccount(id, part, o) {
  function formatHeaders (line 111) | function formatHeaders(h) {
  function formatArgument (line 115) | function formatArgument(s) {
  function ChangeHeaders (line 119) | function ChangeHeaders(id) {
  function ChangeBody (line 141) | function ChangeBody(resp) {
  function QueryRequest (line 182) | function QueryRequest(o) {
  function ExternalAccount (line 222) | function ExternalAccount(key) {
  function ShareAccount (line 241) | function ShareAccount(appID) {
  function letterEncode (line 280) | function letterEncode(e) {
  function letterDecode (line 295) | function letterDecode(e) {
  function ENV (line 311) | function ENV() { const a = "function" == typeof require && "undefined" !...
  function HTTP (line 311) | function HTTP(a = { baseURL: "" }) { function b(b, j) { j = "string" == ...
  function API (line 311) | function API(a = "untitled", b = !1) { const { isQX: c, isLoon: d, isSur...

FILE: iQIYI-DailyBonus/iQIYI.js
  constant P00001 (line 79) | let P00001, P00003, DFP
  constant P00003 (line 79) | let P00001, P00003, DFP
  constant DFP (line 79) | let P00001, P00003, DFP
  function login (line 133) | function login() {
  function Checkin (line 155) | function Checkin() {
  function Lottery (line 238) | function Lottery(s) {
  function getTaskList (line 269) | function getTaskList(task) {
  function joinTask (line 300) | function joinTask(task) {
  function notifyTask (line 317) | function notifyTask(task) {
  function getTaskRewards (line 334) | function getTaskRewards(task) {
  function GetCookie (line 357) | function GetCookie() {
  function BarkNotify (line 386) | async function BarkNotify(c, k, t, b, p) { for (let i = 0; i < 3; i++) {...
  function nobyda (line 388) | function nobyda() {
  function k (line 528) | function k(e, t) {
  function md5 (line 545) | function md5(string) { function RotateLeft(lValue, iShiftBits) { return ...
  function w (line 547) | function w() {
Condensed preview — 90 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,238K chars).
[
  {
    "path": "52pojie-DailyBonus/52pojie.js",
    "chars": 10026,
    "preview": "/*\n吾爱破解签到脚本\n\n更新时间: 2022.6.18\n脚本兼容: QuantumultX, Surge, Loon, Node.js\n电报频道: @NobyDa\n问题反馈: @NobyDa_bot\n\n******************"
  },
  {
    "path": "BDTieBa-DailyBonus/TieBa.js",
    "chars": 9476,
    "preview": "/*********************************\n百度贴吧签到脚本\n\n脚本原作者: @sazs34\n平台兼容: QuantumultX, Surge, Loon\n更新日期: 2024/06/01\n\n获取Cookie说明:"
  },
  {
    "path": "Bahamut/BahamutAnimeAds.js",
    "chars": 1707,
    "preview": "/************************\n\n动画疯,屏蔽播放广告脚本 (黑屏25秒自动播放)\n由于动画疯强制验证观看广告时间,无法实现真正意义上的跳过广告。\n\nSurge(4.11+)模块:\nhttps://raw.githubu"
  },
  {
    "path": "Bahamut/BahamutDailyBonus.js",
    "chars": 28770,
    "preview": "/************************\n\n巴哈姆特签到脚本\n包含主站签到、公会签到、动画疯答题等\n\n脚本兼容: Surge、QuantumultX、Loon、Shadowrocket、Node.js\n适配过程: https://"
  },
  {
    "path": "Bilibili-DailyBonus/ExchangePoints.js",
    "chars": 22979,
    "preview": "/********************************\n哔哩哔哩漫画积分商城自动抢购脚本\n\n默认兑换积分商城中的\"【超特惠】限量-0点秒杀\"\n兑换数量为用户积分可兑换的最大值 (可于BoxJs内修改)\n默认执行时间为:每周日、每"
  },
  {
    "path": "Bilibili-DailyBonus/Manga.js",
    "chars": 19103,
    "preview": "/********************************\n哔哩哔哩漫画签到脚本\n\n支持多账号,支持Node.js,支持Bark推送。\n打开哔哩哔哩/漫画后 (AppStore中国区),单击\"我的\", 即可获取cookie \n\n脚本"
  },
  {
    "path": "Ctrip-DailyBonus/Ctrip.js",
    "chars": 19254,
    "preview": "/********************************\n携程旅行签到脚本\n\n支持多账号,支持Node.js,支持Bark推送。\n配置脚本后登陆\"携程旅行\"微信小程序或\"携程网页版\"(https://m.ctrip.com/)即可"
  },
  {
    "path": "Debug/Real-time-debug.js",
    "chars": 1723,
    "preview": "/*\n * LAN script real-time debug\n *\n * PC: Use \"Live Server\" plugin in VSCode to create a LAN backend\n * APP: After back"
  },
  {
    "path": "Disney/DisneyRating.js",
    "chars": 15382,
    "preview": "/*********************************\nDisney+ 显示IMDb评分 / 烂番茄评分 / 豆瓣评分\n\n脚本作者: @NobyDa \n脚本兼容: Surge、QuantumultX、Loon\n系统兼容: iO"
  },
  {
    "path": "IPA-Installer/IPA-Installer-JSBox.js",
    "chars": 3398,
    "preview": "/*\n * IPA-installer JSBox script. This script is not available stand alone, checkout the demo from TG channel @NobyDa\n *"
  },
  {
    "path": "IPA-Installer/IPA-Installer-Pythonista.py",
    "chars": 2071,
    "preview": "# IPA-installer pythonista script. This script is not available stand alone, checkout the demo from TG channel @NobyDa\n#"
  },
  {
    "path": "IPA-Installer/IPA-Installer.js",
    "chars": 4800,
    "preview": "/*\n * iOS IPA应用辅助安装脚本.\n * \n * 兼容: QuantumultX、Surge5,Loon、Shadowrocket、Stash\n * 作者: @NobyDa\n * \n * 快捷指令 + Shu配合安装:\n * 导入"
  },
  {
    "path": "JD-DailyBonus/JD_DailyBonus.js",
    "chars": 80684,
    "preview": "/*************************\n\n京东多合一签到脚本\n\n更新时间: 2021.09.09 20:20 v2.1.3\n有效接口: 20+\n脚本兼容: QuantumultX, Surge, Loon, JSBox, No"
  },
  {
    "path": "KuaiKan-DailyBonus/KKMH.js",
    "chars": 14869,
    "preview": "/*\n快看漫画签到脚本\n\n更新时间: 2022.06.18\n脚本兼容: QuantumultX, Surge4, Loon, Node.js\n电报频道: @NobyDa\n问题反馈: @NobyDa_bot\n\n获取Cookie说明:\n打开快看"
  },
  {
    "path": "LICENSE",
    "chars": 35149,
    "preview": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free "
  },
  {
    "path": "Loon/Loon_Bahamut_ADS.plugin",
    "chars": 495,
    "preview": "#!name= 巴哈姆特动画疯\n#!desc= 该Loon插件用以屏蔽动画疯播放广告 (由于强制验证观看广告时间,故以黑屏25秒的方式屏蔽)\n#!author= NobyDa\n#!homepage= https://github.com/N"
  },
  {
    "path": "Loon/Loon_Daily_bonus.plugin",
    "chars": 3272,
    "preview": "#!name= 🐻 NobyDa签到脚本\n#!desc= 包括哔哩漫画、贴吧、快看、爱奇艺,携程旅行、巴哈姆特。部分脚本获取Cookie方法请看脚本注释。注意,该Loon链接目前仅适用于\"订阅脚本\",非\"插件\"。添加脚本订阅后请按需启用脚本"
  },
  {
    "path": "Loon/Loon_GetCookie.plugin",
    "chars": 1342,
    "preview": "#!name= NobyDa签到脚本Cookie获取\n#!desc= 包括哔哩漫画、贴吧、快看、爱奇艺,携程旅行。获取方法请看脚本注释;建议使用后手动将该插件禁用, 以避免无意义的MITM。\n#!author= NobyDa\n#!homep"
  },
  {
    "path": "Loon/Loon_Google_CAPTCHA.plugin",
    "chars": 565,
    "preview": "#!name = Google人机验证\n#!desc = Google搜索内容时并发使用多个策略/策略组,以避免可能出现的人机验证。注意:需要在插件参数填写策略/策略组名的正则表达式。\n#!author = NobyDa\n#!input ="
  },
  {
    "path": "Loon/Loon_IPA_Installer.plugin",
    "chars": 502,
    "preview": "#!name=IPA应用辅助安装器\n#!desc=该模块可在iOS端辅助安装商店版或已签名IPA(需使用快捷指令 + Shu/Jsbox/pythonista), 查看脚本注释以了解具体方法; 安装演示可查看TG频道 @NobyDa\n#!a"
  },
  {
    "path": "Loon/Loon_TF_Account.plugin",
    "chars": 548,
    "preview": "#!name=TestFlight账户管理\n#!desc=自动存储/合并多个TestFlight账户列表, 并可分享/导出TestFlight APP.\n#!author= NobyDa\n#!homepage= https://github"
  },
  {
    "path": "Loon/Loon_TF_Download.plugin",
    "chars": 551,
    "preview": "#!name= TestFlight区域限制解除\n#!desc= 该Loon插件用以解决更新TestFlight App时, 提示\"APP不可用\"问题.\n#!author= NobyDa\n#!homepage= https://github"
  },
  {
    "path": "NobyDa_BoxJs.json",
    "chars": 30426,
    "preview": "{\n    \"id\": \"NobyDa.app.sub\",\n    \"name\": \"NobyDa 脚本订阅\",\n    \"description\": \"该订阅兼容BoxJs以及QX Gallery\",\n    \"author\": \"@No"
  },
  {
    "path": "QuantumultX/AdRule.list",
    "chars": 291093,
    "preview": "# Update > 2023/12/23\nDOMAIN-SUFFIX,api-access.pangolin-sdk-toutiao-b.com,REJECT\nDOMAIN-SUFFIX,apoll.m.taobao.com,REJECT"
  },
  {
    "path": "QuantumultX/AdRuleTest.list",
    "chars": 47306,
    "preview": "#Update 2021.1.30 Self-use for testing only\nDOMAIN-SUFFIX,admaster.com,REJECT\nDOMAIN-SUFFIX,0z5jn.cn,REJECT\nDOMAIN-SUFFI"
  },
  {
    "path": "QuantumultX/Bilibili.list",
    "chars": 419,
    "preview": "# 该规则集用于配合bilibili自动地区脚本使用。\nhost,api.biliapi.com,Bilibili\nhost,api.biliapi.net,Bilibili\nhost,api.bilibili.com,Bilibili\nh"
  },
  {
    "path": "QuantumultX/DisneyRating.snippet",
    "chars": 343,
    "preview": "# Disney+剧集页显示IMDb / 烂番茄 / 豆瓣评分\n# https://raw.githubusercontent.com/NobyDa/Script/master/QuantumultX/DisneyRating.snippe"
  },
  {
    "path": "QuantumultX/File/91.js",
    "chars": 4242,
    "preview": "/*\n91短视频 解锁部分限制\nhttp://download.91porn.love/\n\n***************************\nQuantumultX:\n\n[rewrite_local]\n^https?:\\/\\/.+?\\"
  },
  {
    "path": "QuantumultX/File/Wechat.js",
    "chars": 723,
    "preview": "/*\n微信 去除公众号文章底部广告\n\n***************************\nQuantumultX:\n\n[rewrite_local]\n^https?:\\/\\/mp\\.weixin\\.qq\\.com\\/mp\\/getapp"
  },
  {
    "path": "QuantumultX/File/Zymh.js",
    "chars": 905,
    "preview": "/*\n解锁知音漫客付费章节 (需登录)\n\n***************************\nQuantumultX:\n\n[rewrite_local]\n^https:\\/\\/apigate\\.kaimanhua\\.com\\/(zymk"
  },
  {
    "path": "QuantumultX/File/vsco.js",
    "chars": 2109,
    "preview": "/********************************\nMembership unlock for VSCO & 1Blocker & HTTPBot\nPlease note that you may need to reins"
  },
  {
    "path": "QuantumultX/File/wnyd.js",
    "chars": 727,
    "preview": "/*\n网易蜗牛读书 解锁特权\n原作者: yxiaocai & JO2EY\n\n***************************\nQuantumultX:\n\n[rewrite_local]\n^https?:\\/\\/p\\.du\\.163\\."
  },
  {
    "path": "QuantumultX/File/xjsp.js",
    "chars": 1861,
    "preview": "/*\n香蕉视频 解锁部分观看限制\n官网: https://www.aa2.app\n\n***************************\nQuantumultX:\n\n[rewrite_local]\n^https?:\\/\\/.+?\\.(pi"
  },
  {
    "path": "QuantumultX/IPA-Installer.snippet",
    "chars": 317,
    "preview": "# 该文件为 \"IPA应用辅助安装脚本\" QuantumultX远端重写资源.\n# 该资源可在iOS端辅助安装商店版或已签名IPA(需使用快捷指令 + Shu/Jsbox/pythonista), 查看脚本注释以了解具体方法; 安装演示可查"
  },
  {
    "path": "QuantumultX/Js.conf",
    "chars": 4410,
    "preview": "hostname = api.weibo.cn, mapi.weibo.com, *.uve.weibo.com, mp.weixin.qq.com, api.zhihu.com, p.du.163.com, apigate.zymk.cn"
  },
  {
    "path": "QuantumultX/Rewrite_lhie1.conf",
    "chars": 33703,
    "preview": "# Update-2021.8.26\n# This rewrite rule does not include ConnersHua\nhostname = api.cognitive.microsofttranslator.com,offl"
  },
  {
    "path": "QuantumultX/Snippet/BiliComicCookie.snippet",
    "chars": 383,
    "preview": "# 该 QuantumultX 远程重写配置片段用于获取\"哔哩哔哩漫画签到\"账号Cookie\n# 配置脚本后打开哔哩哔哩漫画(AppStore中国区),点击\"我的\"可完成获取\n# https://raw.githubusercontent."
  },
  {
    "path": "QuantumultX/Snippet/CtripAuth.snippet",
    "chars": 391,
    "preview": "# 该 QuantumultX 远程重写配置片段用于获取\"携程签到\"账号授权\n# 配置脚本后登陆\"携程旅行\"微信小程序或\"携程网页版\"(https://m.ctrip.com/)即可完成获取\n# https://raw.githubuser"
  },
  {
    "path": "QuantumultX/Snippet/GoogleCAPTCHA.snippet",
    "chars": 492,
    "preview": "# QuantumultX 远程重写配置片段\n\n# Google搜索人机验证解决方案\n# Google搜索内容时并发使用多个代理策略、策略组尝试搜索内容,并返回最优结果。具体细节可查看脚本注释。\n\n# 脚本:https://raw.gith"
  },
  {
    "path": "QuantumultX/Snippet/KuaiKanCookie.snippet",
    "chars": 364,
    "preview": "# 该 QuantumultX 远程重写配置片段用于获取\"快看漫画签到\"账号Cookie\n# 配置脚本后打开快看漫画(AppStore中国区),点击\"我的\"可完成获取\n# https://raw.githubusercontent.com/"
  },
  {
    "path": "QuantumultX/Snippet/TieBaCookie.snippet",
    "chars": 395,
    "preview": "# 该 QuantumultX 远程重写配置片段用于获取\"百度贴吧签到\"账号Cookie\n# 配置脚本后打开百度贴吧App(AppStore中国区, 非内部版),点击\"我的\"可完成获取\n# https://raw.githubusercon"
  },
  {
    "path": "QuantumultX/Snippet/iQiYiCookie.snippet",
    "chars": 389,
    "preview": "# 该 QuantumultX 远程重写配置片段用于获取\"爱奇艺签到\"账号Cookie\n# 配置脚本后 Safari 浏览器打开 https://m.iqiyi.com/user.html 使用密码登录可完成获取\n# https://raw"
  },
  {
    "path": "QuantumultX/TestFlightDownload.conf",
    "chars": 281,
    "preview": "# 该订阅仅适用于QuantumultX, 用于更新TestFlight App时, 提示\"APP不可用\"问题. 解除区域限制.\n\nhostname = testflight.apple.com\n\n^https?:\\/\\/testfligh"
  },
  {
    "path": "README.md",
    "chars": 17576,
    "preview": "## TOC\n- [TOC](#toc)\n- [Script Overview](#script-overview)\n  - [Daily-Bonus Script](#daily-bonus-script)\n  - [Functional"
  },
  {
    "path": "Rule-Storage/Include-Domain.txt",
    "chars": 467,
    "preview": "DOMAIN-KEYWORD,.a\nDOMAIN-KEYWORD,.b\nDOMAIN-KEYWORD,.c\nDOMAIN-KEYWORD,.d\nDOMAIN-KEYWORD,.e\nDOMAIN-KEYWORD,.f\nDOMAIN-KEYWO"
  },
  {
    "path": "Rule-Storage/Rule-Storage.js",
    "chars": 6501,
    "preview": "/*\nSurge规则自动生成脚本\n更新时间:2024/08/11\n\n需按照博客内教程配合使用:\nhttps://nobyda.github.io/2024/02/24/Surge_Rule_Storage\n\n*/\n\nconst args ="
  },
  {
    "path": "Shortcuts/PolicySwitch.js",
    "chars": 3419,
    "preview": "/*\n捷径策略切换脚本, 该脚本需与捷径配合使用.\n\n脚本兼容: Surge4.7, QuanX1.0.22(545+), Loon2.1.10(290+)\n捷径地址: https://www.icloud.com/shortcuts/0f"
  },
  {
    "path": "Stash/IPA-Installer.stoverride",
    "chars": 449,
    "preview": "name: IPA应用辅助安装器\ndesc: 该模块可在iOS端辅助安装商店版或已签名IPA(需使用快捷指令 + Shu/Jsbox/pythonista), 查看脚本注释以了解具体方法; 安装演示可查看TG频道 @NobyDa\n\nhttp"
  },
  {
    "path": "Sub-store-parser/DataQuery.js",
    "chars": 3244,
    "preview": "/******************************\n\nSub-Store外置流量查询脚本\n\n该脚本基于Sub-Store, 可解决APP使用Sub-Store链接后, 没有流量通知的问题. 使用前需确认您的机场订阅是否支持流量信"
  },
  {
    "path": "Surge/AdRule.list",
    "chars": 257021,
    "preview": "# Update > 2023/12/23\nDOMAIN-SUFFIX,api-access.pangolin-sdk-toutiao-b.com\nDOMAIN-SUFFIX,mcupdate.gstarcad.com\nDOMAIN-SUF"
  },
  {
    "path": "Surge/AdRuleTest.list",
    "chars": 34760,
    "preview": "#Update 2021.1.30 Self-use for testing only.\nDOMAIN-SUFFIX,admaster.com\nDOMAIN-SUFFIX,0z5jn.cn\nDOMAIN-SUFFIX,114so.cn\nDO"
  },
  {
    "path": "Surge/Apple.list",
    "chars": 2069,
    "preview": "# This ruleset includes 99% of apple inc network connections(global). If there are any missing, please PR.\n\nDOMAIN-SUFFI"
  },
  {
    "path": "Surge/Bilibili.list",
    "chars": 344,
    "preview": "# 该规则集用于配合bilibili自动地区脚本使用。\nDOMAIN,api.biliapi.com\nDOMAIN,api.biliapi.net\nDOMAIN,api.bilibili.com\nDOMAIN,app.biliapi.com"
  },
  {
    "path": "Surge/Download.list",
    "chars": 508,
    "preview": "# Mac Download\nPROCESS-NAME,aria2c\nPROCESS-NAME,fdm\nPROCESS-NAME,Folx\nPROCESS-NAME,NetTransport\nPROCESS-NAME,Thunder\nPRO"
  },
  {
    "path": "Surge/JS/BaiduCloud.js",
    "chars": 1736,
    "preview": "/*\n百度网盘 解锁在线视频倍率/清晰度\n\n***************************\nQuantumultX:\n\n[rewrite_local]\nhttps:\\/\\/pan\\.baidu\\.com\\/rest\\/\\d\\.\\d\\"
  },
  {
    "path": "Surge/JS/Bili_Auto_Regions.js",
    "chars": 15517,
    "preview": "/**************************\n\n哔哩哔哩(白图标外区版), 港澳台番剧自动切换地区 & 显示豆瓣评分\n\n如需禁用豆瓣评分或策略通知, 可前往BoxJs设置.\nBoxJs订阅地址: https://raw.githu"
  },
  {
    "path": "Surge/JS/CamScanner.js",
    "chars": 1038,
    "preview": "/*\nCamScanner 解锁部分高级特权\n\n***************************\nQuantumult X:\n\n[rewrite_local]\n^https:\\/\\/(api|api-cs)\\.intsig\\.net\\"
  },
  {
    "path": "Surge/JS/Google_CAPTCHA.js",
    "chars": 6176,
    "preview": "/********************************\nGoogle搜索人机验证解决方案\n搜索内容时遇到人机验证立即并发使用多个代理策略、策略组尝试搜索内容,并返回最优结果。\n\n脚本作者:@NobyDa\n更新时间:2024/05"
  },
  {
    "path": "Surge/JS/Kuwo.js",
    "chars": 1250,
    "preview": "/*\n酷我音乐 解锁会员试听及部分功能\n\n***************************\nQuantumultX:\n\n[rewrite_local]\n^https?:\\/\\/vip1\\.kuwo\\.cn\\/(vip\\/v2\\/use"
  },
  {
    "path": "Surge/JS/MIX.js",
    "chars": 4274,
    "preview": "/*\nMIX 解锁特权 (需恢复购买)\n\n***************************\nQuantumultX:\n\n[rewrite_local]\nhttps?:\\/\\/cdn-bm\\.camera360\\.com\\/api\\/m"
  },
  {
    "path": "Surge/JS/NiChi.js",
    "chars": 755,
    "preview": "/*\nNiChi 解锁素材包\n\n***************************\nQuantumultX:\n\n[rewrite_local]\n^https?:\\/\\/m(p|ini-hk)\\.bybutter\\.com\\/mood\\/"
  },
  {
    "path": "Surge/JS/PicsArt.js",
    "chars": 2111,
    "preview": "/*\nPicsArt 解锁高级功能\n数据来自 @chxm1023\n\n***************************\nQuantumultX:\n\n[rewrite_local]\n^https:\\/\\/api\\.(picsart|mei"
  },
  {
    "path": "Surge/JS/Polarr.js",
    "chars": 1725,
    "preview": "/*\nPolarr泼辣修图 解锁特权 (需登陆)\n\n***************************\nQuantumultX:\n\n[rewrite_local]\n^https:\\/\\/api\\.polaxiong\\.com\\/v1\\/"
  },
  {
    "path": "Surge/JS/Super.js",
    "chars": 3321,
    "preview": "/*皮皮虾去广告和水印 by Liquor030\n如果只需要去广告功能请在[URL Rewrite]中添加\n \n# Remove Super's Ad (By Liquor030)\napp_name=super&([\\S]*)aid=\\d+"
  },
  {
    "path": "Surge/JS/VUE.js",
    "chars": 768,
    "preview": "/*\nVUE Vlog 解锁高级功能 (需登录)\n\n***************************\nQuantumultX:\n\n[rewrite_local]\n^https:\\/\\/api\\.vuevideo\\.net\\/api\\/"
  },
  {
    "path": "Surge/JS/Wps.js",
    "chars": 1528,
    "preview": "/*\nWPS Office 解锁部分功能\n\n***************************\nQuantumultX:\n\n[rewrite_local]\n^https?:\\/\\/[a-z-]*account\\.wps\\.c(n|om)"
  },
  {
    "path": "Surge/JS/jibjab.js",
    "chars": 757,
    "preview": "/*\nJibJab 解锁高级功能\n\n***************************\nQuantumultX:\n\n[rewrite_local]\nhttps:\\/\\/origin-prod-phoenix\\.jibjab\\.com\\/"
  },
  {
    "path": "Surge/JS/luqi.js",
    "chars": 706,
    "preview": "/*\n陆琪讲故事 解锁电台\n\n***************************\nQuantumultX:\n\n[rewrite_local]\n^https:\\/\\/www\\.luqijianggushi\\.com\\/api\\/v2\\/u"
  },
  {
    "path": "Surge/JS/vivavideo.js",
    "chars": 1194,
    "preview": "/*\n小影 解锁高级功能 \n\n***************************\nQuantumultX:\n\n[rewrite_local]\n^https:\\/\\/api-use\\.intsvs\\.com\\/api\\/rest\\/u\\/"
  },
  {
    "path": "Surge/Module/BahamutAnimeAds.sgmodule",
    "chars": 349,
    "preview": "#!name=动画疯\n#!desc=屏蔽播放广告 (黑屏25秒自动播放)\n#!system=ios\n\n[Script]\n动画疯(屏蔽广告) = type=http-response,pattern=^https:\\/\\/api\\.gamer"
  },
  {
    "path": "Surge/Module/BahamutDailyBonus.sgmodule",
    "chars": 423,
    "preview": "#!name=🐻 巴哈姆特 [签到]\n#!desc=每日定时签到,包含主站签到、公会签到、动画疯答题等;模块参数可调整签到时间。\\n使用前需要进入BoxJs填写账号密码,BoxJs订阅链接可查看脚本注释。\n#!arguments=定时签到:"
  },
  {
    "path": "Surge/Module/BiliComicsDailyBonus.sgmodule",
    "chars": 706,
    "preview": "#!name=🐻 哔哩哔哩漫画 [签到]\n#!desc=每日定时签到,模块参数可调整签到时间。\\n打开哔哩哔哩漫画APP点击\"我的\"即可获取cookie.\n#!arguments=定时签到:0 9 * * *,禁用脚本:哔哩漫画[Cooki"
  },
  {
    "path": "Surge/Module/BiliComicsExchangePoints.sgmodule",
    "chars": 610,
    "preview": "#!name=🐻 哔哩哔哩漫画 [积分抢购]\n#!desc=定时抢购积分商城物品,模块参数可调整抢购设置,该模块需使用\"哔哩哔哩漫画签到模块\"获取Cookie。\n#!arguments=定时抢购:0-59 0 0 * * 0-1,商品名称:"
  },
  {
    "path": "Surge/Module/CtripDailyBonus.sgmodule",
    "chars": 763,
    "preview": "#!name=🐻 携程旅行 [签到]\n#!desc=每日定时签到,支持多账号。\\n登陆\"携程旅行\"微信小程序或\"携程网页版\"(https://m.ctrip.com/)可获取账号授权,填写模块参数可禁用脚本。\n#!arguments=定时签"
  },
  {
    "path": "Surge/Module/DisneyRating.sgmodule",
    "chars": 522,
    "preview": "#!name=Disney+评分\n#!desc=Disney+剧集页显示IMDb / 烂番茄 / 豆瓣评分\n#!arguments=脚本引擎:jsc,调试模式:0\n#!arguments-desc=脚本引擎:jsc/webview/auto"
  },
  {
    "path": "Surge/Module/GetCookie.sgmodule",
    "chars": 1202,
    "preview": "#!name=🐻 签到脚本Cookie获取\n#!desc=该模块适用于NobyDa定时签到脚本的Cookie获取. 集成: 爱奇艺, 哔哩哔哩漫画, 百度贴吧, 快看漫画, 携程旅行。\n#!system=ios\n\n[Script]\n爱奇艺C"
  },
  {
    "path": "Surge/Module/GoogleCAPTCHA.sgmodule",
    "chars": 622,
    "preview": "#!name=Google人机验证\n#!desc=Google搜索内容时并发使用多个策略/策略组,以避免可能出现的人机验证。注意:需要在模块参数填写策略/策略组名的正则表达式。\n#!arguments=策略正则,脚本引擎:auto\n#!ar"
  },
  {
    "path": "Surge/Module/HuiJuDongManAds.sgmodule",
    "chars": 909,
    "preview": "#!name=荟聚动漫\n#!desc=去除大多数弹屏以及底栏广告, 由于有广告缓存, 因此可能需要卸载App重装. @NobyDa\n\n# Update at 2022/07/11\n\n[Rule]\n#底栏\nDOMAIN,googleads.g"
  },
  {
    "path": "Surge/Module/IPA_install.sgmodule",
    "chars": 363,
    "preview": "#!name=IPA应用辅助安装器\n#!desc=该模块可在iOS端辅助安装商店版或已签名IPA(需使用快捷指令 + Shu/Jsbox/pythonista), 查看脚本注释以了解具体方法; 安装演示可查看TG频道 @NobyDa\n\n\n["
  },
  {
    "path": "Surge/Module/KuaiKanComicsDailyBonus.sgmodule",
    "chars": 688,
    "preview": "#!name=🐻 快看漫画 [签到]\n#!desc=每日定时签到,模块参数可调整签到时间。\\n打开快看漫画APP点击\"我的\"即可获取cookie.\n#!arguments=定时签到:10 9 * * *,禁用脚本:快看漫画[Cookie],"
  },
  {
    "path": "Surge/Module/NewBing.sgmodule",
    "chars": 350,
    "preview": "#!name=New Bing for other browsers\n#!desc=Unlock browser restrictions for new bing AI search.\n\n[Header Rewrite]\n^https:\\"
  },
  {
    "path": "Surge/Module/RewriteRules.sgmodule",
    "chars": 51627,
    "preview": "#!name=Ad rewrite rules, By NobyDa\n#!desc=This module integrate lhie1 and ConnersHua, and some self use rewrite rules\n\n["
  },
  {
    "path": "Surge/Module/TestFlightAccount.sgmodule",
    "chars": 901,
    "preview": "#!name=TestFlight账户管理\n#!desc=自动存储/合并多个TestFlight账户列表, 并可导出/分享TestFlight APP.\n#!arguments=请求超时:30,启用缓存:1,使用iOS列表:0,脚本引擎:j"
  },
  {
    "path": "Surge/Module/TestFlightDownload.sgmodule",
    "chars": 445,
    "preview": "#!name=TestFlight区域限制解除\n#!desc=该模块适用于更新TestFlight App时, 提示\"APP不可用\"问题.\n\n[General]\nskip-proxy = %APPEND% iosapps.itunes.ap"
  },
  {
    "path": "Surge/Module/TieBaDailyBonus.sgmodule",
    "chars": 721,
    "preview": "#!name=🐻 百度贴吧 [签到]\n#!desc=每日定时签到,模块参数可调整签到时间。\\n打开百度贴吧APP点击\"我的\"即可获取cookie.\n#!arguments=定时签到:40 8 * * *,禁用脚本:百度贴吧[Cookie],"
  },
  {
    "path": "Surge/Module/iQIYIDailyBonus.sgmodule",
    "chars": 713,
    "preview": "#!name=🐻 爱奇艺 [会员签到]\n#!desc=每日定时签到,模块参数可调整签到时间。\\n登陆爱奇艺网页版 https://m.ctrip.com/ 使用密码登录可获取签到Cookie.\n#!arguments=定时签到:10 9 *"
  },
  {
    "path": "Surge/WeChat.list",
    "chars": 10521,
    "preview": "# 该规则集包含绝大部分微信/WeChat网络请求 (IPv4/IPv6)\n# 请注意,该规则集除 Surge 之外不应该使用 (由于双栈问题且考虑到规则数量,部分子规则写法仅与Surge兼容)\n\nDOMAIN-KEYWORD,101.22"
  },
  {
    "path": "TestFlight/TestFlightAccount.js",
    "chars": 18026,
    "preview": "/********************************\nTestFlight账户管理脚本\n\n脚本作者: @NobyDa \n脚本兼容: Surge4、QuantumultX、Loon(2.1.20 413+)\n更新时间: 202"
  },
  {
    "path": "Time-based-One-Time-Password/README.md",
    "chars": 583,
    "preview": "## Desc\n\nA time-based one-time password algorithm(TOTP), implemented entirely 100% in Javascript. \n\nThis script complies"
  },
  {
    "path": "iQIYI-DailyBonus/iQIYI.js",
    "chars": 25385,
    "preview": "/*\n爱奇艺会员签到脚本\n\n更新时间: 2024/05/15\n脚本兼容: QuantumultX, Surge4, Loon, JsBox, Node.js\n电报频道: @NobyDa\n问题反馈: @NobyDa_bot\n\n获取Cookie"
  }
]

About this extraction

This page contains the full source code of the NobyDa/Script GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 90 files (1.1 MB), approximately 423.0k tokens, and a symbol index with 152 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!