/*
哔哩哔哩番剧监控-lowking-v1.6.1
⚠️注意,如果频繁出现“追番列表数据处理错误❌请带上日志联系作者”这个提示,多半是返回的数据太长,接收不完整破坏了原有json结构
只需在BoxJs配置调小“页大小”,即可解决,建议10
该参数决定每次请求多少个番剧信息,自己平衡
按下面配置完之后,手机哔哩哔哩点击我的-动态,即可获取cookie
************************
Surge 4.2.0+ 脚本配置(其他APP自行转换配置):
************************
[Script]
# > 哔哩哔哩番剧监控
哔哩哔哩番剧监控cookie = type=http-request,pattern=https?:\/\/app.bilibili.com\/x\/v2\/space\/bangumi,script-path=https://raw.githubusercontent.com/lowking/Scripts/master/bilibili/bangumiMonitor.js
哔哩哔哩番剧监控 = type=cron,cronexp="0 0 0,1 * * ?",wake-system=1,script-path=https://raw.githubusercontent.com/lowking/Scripts/master/bilibili/bangumiMonitor.js
[MITM]
hostname = %APPEND% *.bilibili.com
*/
const lk = new ToolKit('哔哩哔哩番剧监控', 'BilibiliBangumiMonitor', {"httpApi": "ffff@10.0.0.19:6166"})
const vmid = lk.getVal('lkVmidBilibiliBangumiMonitor')
const followStatus = lk.getVal('lkBilibiliBangumiFollowStatus', 2)
const bangumiListKey = `lkBilibiliBangumiList${followStatus}`
const pageSize = lk.getVal('lkBilibiliBangumiPageSize', 15)
const limitNo = lk.getVal('lkBilibiliBangumiLimitNo', 10)
const errCountKey = "lkBilibiliBangumiErrCount"
let errCount = lk.getVal(errCountKey, 0)
if (!lk.isExecComm) {
if (lk.isRequest()) {
getCookie();
lk.done();
} else {
lk.boxJsJsonBuilder({
"icons": [
"https://raw.githubusercontent.com/Orz-3/mini/master/Alpha/bilibili.png",
"https://raw.githubusercontent.com/Orz-3/mini/master/Color/bilibili.png"
],
"settings": [
{
"id": "lkIsEnableLogBilibiliBangumiMonitor",
"name": "开启/关闭日志",
"val": true,
"type": "boolean",
"desc": "默认开启"
},
{
"id": "lkNotifyOnlyFailBilibiliBangumiMonitor",
"name": "只当有番剧更新了才通知",
"val": false,
"type": "boolean",
"desc": "默认关闭"
},
{
"id": "lkIsEnableTgNotifyBilibiliBangumiMonitor",
"name": "开启/关闭Telegram通知",
"val": false,
"type": "boolean",
"desc": "默认关闭"
},
{
"id": "lkTgNotifyUrlBilibiliBangumiMonitor",
"name": "Telegram通知地址",
"val": "",
"type": "text",
"desc": "Tg的通知地址,如:https://api.telegram.org/bot-token/sendMessage?chat_id=-100140&parse_mode=Markdown&text="
},
{
"id": "lkBilibiliBangumiPageSize",
"name": "页大小",
"val": 15,
"type": "number",
"desc": "每次请求番剧数量,避免数据太大导致错误"
},
{
"id": "lkBilibiliBangumiLimitNo",
"name": "番剧异常通知限制数量",
"val": 10,
"type": "number",
"desc": "有时候B站番剧会更新数据,导致大量番剧更新,设置一个数字,番剧更新数量超过这个数字不通知"
},
{
"id": "lkBilibiliBangumiFollowStatus",
"name": "追番筛选",
"val": "2",
"type": "radios",
"items": [
{
"key": "0",
"label": "全部"
},
{
"key": "1",
"label": "想看"
},
{
"key": "2",
"label": "在看"
},
{
"key": "3",
"label": "看过"
}
],
"desc": "默认-在看"
}
],
"keys": [
"lkVmidBilibiliBangumiMonitor",
"lkBilibiliBangumiList0",
"lkBilibiliBangumiList1",
"lkBilibiliBangumiList2",
"lkBilibiliBangumiList3"
]
}, {
"author": "@lowking",
"repo": "https://github.com/lowking/Scripts",
"script_url": "https://github.com/lowking/Scripts/blob/master/bilibili/bangumiMonitor.js"
});
all();
}
}
function getCookie() {
const url = $request.url
if ($request && $request.method != 'OPTIONS' && url.match(/\/x\/v2\/space\/bangumi/)) {
lk.setVal('lkVmidBilibiliBangumiMonitor', url.split("vmid=")[1].split("&")[0])
lk.msg(``, `获取Cookie成功🎉`)
}
}
async function all() {
if (lk.isEmpty(vmid)) {
lk.execFail()
lk.appendNotifyInfo(`请获取Cookie之后再试❌`)
} else {
let resultList = []
let bangumi1List = await getFollowList(1, pageSize, {}, 1)
let bangumi2List = await getFollowList(1, pageSize, {}, 2)
resultList = Object.assign(bangumi1List, resultList)
resultList = Object.assign(bangumi2List, resultList)
if (!lk.isEmpty(resultList) && Object.keys(resultList).length > 0) {
await compareDiff(resultList)
}
}
lk.msg(``)
lk.done()
}
function compareDiff(curList) {
return new Promise((resolve, reject) => {
if ((!lk.isEmpty(curList)) && (Object.keys(curList).length > 0)) {
let storedList = lk.getVal(bangumiListKey)
lk.setVal(bangumiListKey, JSON.stringify(curList))
if (lk.isEmpty(storedList)) {
lk.appendNotifyInfo(`首次运行,已保存追番列表`)
} else {
try {
storedList = JSON.parse(storedList)
if (Object.keys(storedList).length > 0) {
//curList转成对象
let curKeyList = []
for (let i in curList) {
curKeyList.push(i)
}
let storedKeyList = []
for (let i in storedList) {
storedKeyList.push(i)
}
let result = findDifferentElements2(storedKeyList, curKeyList)
if (lk.isEmpty(result) || result.length == 0) {
if (lk.execStatus) {
lk.appendNotifyInfo(`无番剧更新🔉`)
}
} else {
lk.log(`番剧更新如下:`)
if (result.length >= limitNo) {
lk.log(`番剧更新数量超过限制,不通知`)
} else {
for (let i in result) {
lk.execFail()
lk.appendNotifyInfo(`【${curList[result[i]].title}】- ${curList[result[i]].indexShow}`)
lk.log(`【${curList[result[i]].title}】- ${curList[result[i]].indexShow}`)
}
}
}
} else {
lk.execFail()
lk.appendNotifyInfo(`已保存的追番列表无数据,下次运行才有更新提醒⚠️`)
}
} catch (e) {
lk.logErr(e)
lk.execFail()
lk.appendNotifyInfo(`已保存的追番列表数据格式错误❌,请使用BoxJs手动清空后再试`)
}
}
} else {
lk.execFail()
lk.appendNotifyInfo(`未发现番剧更新⚠️`)
}
resolve()
})
}
function findDifferentElements2(array1, array2) {
// 定义一个空数res组作为返回值的容器,基本操作次数1。
const res = []
// 定义一个对象用于装数组一的元素,基本操作次数1。
const objectA = {}
// 使用对象的 hash table 存储元素,并且去重。基本操作次数2n。
for(const ele of array1) { // 取出n个元素n次
objectA[ele] = undefined // 存入n个元素n次
}
// 定义一个对象用于装数组二的元素,基本操作次数1。
const objectB = {}
// 使用对象的 hash table 存储元素,并且去重。基本操作次数2n。
for(const ele of array2){ // 取出n个元素n次
objectB[ele] = undefined // 存入n个元素n次
}
// 使用对象的 hash table 删除相同元素。基本操作次数4n。
for(const key in objectA){ //取出n个key (n次操作)
if(key in objectB){ // 基本操作1次 (外层循环n次)
delete objectB[key] // 基本操作1次 (外层循环n次)
delete objectA[key] // 基本操作1次 (外层循环n次)(总共是3n 加上n次取key的操作 一共是4n)
}
}
// 将第二个对象剩下来的key push到res容器中,基本操作次数也是3n次(最糟糕的情况)。
for(const key in objectB){ // 取出n个元素n次(最糟糕的情况)。
res[res.length] = key // 读取n次length n次,存入n个元素n次,一共2n(最糟糕的情况)。
}
// 返回结果,基本操作次数1。
return res
}
function getFollowList(pn, ps, preList, type) {
return new Promise((resolve, reject) => {
let listApi = `https://api.bilibili.com/x/space/bangumi/follow/list?type=#{type}&follow_status=#{followStatus}&pn=#{pn}&ps=#{ps}&vmid=#{vmid}&ts=#{ts}`
let param = {
"pn": pn,
"ps": ps,
"vmid": vmid,
"type": type,
"ts": new Date().getTime(),
"followStatus": followStatus
}
listApi = lk.customReplace(listApi, param)
lk.log(listApi)
let url = {
url: listApi,
headers: {
"User-Agent": lk.userAgent
}
}
lk.get(url, async (error, response, data) => {
let curList = {}
try {
lk.log(error)
if (error) {
lk.execFail()
lk.appendNotifyInfo(`获取追番列表失败❌请稍后再试`)
} else {
let ret = JSON.parse(data)
if (ret.code == 0) {
let list = ret.data.list
let total = ret.data.total
for (let i in list) {
let bangumit = {}
let bangumi = list[i]
let sessionId = bangumi["season_id"]
let newEpId = bangumi["new_ep"].id
let title = bangumi.title
let indexShow = bangumi["new_ep"]["index_show"]
// lk.log(`番剧【${sessionId}-${title}】最新【${newEpId}-${indexShow}】更新时间【${bangumi["new_ep"]["pub_time"]}】`)
//记录信息
bangumit.sessionId = sessionId
bangumit.newEpId = newEpId
bangumit.title = title
bangumit.indexShow = indexShow
//判断是否有效数据,无效数据跳过
if (lk.isEmpty(indexShow) || lk.isEmpty(title) || lk.isEmpty(sessionId) || lk.isEmpty(newEpId)) {
continue
}
curList[`${sessionId}${newEpId}`] = bangumit
}
if (!lk.isEmpty(preList)) {
curList = Object.assign(preList, curList)
} else {
preList = {}
}
// lk.log(JSON.stringify(curList))
// lk.appendNotifyInfo(`${pn}-${ps}-${total}-${preList.length}-${curList.length}`)
if (pn * ps < total) {
curList = await getFollowList(++pn, ps, curList, type)
}
lk.setVal(errCountKey, "0")
} else {
if (errCount >= 5) {
lk.execFail()
lk.appendNotifyInfo(`❌获取追番列表失败:${ret.message}`)
lk.setVal(errCountKey, "0")
} else {
++errCount
lk.setVal(errCountKey, JSON.stringify(errCount))
}
}
}
} catch (e) {
lk.logErr(e)
lk.log(`b站返回数据:${data}`)
if (errCount >= 5) {
lk.execFail()
lk.appendNotifyInfo(`追番列表数据处理错误❌请带上日志联系作者`)
lk.setVal(errCountKey, "0")
} else {
++errCount
lk.setVal(errCountKey, JSON.stringify(errCount))
}
} finally {
resolve(curList)
}
})
})
}
//ToolKit-start
function ToolKit(t,s,i){return new class{constructor(t,s,i){this.tgEscapeCharMapping={"&":"&","#":"#"};this.userAgent=`Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0.2 Safari/605.1.15`;this.prefix=`lk`;this.name=t;this.id=s;this.data=null;this.dataFile=this.getRealPath(`${this.prefix}${this.id}.dat`);this.boxJsJsonFile=this.getRealPath(`${this.prefix}${this.id}.boxjs.json`);this.options=i;this.isExecComm=false;this.isEnableLog=this.getVal(`${this.prefix}IsEnableLog${this.id}`);this.isEnableLog=this.isEmpty(this.isEnableLog)?true:JSON.parse(this.isEnableLog);this.isNotifyOnlyFail=this.getVal(`${this.prefix}NotifyOnlyFail${this.id}`);this.isNotifyOnlyFail=this.isEmpty(this.isNotifyOnlyFail)?false:JSON.parse(this.isNotifyOnlyFail);this.isEnableTgNotify=this.getVal(`${this.prefix}IsEnableTgNotify${this.id}`);this.isEnableTgNotify=this.isEmpty(this.isEnableTgNotify)?false:JSON.parse(this.isEnableTgNotify);this.tgNotifyUrl=this.getVal(`${this.prefix}TgNotifyUrl${this.id}`);this.isEnableTgNotify=this.isEnableTgNotify?!this.isEmpty(this.tgNotifyUrl):this.isEnableTgNotify;this.costTotalStringKey=`${this.prefix}CostTotalString${this.id}`;this.costTotalString=this.getVal(this.costTotalStringKey);this.costTotalString=this.isEmpty(this.costTotalString)?`0,0`:this.costTotalString.replace('"',"");this.costTotalMs=this.costTotalString.split(",")[0];this.execCount=this.costTotalString.split(",")[1];this.costTotalMs=this.isEmpty(this.costTotalMs)?0:parseInt(this.costTotalMs);this.execCount=this.isEmpty(this.execCount)?0:parseInt(this.execCount);this.logSeparator="\n██";this.now=new Date;this.startTime=this.now.getTime();this.node=(()=>{if(this.isNode()){const t=require("request");return{request:t}}else{return null}})();this.execStatus=true;this.notifyInfo=[];this.boxjsCurSessionKey="chavy_boxjs_cur_sessions";this.boxjsSessionsKey="chavy_boxjs_sessions";this.log(`${this.name}, 开始执行!`);this.execComm()}getRealPath(t){if(this.isNode()){let s=process.argv.slice(1,2)[0].split("/");s[s.length-1]=t;return s.join("/")}return t}async execComm(){if(!this.isNode()){return}this.comm=process.argv.slice(1);if(this.comm[1]!="p"){return}let t=false;this.isExecComm=true;this.log(`开始执行指令【${this.comm[1]}】=> 发送到其他终端测试脚本!`);if(this.isEmpty(this.options)||this.isEmpty(this.options.httpApi)){this.log(`未设置options,使用默认值`);if(this.isEmpty(this.options)){this.options={}}this.options.httpApi=`ffff@10.0.0.19:6166`}else{if(!/.*?@.*?:[0-9]+/.test(this.options.httpApi)){t=true;this.log(`❌httpApi格式错误!格式:ffff@3.3.3.18:6166`);this.done()}}if(!t){this.callApi(this.comm[2])}}callApi(t){let s=this.comm[0];let i=this.options.httpApi.split("@")[1];this.log(`获取【${s}】内容传给【${i}】`);let e="";this.fs=this.fs?this.fs:require("fs");this.path=this.path?this.path:require("path");const o=this.path.resolve(s);const h=this.path.resolve(process.cwd(),s);const r=this.fs.existsSync(o);const n=!r&&this.fs.existsSync(h);if(r||n){const t=r?o:h;try{e=this.fs.readFileSync(t)}catch(t){e=""}}else{e=""}let a={url:`http://${i}/v1/scripting/evaluate`,headers:{"X-Key":`${this.options.httpApi.split("@")[0]}`},body:{script_text:`${e}`,mock_type:"cron",timeout:!this.isEmpty(t)&&t>5?t:5},json:true};this.post(a,(t,e,o)=>{this.log(`已将脚本【${s}】发给【${i}】`);this.done()})}boxJsJsonBuilder(t,s){if(!this.isNode()){return}if(!this.isJsonObject(t)||!this.isJsonObject(s)){this.log("构建BoxJsJson传入参数格式错误,请传入json对象");return}let i="/Users/lowking/Desktop/Scripts/lowking.boxjs.json";if(s&&s.hasOwnProperty("target_boxjs_json_path")){i=s["target_boxjs_json_path"]}if(!this.fs.existsSync(i)){return}this.log("using node");let e=["settings","keys"];const o="https://raw.githubusercontent.com/Orz-3";let h={};let r="#lk{script_url}";if(s&&s.hasOwnProperty("script_url")){r=this.isEmpty(s["script_url"])?"#lk{script_url}":s["script_url"]}h.id=`${this.prefix}${this.id}`;h.name=this.name;h.desc_html=`⚠️使用说明详情【点我查看】`;h.icons=[`${o}/mini/master/Alpha/${this.id.toLocaleLowerCase()}.png`,`${o}/mini/master/Color/${this.id.toLocaleLowerCase()}.png`];h.keys=[];h.settings=[{id:`${this.prefix}IsEnableLog${this.id}`,name:"开启/关闭日志",val:true,type:"boolean",desc:"默认开启"},{id:`${this.prefix}NotifyOnlyFail${this.id}`,name:"只当执行失败才通知",val:false,type:"boolean",desc:"默认关闭"},{id:`${this.prefix}IsEnableTgNotify${this.id}`,name:"开启/关闭Telegram通知",val:false,type:"boolean",desc:"默认关闭"},{id:`${this.prefix}TgNotifyUrl${this.id}`,name:"Telegram通知地址",val:"",type:"text",desc:"Tg的通知地址,如:https://api.telegram.org/bot-token/sendMessage?chat_id=-100140&parse_mode=Markdown&text="}];h.author="#lk{author}";h.repo="#lk{repo}";h.script=`${r}?raw=true`;if(!this.isEmpty(t)){for(let s of e){if(this.isEmpty(t[s])){break}if(s==="settings"){for(let i=0;i0)){return}let c=u.apps;let g=c.indexOf(c.filter(t=>{return t.id==h.id})[0]);if(g>=0){u.apps[g]=h}else{u.apps.push(h)}let d=JSON.stringify(u,null,2);if(!this.isEmpty(s)){for(const t in s){let i=s[t];if(!i){switch(t){case"author":i="@lowking";case"repo":i="https://github.com/lowking/Scripts";default:continue}}d=d.replace(`#lk{${t}}`,i)}}const y=/(?:#lk\{)(.+?)(?=\})/;let S=y.exec(d);if(S!==null){this.log(`生成BoxJs还有未配置的参数,请参考https://github.com/lowking/Scripts/blob/master/util/example/ToolKitDemo.js#L17-L19传入参数:`)}let m=new Set;while((S=y.exec(d))!==null){m.add(S[1]);d=d.replace(`#lk{${S[1]}}`,``)}m.forEach(t=>{console.log(`${t} `)});this.fs.writeFileSync(i,d)}isJsonObject(t){return typeof t=="object"&&Object.prototype.toString.call(t).toLowerCase()=="[object object]"&&!t.length}appendNotifyInfo(t,s){if(s==1){this.notifyInfo=t}else{this.notifyInfo.push(t)}}prependNotifyInfo(t){this.notifyInfo.splice(0,0,t)}execFail(){this.execStatus=false}isRequest(){return typeof $request!="undefined"}isSurge(){return typeof $httpClient!="undefined"}isQuanX(){return typeof $task!="undefined"}isLoon(){return typeof $loon!="undefined"}isJSBox(){return typeof $app!="undefined"&&typeof $http!="undefined"}isStash(){return"undefined"!==typeof $environment&&$environment["stash-version"]}isNode(){return typeof require=="function"&&!this.isJSBox()}sleep(t){return new Promise(s=>setTimeout(s,t))}log(t){if(this.isEnableLog)console.log(`${this.logSeparator}${t}`)}logErr(t){this.execStatus=true;if(this.isEnableLog){console.log(`${this.logSeparator}${this.name}执行异常:`);console.log(t);if(!t.message){return}console.log(`\n${t.message}`)}}msg(t,s,i,e,o){if(!this.isRequest()&&this.isNotifyOnlyFail&&this.execStatus){return}if(this.isEmpty(s)){if(Array.isArray(this.notifyInfo)){s=this.notifyInfo.join("\n")}else{s=this.notifyInfo}}if(this.isEmpty(s)){return}if(this.isEnableTgNotify){this.log(`${this.name}Tg通知开始`);for(let t in this.tgEscapeCharMapping){if(!this.tgEscapeCharMapping.hasOwnProperty(t)){continue}s=s.replace(t,this.tgEscapeCharMapping[t])}this.get({url:encodeURI(`${this.tgNotifyUrl}📌${this.name}\n${s}`)},(t,s,i)=>{this.log(`Tg通知完毕`)})}else{let h={};const r=!this.isEmpty(i);const n=!this.isEmpty(e);const a=!this.isEmpty(o);if(this.isSurge()||this.isLoon()||this.isStash()){if(r){h["url"]=i;h["action"]="open-url"}if(a){h["text"]=o;h["action"]="clipboard"}if(n)h["media-url"]=e;$notification.post(this.name,t,s,h)}else if(this.isQuanX()){if(r)h["open-url"]=i;if(n)h["media-url"]=e;$notify(this.name,t,s,h)}else if(this.isNode()){this.log("⭐️"+this.name+"\n"+t+"\n"+s)}else if(this.isJSBox()){$push.schedule({title:this.name,body:t?t+"\n"+s:s})}}}getVal(t,s=""){let i;if(this.isSurge()||this.isLoon()||this.isStash()){i=$persistentStore.read(t)}else if(this.isQuanX()){i=$prefs.valueForKey(t)}else if(this.isNode()){this.data=this.loadData();i=process.env[t]||this.data[t]}else{i=this.data&&this.data[t]||null}return!i?s:i}updateBoxjsSessions(t,s){if(t==this.boxjsSessionsKey){return}const i=`${this.prefix}${this.id}`;let e=JSON.parse(this.getVal(this.boxjsCurSessionKey,"{}"));if(!e.hasOwnProperty(i)){return}let o=e[i];let h=JSON.parse(this.getVal(this.boxjsSessionsKey,"[]"));if(h.length==0){return}let r=[];h.forEach(t=>{if(t.id==o){r=t.datas}});if(r.length==0){return}let n=false;r.forEach(i=>{if(i.key==t){i.val=s;n=true}});if(!n){r.push({key:t,val:s})}h.forEach(t=>{if(t.id==o){t.datas=r}});this.setVal(this.boxjsSessionsKey,JSON.stringify(h))}setVal(t,s){if(this.isSurge()||this.isLoon()||this.isStash()){this.updateBoxjsSessions(t,s);return $persistentStore.write(s,t)}else if(this.isQuanX()){this.updateBoxjsSessions(t,s);return $prefs.setValueForKey(s,t)}else if(this.isNode()){this.data=this.loadData();this.data[t]=s;this.writeData();return true}else{return this.data&&this.data[t]||null}}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);const s=this.path.resolve(process.cwd(),this.dataFile);const i=this.fs.existsSync(t);const e=!i&&this.fs.existsSync(s);if(i||e){const e=i?t:s;try{return JSON.parse(this.fs.readFileSync(e))}catch(t){return{}}}else{return{}}}writeData(){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);const s=this.path.resolve(process.cwd(),this.dataFile);const i=this.fs.existsSync(t);const e=!i&&this.fs.existsSync(s);const o=JSON.stringify(this.data);if(i){this.fs.writeFileSync(t,o)}else if(e){this.fs.writeFileSync(s,o)}else{this.fs.writeFileSync(t,o)}}adapterStatus(t){if(t){if(t.status){t["statusCode"]=t.status}else if(t.statusCode){t["status"]=t.statusCode}}return t}get(t,s=(()=>{})){if(this.isSurge()||this.isLoon()||this.isStash()){$httpClient.get(t,(t,i,e)=>{s(t,this.adapterStatus(i),e)})}else if(this.isQuanX()){if(typeof t=="string")t={url:t};t["method"]="GET";$task.fetch(t).then(t=>{s(null,this.adapterStatus(t),t.body)},t=>s(t.error,null,null))}else if(this.isNode()){this.node.request(t,(t,i,e)=>{s(t,this.adapterStatus(i),e)})}else if(this.isJSBox()){if(typeof t=="string")t={url:t};t["header"]=t["headers"];t["handler"]=function(t){let i=t.error;if(i)i=JSON.stringify(t.error);let e=t.data;if(typeof e=="object")e=JSON.stringify(t.data);s(i,this.adapterStatus(t.response),e)};$http.get(t)}}post(t,s=(()=>{})){if(this.isSurge()||this.isLoon()||this.isStash()){$httpClient.post(t,(t,i,e)=>{s(t,this.adapterStatus(i),e)})}else if(this.isQuanX()){if(typeof t=="string")t={url:t};t["method"]="POST";$task.fetch(t).then(t=>{s(null,this.adapterStatus(t),t.body)},t=>s(t.error,null,null))}else if(this.isNode()){this.node.request.post(t,(t,i,e)=>{s(t,this.adapterStatus(i),e)})}else if(this.isJSBox()){if(typeof t=="string")t={url:t};t["header"]=t["headers"];t["handler"]=function(t){let i=t.error;if(i)i=JSON.stringify(t.error);let e=t.data;if(typeof e=="object")e=JSON.stringify(t.data);s(i,this.adapterStatus(t.response),e)};$http.post(t)}}put(t,s=(()=>{})){if(this.isSurge()||this.isLoon()||this.isStash()){t.method="PUT";$httpClient.put(t,(t,i,e)=>{s(t,this.adapterStatus(i),e)})}else if(this.isQuanX()){if(typeof t=="string")t={url:t};t["method"]="PUT";$task.fetch(t).then(t=>{s(null,this.adapterStatus(t),t.body)},t=>s(t.error,null,null))}else if(this.isNode()){t.method="PUT";this.node.request.put(t,(t,i,e)=>{s(t,this.adapterStatus(i),e)})}else if(this.isJSBox()){if(typeof t=="string")t={url:t};t["header"]=t["headers"];t["handler"]=function(t){let i=t.error;if(i)i=JSON.stringify(t.error);let e=t.data;if(typeof e=="object")e=JSON.stringify(t.data);s(i,this.adapterStatus(t.response),e)};$http.post(t)}}costTime(){let t=`${this.name}执行完毕!`;if(this.isNode()&&this.isExecComm){t=`指令【${this.comm[1]}】执行完毕!`}const s=(new Date).getTime();const i=s-this.startTime;const e=i/1e3;this.execCount++;this.costTotalMs+=i;this.log(`${t}耗时【${e}】秒\n总共执行【${this.execCount}】次,平均耗时【${(this.costTotalMs/this.execCount/1e3).toFixed(4)}】秒`);this.setVal(this.costTotalStringKey,JSON.stringify(`${this.costTotalMs},${this.execCount}`))}done(t={}){this.costTime();if(this.isSurge()||this.isQuanX()||this.isLoon()||this.isStash()){$done(t)}}getRequestUrl(){return $request.url}getResponseBody(){return $response.body}isGetCookie(t){return!!($request.method!="OPTIONS"&&this.getRequestUrl().match(t))}isEmpty(t){return typeof t=="undefined"||t==null||t==""||t=="null"||t=="undefined"||t.length===0}randomString(t,s="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890"){t=t||32;let i=s.length;let e="";for(let o=0;o