| 1 |
- function ToolKit(scriptName,scriptId,options){return new class{constructor(scriptName,scriptId,options){this.tgEscapeCharMapping={"&":"&","#":"#"},this.userAgent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0.2 Safari/605.1.15",this.prefix="lk",this.name=scriptName,this.id=scriptId,this.data=null,this.dataFile=this.getRealPath(`${this.prefix}${this.id}.dat`),this.boxJsJsonFile=this.getRealPath(`${this.prefix}${this.id}.boxjs.json`),this.options=options,this.isExecComm=!1,this.isEnableLog=this.getVal(`${this.prefix}IsEnableLog${this.id}`),this.isEnableLog=!!this.isEmpty(this.isEnableLog)||JSON.parse(this.isEnableLog),this.isNotifyOnlyFail=this.getVal(`${this.prefix}NotifyOnlyFail${this.id}`),this.isNotifyOnlyFail=!this.isEmpty(this.isNotifyOnlyFail)&&JSON.parse(this.isNotifyOnlyFail),this.isEnableTgNotify=this.getVal(`${this.prefix}IsEnableTgNotify${this.id}`),this.isEnableTgNotify=!this.isEmpty(this.isEnableTgNotify)&&JSON.parse(this.isEnableTgNotify),this.tgNotifyUrl=this.getVal(`${this.prefix}TgNotifyUrl${this.id}`),this.isEnableTgNotify=this.isEnableTgNotify?!this.isEmpty(this.tgNotifyUrl):this.isEnableTgNotify,this.costTotalStringKey=`${this.prefix}CostTotalString${this.id}`,this.costTotalString=this.getVal(this.costTotalStringKey),this.costTotalString=this.isEmpty(this.costTotalString)?"0,0":this.costTotalString.replace('"',""),this.costTotalMs=this.costTotalString.split(",")[0],this.execCount=this.costTotalString.split(",")[1],this.costTotalMs=this.isEmpty(this.costTotalMs)?0:parseInt(this.costTotalMs),this.execCount=this.isEmpty(this.execCount)?0:parseInt(this.execCount),this.logSeparator="\n██",this.now=new Date,this.startTime=this.now.getTime(),this.node=(()=>{if(this.isNode()){const request=require("request");return{request:request}}return null})(),this.execStatus=!0,this.notifyInfo=[],this.log(`${this.name}, 开始执行!`),this.initCache(),this.execComm()}initCache(){const pKey=this.getPersistKey();if(this.isQuanX()&&(this.cache=JSON.parse($prefs.valueForKey(pKey)||"{}")),(this.isLoon()||isSurge)&&(this.cache=JSON.parse($persistentStore.read(pKey)||"{}")),this.isNode()){let fpath="root.json";this.node.fs.existsSync(fpath)||this.node.fs.writeFileSync(fpath,JSON.stringify({}),{flag:"wx"},err=>console.log(err)),this.root={},fpath=`${pKey}.json`,this.node.fs.existsSync(fpath)?this.cache=JSON.parse(this.node.fs.readFileSync(`${pKey}.json`)):(this.node.fs.writeFileSync(fpath,JSON.stringify({}),{flag:"wx"},err=>console.log(err)),this.cache={})}}getPersistKey(){return`private_${this.id}`}persistCache(){const pKey=this.getPersistKey(),data=JSON.stringify(this.cache,null,2);this.isQuanX()&&$prefs.setValueForKey(data,pKey),(this.isLoon()||this.isSurge())&&$persistentStore.write(data,pKey),this.isNode()&&(this.node.fs.writeFileSync(`${pKey}.json`,data,{flag:"w"},err=>console.log(err)),this.node.fs.writeFileSync("root.json",JSON.stringify(this.root,null,2),{flag:"w"},err=>console.log(err)))}write(data,key){if(this.log(`SET ${key}`),-1!==key.indexOf("#")){if(key=key.substr(1),isSurge||this.isLoon())return $persistentStore.write(data,key);if(this.isQuanX())return $prefs.setValueForKey(data,key);this.isNode()&&(this.root[key]=data)}else this.cache[key]=data;this.persistCache()}read(key){return this.log(`READ ${key}`),-1===key.indexOf("#")?this.cache[key]:(key=key.substr(1),this.isSurge()||this.isLoon()?$persistentStore.read(key):this.isQuanX()?$prefs.valueForKey(key):this.isNode()?this.root[key]:void 0)}delete(key){if(this.log(`DELETE ${key}`),-1!==key.indexOf("#")){if(key=key.substr(1),this.isSurge()||this.isLoon())return $persistentStore.write(null,key);if(this.isQuanX())return $prefs.removeValueForKey(key);this.isNode()&&delete this.root[key]}else delete this.cache[key];this.persistCache()}getRealPath(fileName){if(this.isNode()){let targetPath=process.argv.slice(1,2)[0].split("/");return targetPath[targetPath.length-1]=fileName,targetPath.join("/")}return fileName}getUrlHost(url){return url.slice(0,url.indexOf("/",8))}getUrlPath(url){const end=url.lastIndexOf("/")===url.length-1?-1:void 0;return url.slice(url.indexOf("/",8),end)}async execComm(){if(this.isNode()){this.comm=process.argv.slice(1);let isHttpApiErr=!1;"p"==this.comm[1]&&(this.isExecComm=!0,this.log(`开始执行指令【${this.comm[1]}】=> 发送到手机测试脚本!`),this.isEmpty(this.options)||this.isEmpty(this.options.httpApi)?(this.log("未设置options,使用默认值"),this.isEmpty(this.options)&&(this.options={}),this.options.httpApi="[email protected]:6166"):/.*?@.*?:[0-9]+/.test(this.options.httpApi)||(isHttpApiErr=!0,this.log("❌httpApi格式错误!格式:[email protected]:6166"),this.done()),isHttpApiErr||this.callApi(this.comm[2]))}}callApi(timeout){let fname=this.comm[0];this.log(`获取【${fname}】内容传给手机`);let scriptStr="";this.fs=this.fs?this.fs:require("fs"),this.path=this.path?this.path:require("path");const curDirDataFilePath=this.path.resolve(fname),rootDirDataFilePath=this.path.resolve(process.cwd(),fname),isCurDirDataFile=this.fs.existsSync(curDirDataFilePath),isRootDirDataFile=!isCurDirDataFile&&this.fs.existsSync(rootDirDataFilePath);if(isCurDirDataFile||isRootDirDataFile){const datPath=isCurDirDataFile?curDirDataFilePath:rootDirDataFilePath;try{scriptStr=this.fs.readFileSync(datPath)}catch(e){scriptStr=""}}else scriptStr="";let options={url:`http://${this.options.httpApi.split("@")[1]}/v1/scripting/evaluate`,headers:{"X-Key":`${this.options.httpApi.split("@")[0]}`},body:{script_text:`${scriptStr}`,mock_type:"cron",timeout:!this.isEmpty(timeout)&&timeout>5?timeout:5},json:!0};this.post(options,(_error,_response,_data)=>{this.log(`已将脚本【${fname}】发给手机!`),this.done()})}getCallerFileNameAndLine(){let error;try{throw Error("")}catch(err){error=err}const stack=error.stack,stackArr=stack.split("\n");let callerLogIndex=1;{const callerStackLine=stackArr[1];return this.path=this.path?this.path:require("path"),`[${callerStackLine.substring(callerStackLine.lastIndexOf(this.path.sep)+1,callerStackLine.lastIndexOf(":"))}]`}}getFunName(fun){var ret=fun.toString();return ret=(ret=ret.substr("function ".length)).substr(0,ret.indexOf("("))}boxJsJsonBuilder(info,param){if(this.isNode()){let boxjsJsonPath="/Users/lowking/Desktop/Scripts/lowking.boxjs.json";if(param&¶m.hasOwnProperty("target_boxjs_json_path")&&(boxjsJsonPath=param.target_boxjs_json_path),!this.fs.existsSync(boxjsJsonPath))return;if(!this.isJsonObject(info)||!this.isJsonObject(param))return void this.log("构建BoxJsJson传入参数格式错误,请传入json对象");this.log("using node");let needAppendKeys=["settings","keys"];const domain="https://raw.githubusercontent.com/Orz-3";let boxJsJson={},scritpUrl="#lk{script_url}";if(param&¶m.hasOwnProperty("script_url")&&(scritpUrl=this.isEmpty(param.script_url)?"#lk{script_url}":param.script_url),boxJsJson.id=`${this.prefix}${this.id}`,boxJsJson.name=this.name,boxJsJson.desc_html=`⚠️使用说明</br>详情【<a href='${scritpUrl}?raw=true'><font class='red--text'>点我查看</font></a>】`,boxJsJson.icons=[`${domain}/mini/master/Alpha/${this.id.toLocaleLowerCase()}.png`,`${domain}/mini/master/Color/${this.id.toLocaleLowerCase()}.png`],boxJsJson.keys=[],boxJsJson.settings=[{id:`${this.prefix}IsEnableLog${this.id}`,name:"开启/关闭日志",val:!0,type:"boolean",desc:"默认开启"},{id:`${this.prefix}NotifyOnlyFail${this.id}`,name:"只当执行失败才通知",val:!1,type:"boolean",desc:"默认关闭"},{id:`${this.prefix}IsEnableTgNotify${this.id}`,name:"开启/关闭Telegram通知",val:!1,type:"boolean",desc:"默认关闭"},{id:`${this.prefix}TgNotifyUrl${this.id}`,name:"Telegram通知地址",val:"",type:"text",desc:"Tg的通知地址,如:https://api.telegram.org/bot-token/sendMessage?chat_id=-100140&parse_mode=Markdown&text="}],boxJsJson.author="#lk{author}",boxJsJson.repo="#lk{repo}",boxJsJson.script=`${scritpUrl}?raw=true`,!this.isEmpty(info))for(let i in needAppendKeys){let key=needAppendKeys[i];if(!this.isEmpty(info[key])){if("settings"===key)for(let i=0;i<info[key].length;i++){let input=info[key][i];for(let j=0;j<boxJsJson.settings.length;j++){let def=boxJsJson.settings[j];input.id===def.id&&boxJsJson.settings.splice(j,1)}}boxJsJson[key]=boxJsJson[key].concat(info[key])}delete info[key]}if(Object.assign(boxJsJson,info),this.isNode()){this.fs=this.fs?this.fs:require("fs"),this.path=this.path?this.path:require("path");const curDirDataFilePath=this.path.resolve(this.boxJsJsonFile),rootDirDataFilePath=this.path.resolve(process.cwd(),this.boxJsJsonFile),isCurDirDataFile=this.fs.existsSync(curDirDataFilePath),isRootDirDataFile=!isCurDirDataFile&&this.fs.existsSync(rootDirDataFilePath),jsondata=JSON.stringify(boxJsJson,null,"\t");isCurDirDataFile?this.fs.writeFileSync(curDirDataFilePath,jsondata):isRootDirDataFile?this.fs.writeFileSync(rootDirDataFilePath,jsondata):this.fs.writeFileSync(curDirDataFilePath,jsondata);let boxjsJson=JSON.parse(this.fs.readFileSync(boxjsJsonPath));if(boxjsJson.hasOwnProperty("apps")&&Array.isArray(boxjsJson.apps)&&boxjsJson.apps.length>0){let apps=boxjsJson.apps,targetIdx=apps.indexOf(apps.filter(app=>app.id==boxJsJson.id)[0]);targetIdx>=0?boxjsJson.apps[targetIdx]=boxJsJson:boxjsJson.apps.push(boxJsJson);let ret=JSON.stringify(boxjsJson,null,2);if(!this.isEmpty(param))for(const key in param){let val="";param.hasOwnProperty(key)?val=param[key]:"author"===key?val="@lowking":"repo"===key&&(val="https://github.com/lowking/Scripts"),ret=ret.replace(`#lk{${key}}`,val)}const regex=/(?:#lk\{)(.+?)(?=\})/;let m=regex.exec(ret);null!==m&&this.log("生成BoxJs还有未配置的参数,请参考https://github.com/lowking/Scripts/blob/master/util/example/ToolKitDemo.js#L17-L18传入参数:\n");let loseParamSet=new Set;for(;null!==(m=regex.exec(ret));)loseParamSet.add(m[1]),ret=ret.replace(`#lk{${m[1]}}`,"");loseParamSet.forEach(p=>{console.log(`${p} `)}),this.fs.writeFileSync(boxjsJsonPath,ret)}}}}isJsonObject(obj){return"object"==typeof obj&&"[object object]"==Object.prototype.toString.call(obj).toLowerCase()&&!obj.length}appendNotifyInfo(info,type){1==type?this.notifyInfo=info:this.notifyInfo.push(info)}prependNotifyInfo(info){this.notifyInfo.splice(0,0,info)}execFail(){this.execStatus=!1}isRequest(){return"undefined"!=typeof $request}isSurge(){return"undefined"!=typeof $httpClient}isQuanX(){return"undefined"!=typeof $task}isLoon(){return"undefined"!=typeof $loon}isJSBox(){return"undefined"!=typeof $app&&"undefined"!=typeof $http}isStash(){return"undefined"!=typeof $environment&&$environment["stash-version"]}isNode(){return"function"==typeof require&&!this.isJSBox()}sleep(time){return new Promise(resolve=>setTimeout(resolve,time))}log(message){this.isEnableLog&&console.log(`${this.logSeparator}${message}`)}logErr(message){this.execStatus=!0,this.isEnableLog&&(console.log(`${this.logSeparator}${this.name}执行异常:`),console.log(message),console.log(`\n${message.message}`))}msg(subtitle,message,openUrl,mediaUrl){if(!this.isRequest()&&this.isNotifyOnlyFail&&this.execStatus);else if(this.isEmpty(message)&&(message=Array.isArray(this.notifyInfo)?this.notifyInfo.join("\n"):this.notifyInfo),!this.isEmpty(message))if(this.isEnableTgNotify){this.log(`${this.name}Tg通知开始`);for(let key in this.tgEscapeCharMapping)this.tgEscapeCharMapping.hasOwnProperty(key)&&(message=message.replace(key,this.tgEscapeCharMapping[key]));this.get({url:encodeURI(`${this.tgNotifyUrl}📌${this.name}\n${message}`)},(_error,_statusCode,_body)=>{this.log("Tg通知完毕")})}else{let options={};const hasOpenUrl=!this.isEmpty(openUrl),hasMediaUrl=!this.isEmpty(mediaUrl);this.isQuanX()&&(hasOpenUrl&&(options["open-url"]=openUrl),hasMediaUrl&&(options["media-url"]=mediaUrl),$notify(this.name,subtitle,message,options)),(this.isSurge()||this.isStash())&&(hasOpenUrl&&(options.url=openUrl),$notification.post(this.name,subtitle,message,options)),this.isNode()&&this.log("⭐️"+this.name+"\n"+subtitle+"\n"+message),this.isJSBox()&&$push.schedule({title:this.name,body:subtitle?subtitle+"\n"+message:message})}}getVal(key,defaultValue=""){let value;return this.isSurge()||this.isLoon()||this.isStash()?value=$persistentStore.read(key):this.isQuanX()?value=$prefs.valueForKey(key):this.isNode()?(this.data=this.loadData(),value=process.env[key]||this.data[key]):value=this.data&&this.data[key]||null,value||defaultValue}setVal(key,val){return this.isSurge()||this.isLoon()||this.isStash()?$persistentStore.write(val,key):this.isQuanX()?$prefs.setValueForKey(val,key):this.isNode()?(this.data=this.loadData(),this.data[key]=val,this.writeData(),!0):this.data&&this.data[key]||null}loadData(){if(!this.isNode())return{};{this.fs=this.fs?this.fs:require("fs"),this.path=this.path?this.path:require("path");const curDirDataFilePath=this.path.resolve(this.dataFile),rootDirDataFilePath=this.path.resolve(process.cwd(),this.dataFile),isCurDirDataFile=this.fs.existsSync(curDirDataFilePath),isRootDirDataFile=!isCurDirDataFile&&this.fs.existsSync(rootDirDataFilePath);if(!isCurDirDataFile&&!isRootDirDataFile)return{};{const datPath=isCurDirDataFile?curDirDataFilePath:rootDirDataFilePath;try{return JSON.parse(this.fs.readFileSync(datPath))}catch(e){return{}}}}}writeData(){if(this.isNode()){this.fs=this.fs?this.fs:require("fs"),this.path=this.path?this.path:require("path");const curDirDataFilePath=this.path.resolve(this.dataFile),rootDirDataFilePath=this.path.resolve(process.cwd(),this.dataFile),isCurDirDataFile=this.fs.existsSync(curDirDataFilePath),isRootDirDataFile=!isCurDirDataFile&&this.fs.existsSync(rootDirDataFilePath),jsondata=JSON.stringify(this.data);isCurDirDataFile?this.fs.writeFileSync(curDirDataFilePath,jsondata):isRootDirDataFile?this.fs.writeFileSync(rootDirDataFilePath,jsondata):this.fs.writeFileSync(curDirDataFilePath,jsondata)}}adapterStatus(response){return response&&(response.status?response.statusCode=response.status:response.statusCode&&(response.status=response.statusCode)),response}get(options,callback=(()=>{})){this.isQuanX()&&("string"==typeof options&&(options={url:options}),options.method="GET",$task.fetch(options).then(response=>{callback(null,this.adapterStatus(response),response.body)},reason=>callback(reason.error,null,null))),(this.isSurge()||this.isLoon()||this.isStash())&&$httpClient.get(options,(error,response,body)=>{callback(error,this.adapterStatus(response),body)}),this.isNode()&&this.node.request(options,(error,response,body)=>{callback(error,this.adapterStatus(response),body)}),this.isJSBox()&&("string"==typeof options&&(options={url:options}),options.header=options.headers,options.handler=function(resp){let error=resp.error;error&&(error=JSON.stringify(resp.error));let body=resp.data;"object"==typeof body&&(body=JSON.stringify(resp.data)),callback(error,this.adapterStatus(resp.response),body)},$http.get(options))}post(options,callback=(()=>{})){this.isQuanX()&&("string"==typeof options&&(options={url:options}),options.method="POST",$task.fetch(options).then(response=>{callback(null,this.adapterStatus(response),response.body)},reason=>callback(reason.error,null,null))),(this.isSurge()||this.isLoon()||this.isStash())&&$httpClient.post(options,(error,response,body)=>{callback(error,this.adapterStatus(response),body)}),this.isNode()&&this.node.request.post(options,(error,response,body)=>{callback(error,this.adapterStatus(response),body)}),this.isJSBox()&&("string"==typeof options&&(options={url:options}),options.header=options.headers,options.handler=function(resp){let error=resp.error;error&&(error=JSON.stringify(resp.error));let body=resp.data;"object"==typeof body&&(body=JSON.stringify(resp.data)),callback(error,this.adapterStatus(resp.response),body)},$http.post(options))}put(options,callback=(()=>{})){this.isQuanX()&&("string"==typeof options&&(options={url:options}),options.method="PUT",$task.fetch(options).then(response=>{callback(null,this.adapterStatus(response),response.body)},reason=>callback(reason.error,null,null))),(this.isSurge()||this.isLoon()||this.isStash())&&(options.method="PUT",$httpClient.put(options,(error,response,body)=>{callback(error,this.adapterStatus(response),body)})),this.isNode()&&(options.method="PUT",this.node.request.put(options,(error,response,body)=>{callback(error,this.adapterStatus(response),body)})),this.isJSBox()&&("string"==typeof options&&(options={url:options}),options.header=options.headers,options.handler=function(resp){let error=resp.error;error&&(error=JSON.stringify(resp.error));let body=resp.data;"object"==typeof body&&(body=JSON.stringify(resp.data)),callback(error,this.adapterStatus(resp.response),body)},$http.post(options))}costTime(){let info=`${this.name}执行完毕!`;this.isNode()&&this.isExecComm&&(info=`指令【${this.comm[1]}】执行完毕!`);const endTime=(new Date).getTime(),ms=endTime-this.startTime,costTime=ms/1e3;this.execCount++,this.costTotalMs+=ms,this.log(`${info}耗时【${costTime}】秒\n总共执行【${this.execCount}】次,平均耗时【${(this.costTotalMs/this.execCount/1e3).toFixed(4)}】秒`),this.setVal(this.costTotalStringKey,JSON.stringify(`${this.costTotalMs},${this.execCount}`))}done(value={}){this.costTime(),(this.isSurge()||this.isQuanX()||this.isLoon()||this.isStash())&&$done(value)}getRequestUrl(){return $request.url}getResponseBody(){return $response.body}isGetCookie(reg){return!("OPTIONS"==$request.method||!this.getRequestUrl().match(reg))}isEmpty(obj){return void 0===obj||null==obj||""==obj||"null"==obj||"undefined"==obj||0===obj.length}randomString(len){len=len||32;var $chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890",maxPos=$chars.length,pwd="";for(let i=0;i<len;i++)pwd+=$chars.charAt(Math.floor(Math.random()*maxPos));return pwd}autoComplete(str,prefix,suffix,fill,len,direction,ifCode,clen,startIndex,cstr){if((str+="").length<len)for(;str.length<len;)0==direction?str+=fill:str=fill+str;if(ifCode){let temp="";for(var i=0;i<clen;i++)temp+=cstr;str=str.substring(0,startIndex)+temp+str.substring(clen+startIndex)}return str=prefix+str+suffix,this.toDBC(str)}customReplace(str,param,prefix,suffix){try{this.isEmpty(prefix)&&(prefix="#{"),this.isEmpty(suffix)&&(suffix="}");for(let i in param)str=str.replace(`${prefix}${i}${suffix}`,param[i])}catch(e){this.logErr(e)}return str}toDBC(txtstring){for(var tmp="",i=0;i<txtstring.length;i++)32==txtstring.charCodeAt(i)?tmp+=String.fromCharCode(12288):txtstring.charCodeAt(i)<127&&(tmp+=String.fromCharCode(txtstring.charCodeAt(i)+65248));return tmp}hash(str){let h=0,i,chr;for(i=0;i<str.length;i++)chr=str.charCodeAt(i),h=(h<<5)-h+chr,h|=0;return String(h)}formatDate(date,format){let o={"M+":date.getMonth()+1,"d+":date.getDate(),"H+":date.getHours(),"m+":date.getMinutes(),"s+":date.getSeconds(),"q+":Math.floor((date.getMonth()+3)/3),S:date.getMilliseconds()};/(y+)/.test(format)&&(format=format.replace(RegExp.$1,(date.getFullYear()+"").substr(4-RegExp.$1.length)));for(let k in o)new RegExp("("+k+")").test(format)&&(format=format.replace(RegExp.$1,1==RegExp.$1.length?o[k]:("00"+o[k]).substr((""+o[k]).length)));return format}}(scriptName,scriptId,options)}
|