├── 1.txt ├── .gitignore ├── .github └── workflows │ ├── Update work.yaml │ └── npm-publish.yml ├── package.json ├── README.md ├── app.js ├── sendNotify.js └── Env.min.js /1.txt: -------------------------------------------------------------------------------- 1 | 这是一个新的文档 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | .DS_Store 3 | 4 | 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | 12 | # Dependency directories 13 | node_modules/ 14 | package-lock.json 15 | 16 | Manga.js 17 | result.txt -------------------------------------------------------------------------------- /.github/workflows/Update work.yaml: -------------------------------------------------------------------------------- 1 | name: Github iQIYI-DailyBonus 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | watch: 8 | types: started 9 | schedule: 10 | - cron: '5 16 * * *' 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - name: Checkout codes 17 | uses: actions/checkout@v2 18 | - name: Use Node.js 19 | uses: actions/setup-node@v1 20 | with: 21 | node-version: '12.x' 22 | - name: Run app 23 | run: npm install 24 | - run: node app.js 25 | env: 26 | iQIYI_COOKIE: ${{ secrets.iQIYI_COOKIE }} 27 | PUSH_KEY: ${{ secrets.PUSH_KEY }} 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "iQIYI-DailyBonus", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "Manga.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/BlueskyClouds/iQIYI-DailyBonus.git" 12 | }, 13 | "keywords": [], 14 | "author": "", 15 | "license": "ISC", 16 | "bugs": { 17 | "url": "https://github.com/BlueskyClouds/iQIYI-DailyBonus/issues" 18 | }, 19 | "homepage": "https://github.com/BlueskyClouds/iQIYI-DailyBonusa#readme", 20 | "dependencies": { 21 | "download": "^8.0.0", 22 | "request": "^2.88.2", 23 | "request-promise": "^4.2.5" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # 爱奇艺自动签到 4 | 功能: 5 | 1. 获取签到最新代码 6 | 2. 替换参数值 7 | 3. 签到并发送通知 8 | # 使用方式 9 | 1. 打开爱奇艺官网 获取你的authcookie 获取方式 [文字教程](https://www.jianshu.com/p/b3759d78392b) ) authcookie有效期一般三个月 10 | 2. 右上角fork本仓库 11 | 3. 点击Settings -> Secrets -> 点击绿色按钮 (如无绿色按钮说明已激活。直接到第四步。) 12 | 4. 新增 new secret 参数名IQIYI_COOKIE 值是你刚才获取的authcookie 13 | 5. 任意修改仓库内任意文件或点击右上角 Star 即可触发。 14 | 15 | **本项目需要设置的 Secrets:** 16 | 17 | | 名称 | 内容 | 类型 | 说明| 18 | | -------- | ------------- | ------ | ----- | 19 | | IQIYI_COOKIE | 爱奇艺authcookie | 必写参数 | 20 | | PUSH_KEY | Server酱SCKEY值 | 可选参数 | cookie失效推送[server酱的微信通知](http://sc.ftqq.com/3.version) | 21 | | BARK_PUSH | Bark推送值 | 可选参数 | 此内容支持自建Bark添加整个链接即可(自建链接切记删除最后一个/ 比如你的是https://a.a.com/ 只需要填写https://a.a.com即可)| 22 | |BARK_SOUND | BARK app推送铃声|可选参数|BARK app推送铃声,铃声列表去APP查看复制填写| 23 | 24 | 可使用Star触发,点击自己仓库右上角Star即可激活,如是Unstar状态需要点击两次即可。 25 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages 3 | 4 | name: Node.js Package 5 | 6 | on: 7 | release: 8 | types: [created] 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: actions/setup-node@v1 16 | with: 17 | node-version: 12 18 | - run: npm ci 19 | - run: npm test 20 | 21 | publish-npm: 22 | needs: build 23 | runs-on: ubuntu-latest 24 | steps: 25 | - uses: actions/checkout@v2 26 | - uses: actions/setup-node@v1 27 | with: 28 | node-version: 12 29 | registry-url: https://registry.npmjs.org/ 30 | - run: npm ci 31 | - run: npm publish 32 | env: 33 | NODE_AUTH_TOKEN: ${{secrets.npm_token}} 34 | 35 | publish-gpr: 36 | needs: build 37 | runs-on: ubuntu-latest 38 | steps: 39 | - uses: actions/checkout@v2 40 | - uses: actions/setup-node@v1 41 | with: 42 | node-version: 12 43 | registry-url: https://npm.pkg.github.com/ 44 | - run: npm ci 45 | - run: npm publish 46 | env: 47 | NODE_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} 48 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | // version v0.0.1 2 | // create by BlueSkyClouds 3 | // detail url: https://github.com/BlueskyClouds/iQIYI-DailyBonus 4 | 5 | const exec = require('child_process').execSync 6 | const fs = require('fs') 7 | const download = require('download') 8 | 9 | const $ = new Env('爱奇艺会员签到'); 10 | const notify = $.isNode() ? require('./sendNotify') : ''; 11 | // 公共变量 12 | const KEY = process.env.iQIYI_COOKIE 13 | 14 | async function downFile () { 15 | const url = 'https://raw.githubusercontent.com/NobyDa/Script/master/iQIYI-DailyBonus/iQIYI.js' 16 | await download(url, './') 17 | } 18 | 19 | async function changeFiele () { 20 | let content = await fs.readFileSync('./iQIYI.js', 'utf8') 21 | content = content.replace(/var cookie = ''/, `var cookie = '${KEY}'`) 22 | await fs.writeFileSync( './iQIYI.js', content, 'utf8') 23 | } 24 | 25 | async function deleteFile(path) { 26 | // 查看文件result.txt是 否存在,如果存在,先删除 27 | const fileExists = await fs.existsSync(path); 28 | // console.log('fileExists', fileExists); 29 | if (fileExists) { 30 | const unlinkRes = await fs.unlinkSync(path); 31 | // console.log('unlinkRes', unlinkRes) 32 | } 33 | } 34 | 35 | async function start() { 36 | if (!KEY) { 37 | console.log('请填写 key 后在继续') 38 | return 39 | } 40 | // 下载最新代码 41 | await downFile(); 42 | console.log('下载代码完毕') 43 | // 替换变量 44 | await changeFiele(); 45 | console.log('替换变量完毕') 46 | // 执行 47 | await exec("node iQIYI.js >> result.txt"); 48 | console.log('执行完毕') 49 | const path = "./result.txt"; 50 | let content = ""; 51 | if (fs.existsSync(path)) { 52 | content = fs.readFileSync(path, "utf8"); 53 | } 54 | await notify.sendNotify("爱奇艺签到-" + new Date().toLocaleDateString(), content); 55 | console.log("爱奇艺签到-" + content) 56 | 57 | //运行完成后,删除下载的文件 58 | console.log('运行完成后,删除下载的文件\n') 59 | await deleteFile(path); 60 | 61 | } 62 | 63 | start() 64 | 65 | // prettier-ignore 66 | function Env(t,s){return new class{constructor(t,s){this.name=t,this.data=null,this.dataFile="box.dat",this.logs=[],this.logSeparator="\n",this.startTime=(new Date).getTime(),Object.assign(this,s),this.log("",`\ud83d\udd14${this.name}, \u5f00\u59cb!`)}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}getScript(t){return new Promise(s=>{$.get({url:t},(t,e,i)=>s(i))})}runScript(t,s){return new Promise(e=>{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=s&&s.timeout?s.timeout:o;const[h,a]=i.split("@"),r={url:`http://${a}/v1/scripting/evaluate`,body:{script_text:t,mock_type:"cron",timeout:o},headers:{"X-Key":h,Accept:"*/*"}};$.post(r,(t,s,i)=>e(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),s=this.path.resolve(process.cwd(),this.dataFile),e=this.fs.existsSync(t),i=!e&&this.fs.existsSync(s);if(!e&&!i)return{};{const i=e?t:s;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),s=this.path.resolve(process.cwd(),this.dataFile),e=this.fs.existsSync(t),i=!e&&this.fs.existsSync(s),o=JSON.stringify(this.data);e?this.fs.writeFileSync(t,o):i?this.fs.writeFileSync(s,o):this.fs.writeFileSync(t,o)}}lodash_get(t,s,e){const i=s.replace(/\[(\d+)\]/g,".$1").split(".");let o=t;for(const t of i)if(o=Object(o)[t],void 0===o)return e;return o}lodash_set(t,s,e){return Object(t)!==t?t:(Array.isArray(s)||(s=s.toString().match(/[^.[\]]+/g)||[]),s.slice(0,-1).reduce((t,e,i)=>Object(t[e])===t[e]?t[e]:t[e]=Math.abs(s[i+1])>>0==+s[i+1]?[]:{},t)[s[s.length-1]]=e,t)}getdata(t){let s=this.getval(t);if(/^@/.test(t)){const[,e,i]=/^@(.*?)\.(.*?)$/.exec(t),o=e?this.getval(e):"";if(o)try{const t=JSON.parse(o);s=t?this.lodash_get(t,i,""):s}catch(t){s=""}}return s}setdata(t,s){let e=!1;if(/^@/.test(s)){const[,i,o]=/^@(.*?)\.(.*?)$/.exec(s),h=this.getval(i),a=i?"null"===h?null:h||"{}":"{}";try{const s=JSON.parse(a);this.lodash_set(s,o,t),e=this.setval(JSON.stringify(s),i)}catch(s){const h={};this.lodash_set(h,o,t),e=this.setval(JSON.stringify(h),i)}}else e=$.setval(t,s);return e}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,s){return this.isSurge()||this.isLoon()?$persistentStore.write(t,s):this.isQuanX()?$prefs.setValueForKey(t,s):this.isNode()?(this.data=this.loaddata(),this.data[s]=t,this.writedata(),!0):this.data&&this.data[s]||null}initGotEnv(t){this.got=this.got?this.got:require("got"),this.cktough=this.cktough?this.cktough:require("tough-cookie"),this.ckjar=this.ckjar?this.ckjar:new this.cktough.CookieJar,t&&(t.headers=t.headers?t.headers:{},void 0===t.headers.Cookie&&void 0===t.cookieJar&&(t.cookieJar=this.ckjar))}get(t,s=(()=>{})){t.headers&&(delete t.headers["Content-Type"],delete t.headers["Content-Length"]),this.isSurge()||this.isLoon()?$httpClient.get(t,(t,e,i)=>{!t&&e&&(e.body=i,e.statusCode=e.status),s(t,e,i)}):this.isQuanX()?$task.fetch(t).then(t=>{const{statusCode:e,statusCode:i,headers:o,body:h}=t;s(null,{status:e,statusCode:i,headers:o,body:h},h)},t=>s(t)):this.isNode()&&(this.initGotEnv(t),this.got(t).on("redirect",(t,s)=>{try{const e=t.headers["set-cookie"].map(this.cktough.Cookie.parse).toString();this.ckjar.setCookieSync(e,null),s.cookieJar=this.ckjar}catch(t){this.logErr(t)}}).then(t=>{const{statusCode:e,statusCode:i,headers:o,body:h}=t;s(null,{status:e,statusCode:i,headers:o,body:h},h)},t=>s(t)))}post(t,s=(()=>{})){if(t.body&&t.headers&&!t.headers["Content-Type"]&&(t.headers["Content-Type"]="application/x-www-form-urlencoded"),delete t.headers["Content-Length"],this.isSurge()||this.isLoon())$httpClient.post(t,(t,e,i)=>{!t&&e&&(e.body=i,e.statusCode=e.status),s(t,e,i)});else if(this.isQuanX())t.method="POST",$task.fetch(t).then(t=>{const{statusCode:e,statusCode:i,headers:o,body:h}=t;s(null,{status:e,statusCode:i,headers:o,body:h},h)},t=>s(t));else if(this.isNode()){this.initGotEnv(t);const{url:e,...i}=t;this.got.post(e,i).then(t=>{const{statusCode:e,statusCode:i,headers:o,body:h}=t;s(null,{status:e,statusCode:i,headers:o,body:h},h)},t=>s(t))}}time(t){let s={"M+":(new Date).getMonth()+1,"d+":(new Date).getDate(),"H+":(new Date).getHours(),"m+":(new Date).getMinutes(),"s+":(new Date).getSeconds(),"q+":Math.floor(((new Date).getMonth()+3)/3),S:(new Date).getMilliseconds()};/(y+)/.test(t)&&(t=t.replace(RegExp.$1,((new Date).getFullYear()+"").substr(4-RegExp.$1.length)));for(let e in s)new RegExp("("+e+")").test(t)&&(t=t.replace(RegExp.$1,1==RegExp.$1.length?s[e]:("00"+s[e]).substr((""+s[e]).length)));return t}msg(s=t,e="",i="",o){const h=t=>!t||!this.isLoon()&&this.isSurge()?t:"string"==typeof t?this.isLoon()?t:this.isQuanX()?{"open-url":t}:void 0:"object"==typeof t&&(t["open-url"]||t["media-url"])?this.isLoon()?t["open-url"]:this.isQuanX()?t:void 0:void 0;$.isMute||(this.isSurge()||this.isLoon()?$notification.post(s,e,i,h(o)):this.isQuanX()&&$notify(s,e,i,h(o))),this.logs.push("","==============\ud83d\udce3\u7cfb\u7edf\u901a\u77e5\ud83d\udce3=============="),this.logs.push(s),e&&this.logs.push(e),i&&this.logs.push(i)}log(...t){t.length>0?this.logs=[...this.logs,...t]:console.log(this.logs.join(this.logSeparator))}logErr(t,s){const e=!this.isSurge()&&!this.isQuanX()&&!this.isLoon();e?$.log("",`\u2757\ufe0f${this.name}, \u9519\u8bef!`,t.stack):$.log("",`\u2757\ufe0f${this.name}, \u9519\u8bef!`,t)}wait(t){return new Promise(s=>setTimeout(s,t))}done(t={}){const s=(new Date).getTime(),e=(s-this.startTime)/1e3;this.log("",`\ud83d\udd14${this.name}, \u7ed3\u675f! \ud83d\udd5b ${e} \u79d2`),this.log(),(this.isSurge()||this.isQuanX()||this.isLoon())&&$done(t)}}(t,s)} 67 | -------------------------------------------------------------------------------- /sendNotify.js: -------------------------------------------------------------------------------- 1 | const $ = new Env(); 2 | // =======================================微信server酱通知设置区域=========================================== 3 | //此处填你申请的SCKEY. 4 | //注:此处设置github action用户填写到Settings-Secrets里面(Name输入PUSH_KEY) 5 | let SCKEY = ''; 6 | 7 | // =======================================Bark App通知设置区域=========================================== 8 | //此处填你BarkAPP的信息(IP/设备码,例如:https://api.day.app/XXXXXXXX) 9 | //注:此处设置github action用户填写到Settings-Secrets里面(Name输入BARK_PUSH) 10 | let BARK_PUSH = ''; 11 | //BARK app推送铃声,铃声列表去APP查看复制填写 12 | //注:此处设置github action用户填写到Settings-Secrets里面(Name输入BARK_SOUND , Value输入app提供的铃声名称,例如:birdsong) 13 | let BARK_SOUND = ''; 14 | 15 | const rp = require('request-promise') 16 | 17 | if (process.env.PUSH_KEY) { 18 | SCKEY = process.env.PUSH_KEY; 19 | } 20 | if (process.env.BARK_PUSH) { 21 | if(process.env.BARK_PUSH.indexOf('https') > -1 || process.env.BARK_PUSH.indexOf('http') > -1) { 22 | //兼容BARK自建用户 23 | BARK_PUSH = process.env.BARK_PUSH 24 | } else { 25 | BARK_PUSH = `https://api.day.app/${process.env.BARK_PUSH}` 26 | } 27 | if (process.env.BARK_SOUND) { 28 | BARK_SOUND = process.env.BARK_SOUND 29 | } 30 | } else { 31 | if(BARK_PUSH && BARK_PUSH.indexOf('https') === -1 && BARK_PUSH.indexOf('http') === -1) { 32 | //兼容BARK本地用户只填写设备码的情况 33 | BARK_PUSH = `https://api.day.app/${BARK_PUSH}` 34 | } 35 | } 36 | 37 | async function sendNotify(text, desp) { 38 | //提供的通知 39 | await serverNotify(text, desp); 40 | await BarkNotify(text, desp); 41 | } 42 | 43 | function serverNotify(text, desp) { 44 | return new Promise(resolve => { 45 | if (SCKEY) { 46 | //微信server酱推送通知一个\n不会换行,需要两个\n才能换行,故做此替换 47 | desp = desp.replace(/[\n\r]/g, '\n\n'); 48 | const options = { 49 | url: `https://sc.ftqq.com/${SCKEY}.send`, 50 | body: `text=${text}&desp=${desp}`, 51 | headers: { 52 | 'Content-Type': 'application/x-www-form-urlencoded' 53 | } 54 | } 55 | $.post(options, (err, resp, data) => { 56 | try { 57 | if (err) { 58 | console.log('\n发送通知调用API失败!!\n') 59 | console.log(err); 60 | } else { 61 | data = JSON.parse(data); 62 | if (data.errno === 0) { 63 | console.log('\nserver酱发送通知消息成功\n') 64 | } else if (data.errno === 1024) { 65 | console.log('\nPUSH_KEY 错误\n') 66 | } 67 | } 68 | } catch (e) { 69 | $.logErr(e, resp); 70 | } finally { 71 | resolve(data); 72 | } 73 | }) 74 | } else { 75 | console.log('\n您未提供server酱的SCKEY,取消微信推送消息通知\n'); 76 | resolve() 77 | } 78 | }) 79 | } 80 | 81 | function BarkNotify(text, desp) { 82 | return new Promise(resolve => { 83 | if (BARK_PUSH) { 84 | const options = { 85 | url: `${BARK_PUSH}/${encodeURIComponent(text)}/${encodeURIComponent(desp)}?sound=${BARK_SOUND}`, 86 | json: true, 87 | method: 'GET' 88 | } 89 | rp.get(options).then(res=>{ 90 | console.log(res) 91 | }).catch((err)=>{ 92 | console.log('\nBark APP发送通知调用API失败!!\n') 93 | console.log(err) 94 | }) 95 | } else { 96 | console.log('\n您未提供Bark的APP推送BARK_PUSH,取消Bark推送消息通知\n'); 97 | resolve() 98 | } 99 | }) 100 | } 101 | 102 | module.exports = { 103 | sendNotify, 104 | BarkNotify, 105 | SCKEY, 106 | BARK_PUSH 107 | } 108 | 109 | // prettier-ignore 110 | function Env(t,s){return new class{constructor(t,s){this.name=t,this.data=null,this.dataFile="box.dat",this.logs=[],this.logSeparator="\n",this.startTime=(new Date).getTime(),Object.assign(this,s),this.log("",`\ud83d\udd14${this.name}, \u5f00\u59cb!`)}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}getScript(t){return new Promise(s=>{$.get({url:t},(t,e,i)=>s(i))})}runScript(t,s){return new Promise(e=>{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=s&&s.timeout?s.timeout:o;const[h,a]=i.split("@"),r={url:`http://${a}/v1/scripting/evaluate`,body:{script_text:t,mock_type:"cron",timeout:o},headers:{"X-Key":h,Accept:"*/*"}};$.post(r,(t,s,i)=>e(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),s=this.path.resolve(process.cwd(),this.dataFile),e=this.fs.existsSync(t),i=!e&&this.fs.existsSync(s);if(!e&&!i)return{};{const i=e?t:s;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),s=this.path.resolve(process.cwd(),this.dataFile),e=this.fs.existsSync(t),i=!e&&this.fs.existsSync(s),o=JSON.stringify(this.data);e?this.fs.writeFileSync(t,o):i?this.fs.writeFileSync(s,o):this.fs.writeFileSync(t,o)}}lodash_get(t,s,e){const i=s.replace(/\[(\d+)\]/g,".$1").split(".");let o=t;for(const t of i)if(o=Object(o)[t],void 0===o)return e;return o}lodash_set(t,s,e){return Object(t)!==t?t:(Array.isArray(s)||(s=s.toString().match(/[^.[\]]+/g)||[]),s.slice(0,-1).reduce((t,e,i)=>Object(t[e])===t[e]?t[e]:t[e]=Math.abs(s[i+1])>>0==+s[i+1]?[]:{},t)[s[s.length-1]]=e,t)}getdata(t){let s=this.getval(t);if(/^@/.test(t)){const[,e,i]=/^@(.*?)\.(.*?)$/.exec(t),o=e?this.getval(e):"";if(o)try{const t=JSON.parse(o);s=t?this.lodash_get(t,i,""):s}catch(t){s=""}}return s}setdata(t,s){let e=!1;if(/^@/.test(s)){const[,i,o]=/^@(.*?)\.(.*?)$/.exec(s),h=this.getval(i),a=i?"null"===h?null:h||"{}":"{}";try{const s=JSON.parse(a);this.lodash_set(s,o,t),e=this.setval(JSON.stringify(s),i)}catch(s){const h={};this.lodash_set(h,o,t),e=this.setval(JSON.stringify(h),i)}}else e=$.setval(t,s);return e}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,s){return this.isSurge()||this.isLoon()?$persistentStore.write(t,s):this.isQuanX()?$prefs.setValueForKey(t,s):this.isNode()?(this.data=this.loaddata(),this.data[s]=t,this.writedata(),!0):this.data&&this.data[s]||null}initGotEnv(t){this.got=this.got?this.got:require("got"),this.cktough=this.cktough?this.cktough:require("tough-cookie"),this.ckjar=this.ckjar?this.ckjar:new this.cktough.CookieJar,t&&(t.headers=t.headers?t.headers:{},void 0===t.headers.Cookie&&void 0===t.cookieJar&&(t.cookieJar=this.ckjar))}get(t,s=(()=>{})){t.headers&&(delete t.headers["Content-Type"],delete t.headers["Content-Length"]),this.isSurge()||this.isLoon()?$httpClient.get(t,(t,e,i)=>{!t&&e&&(e.body=i,e.statusCode=e.status),s(t,e,i)}):this.isQuanX()?$task.fetch(t).then(t=>{const{statusCode:e,statusCode:i,headers:o,body:h}=t;s(null,{status:e,statusCode:i,headers:o,body:h},h)},t=>s(t)):this.isNode()&&(this.initGotEnv(t),this.got(t).on("redirect",(t,s)=>{try{const e=t.headers["set-cookie"].map(this.cktough.Cookie.parse).toString();this.ckjar.setCookieSync(e,null),s.cookieJar=this.ckjar}catch(t){this.logErr(t)}}).then(t=>{const{statusCode:e,statusCode:i,headers:o,body:h}=t;s(null,{status:e,statusCode:i,headers:o,body:h},h)},t=>s(t)))}post(t,s=(()=>{})){if(t.body&&t.headers&&!t.headers["Content-Type"]&&(t.headers["Content-Type"]="application/x-www-form-urlencoded"),delete t.headers["Content-Length"],this.isSurge()||this.isLoon())$httpClient.post(t,(t,e,i)=>{!t&&e&&(e.body=i,e.statusCode=e.status),s(t,e,i)});else if(this.isQuanX())t.method="POST",$task.fetch(t).then(t=>{const{statusCode:e,statusCode:i,headers:o,body:h}=t;s(null,{status:e,statusCode:i,headers:o,body:h},h)},t=>s(t));else if(this.isNode()){this.initGotEnv(t);const{url:e,...i}=t;this.got.post(e,i).then(t=>{const{statusCode:e,statusCode:i,headers:o,body:h}=t;s(null,{status:e,statusCode:i,headers:o,body:h},h)},t=>s(t))}}time(t){let s={"M+":(new Date).getMonth()+1,"d+":(new Date).getDate(),"H+":(new Date).getHours(),"m+":(new Date).getMinutes(),"s+":(new Date).getSeconds(),"q+":Math.floor(((new Date).getMonth()+3)/3),S:(new Date).getMilliseconds()};/(y+)/.test(t)&&(t=t.replace(RegExp.$1,((new Date).getFullYear()+"").substr(4-RegExp.$1.length)));for(let e in s)new RegExp("("+e+")").test(t)&&(t=t.replace(RegExp.$1,1==RegExp.$1.length?s[e]:("00"+s[e]).substr((""+s[e]).length)));return t}msg(s=t,e="",i="",o){const h=t=>!t||!this.isLoon()&&this.isSurge()?t:"string"==typeof t?this.isLoon()?t:this.isQuanX()?{"open-url":t}:void 0:"object"==typeof t&&(t["open-url"]||t["media-url"])?this.isLoon()?t["open-url"]:this.isQuanX()?t:void 0:void 0;$.isMute||(this.isSurge()||this.isLoon()?$notification.post(s,e,i,h(o)):this.isQuanX()&&$notify(s,e,i,h(o))),this.logs.push("","==============\ud83d\udce3\u7cfb\u7edf\u901a\u77e5\ud83d\udce3=============="),this.logs.push(s),e&&this.logs.push(e),i&&this.logs.push(i)}log(...t){t.length>0?this.logs=[...this.logs,...t]:console.log(this.logs.join(this.logSeparator))}logErr(t,s){const e=!this.isSurge()&&!this.isQuanX()&&!this.isLoon();e?$.log("",`\u2757\ufe0f${this.name}, \u9519\u8bef!`,t.stack):$.log("",`\u2757\ufe0f${this.name}, \u9519\u8bef!`,t)}wait(t){return new Promise(s=>setTimeout(s,t))}done(t={}){const s=(new Date).getTime(),e=(s-this.startTime)/1e3;this.log("",`\ud83d\udd14${this.name}, \u7ed3\u675f! \ud83d\udd5b ${e} \u79d2`),this.log(),(this.isSurge()||this.isQuanX()||this.isLoon())&&$done(t)}}(t,s)} 111 | -------------------------------------------------------------------------------- /Env.min.js: -------------------------------------------------------------------------------- 1 | function EnvMin(name, opts) { 2 | class Http { 3 | constructor(env) { 4 | this.env = env 5 | } 6 | 7 | send(opts, method = 'GET') { 8 | opts = typeof opts === 'string' ? { url: opts } : opts 9 | let sender = this.get 10 | if (method === 'POST') { 11 | sender = this.post 12 | } 13 | return new Promise((resolve, reject) => { 14 | sender.call(this, opts, (err, resp, body) => { 15 | if (err) reject(err) 16 | else resolve(resp) 17 | }) 18 | }) 19 | } 20 | 21 | get(opts) { 22 | return this.send.call(this.env, opts) 23 | } 24 | 25 | post(opts) { 26 | return this.send.call(this.env, opts, 'POST') 27 | } 28 | } 29 | 30 | return new (class { 31 | constructor(name, opts) { 32 | this.name = name 33 | this.http = new Http(this) 34 | this.data = null 35 | this.dataFile = 'box.dat' 36 | this.logs = [] 37 | this.isMute = false 38 | this.isNeedRewrite = false 39 | this.logSeparator = '\n' 40 | this.startTime = new Date().getTime() 41 | Object.assign(this, opts) 42 | this.log('', `🔔${this.name}, 开始!`) 43 | } 44 | 45 | isNode() { 46 | return 'undefined' !== typeof module && !!module.exports 47 | } 48 | 49 | isQuanX() { 50 | return 'undefined' !== typeof $task 51 | } 52 | 53 | isSurge() { 54 | return 'undefined' !== typeof $httpClient && 'undefined' === typeof $loon 55 | } 56 | 57 | isLoon() { 58 | return 'undefined' !== typeof $loon 59 | } 60 | 61 | toObj(str, defaultValue = null) { 62 | try { 63 | return JSON.parse(str) 64 | } catch { 65 | return defaultValue 66 | } 67 | } 68 | 69 | toStr(obj, defaultValue = null) { 70 | try { 71 | return JSON.stringify(obj) 72 | } catch { 73 | return defaultValue 74 | } 75 | } 76 | 77 | getjson(key, defaultValue) { 78 | let json = defaultValue 79 | const val = this.getdata(key) 80 | if (val) { 81 | try { 82 | json = JSON.parse(this.getdata(key)) 83 | } catch {} 84 | } 85 | return json 86 | } 87 | 88 | setjson(val, key) { 89 | try { 90 | return this.setdata(JSON.stringify(val), key) 91 | } catch { 92 | return false 93 | } 94 | } 95 | 96 | getScript(url) { 97 | return new Promise((resolve) => { 98 | this.get({ url }, (err, resp, body) => resolve(body)) 99 | }) 100 | } 101 | 102 | runScript(script, runOpts) { 103 | return new Promise((resolve) => { 104 | let httpapi = this.getdata('@chavy_boxjs_userCfgs.httpapi') 105 | httpapi = httpapi ? httpapi.replace(/\n/g, '').trim() : httpapi 106 | let httpapi_timeout = this.getdata('@chavy_boxjs_userCfgs.httpapi_timeout') 107 | httpapi_timeout = httpapi_timeout ? httpapi_timeout * 1 : 20 108 | httpapi_timeout = runOpts && runOpts.timeout ? runOpts.timeout : httpapi_timeout 109 | const [key, addr] = httpapi.split('@') 110 | const opts = { 111 | url: `http://${addr}/v1/scripting/evaluate`, 112 | body: { script_text: script, mock_type: 'cron', timeout: httpapi_timeout }, 113 | headers: { 'X-Key': key, 'Accept': '*/*' } 114 | } 115 | this.post(opts, (err, resp, body) => resolve(body)) 116 | }).catch((e) => this.logErr(e)) 117 | } 118 | 119 | loaddata() { 120 | if (this.isNode()) { 121 | this.fs = this.fs ? this.fs : require('fs') 122 | this.path = this.path ? this.path : require('path') 123 | const curDirDataFilePath = this.path.resolve(this.dataFile) 124 | const rootDirDataFilePath = this.path.resolve(process.cwd(), this.dataFile) 125 | const isCurDirDataFile = this.fs.existsSync(curDirDataFilePath) 126 | const isRootDirDataFile = !isCurDirDataFile && this.fs.existsSync(rootDirDataFilePath) 127 | if (isCurDirDataFile || isRootDirDataFile) { 128 | const datPath = isCurDirDataFile ? curDirDataFilePath : rootDirDataFilePath 129 | try { 130 | return JSON.parse(this.fs.readFileSync(datPath)) 131 | } catch (e) { 132 | return {} 133 | } 134 | } else return {} 135 | } else return {} 136 | } 137 | 138 | writedata() { 139 | if (this.isNode()) { 140 | this.fs = this.fs ? this.fs : require('fs') 141 | this.path = this.path ? this.path : require('path') 142 | const curDirDataFilePath = this.path.resolve(this.dataFile) 143 | const rootDirDataFilePath = this.path.resolve(process.cwd(), this.dataFile) 144 | const isCurDirDataFile = this.fs.existsSync(curDirDataFilePath) 145 | const isRootDirDataFile = !isCurDirDataFile && this.fs.existsSync(rootDirDataFilePath) 146 | const jsondata = JSON.stringify(this.data) 147 | if (isCurDirDataFile) { 148 | this.fs.writeFileSync(curDirDataFilePath, jsondata) 149 | } else if (isRootDirDataFile) { 150 | this.fs.writeFileSync(rootDirDataFilePath, jsondata) 151 | } else { 152 | this.fs.writeFileSync(curDirDataFilePath, jsondata) 153 | } 154 | } 155 | } 156 | 157 | lodash_get(source, path, defaultValue = undefined) { 158 | const paths = path.replace(/\[(\d+)\]/g, '.$1').split('.') 159 | let result = source 160 | for (const p of paths) { 161 | result = Object(result)[p] 162 | if (result === undefined) { 163 | return defaultValue 164 | } 165 | } 166 | return result 167 | } 168 | 169 | lodash_set(obj, path, value) { 170 | if (Object(obj) !== obj) return obj 171 | if (!Array.isArray(path)) path = path.toString().match(/[^.[\]]+/g) || [] 172 | path 173 | .slice(0, -1) 174 | .reduce((a, c, i) => (Object(a[c]) === a[c] ? a[c] : (a[c] = Math.abs(path[i + 1]) >> 0 === +path[i + 1] ? [] : {})), obj)[ 175 | path[path.length - 1] 176 | ] = value 177 | return obj 178 | } 179 | 180 | getdata(key) { 181 | let val = this.getval(key) 182 | // 如果以 @ 183 | if (/^@/.test(key)) { 184 | const [, objkey, paths] = /^@(.*?)\.(.*?)$/.exec(key) 185 | const objval = objkey ? this.getval(objkey) : '' 186 | if (objval) { 187 | try { 188 | const objedval = JSON.parse(objval) 189 | val = objedval ? this.lodash_get(objedval, paths, '') : val 190 | } catch (e) { 191 | val = '' 192 | } 193 | } 194 | } 195 | return val 196 | } 197 | 198 | setdata(val, key) { 199 | let issuc = false 200 | if (/^@/.test(key)) { 201 | const [, objkey, paths] = /^@(.*?)\.(.*?)$/.exec(key) 202 | const objdat = this.getval(objkey) 203 | const objval = objkey ? (objdat === 'null' ? null : objdat || '{}') : '{}' 204 | try { 205 | const objedval = JSON.parse(objval) 206 | this.lodash_set(objedval, paths, val) 207 | issuc = this.setval(JSON.stringify(objedval), objkey) 208 | } catch (e) { 209 | const objedval = {} 210 | this.lodash_set(objedval, paths, val) 211 | issuc = this.setval(JSON.stringify(objedval), objkey) 212 | } 213 | } else { 214 | issuc = this.setval(val, key) 215 | } 216 | return issuc 217 | } 218 | 219 | getval(key) { 220 | if (this.isSurge() || this.isLoon()) { 221 | return $persistentStore.read(key) 222 | } else if (this.isQuanX()) { 223 | return $prefs.valueForKey(key) 224 | } else if (this.isNode()) { 225 | this.data = this.loaddata() 226 | return this.data[key] 227 | } else { 228 | return (this.data && this.data[key]) || null 229 | } 230 | } 231 | 232 | setval(val, key) { 233 | if (this.isSurge() || this.isLoon()) { 234 | return $persistentStore.write(val, key) 235 | } else if (this.isQuanX()) { 236 | return $prefs.setValueForKey(val, key) 237 | } else if (this.isNode()) { 238 | this.data = this.loaddata() 239 | this.data[key] = val 240 | this.writedata() 241 | return true 242 | } else { 243 | return (this.data && this.data[key]) || null 244 | } 245 | } 246 | 247 | initGotEnv(opts) { 248 | this.got = this.got ? this.got : require('got') 249 | this.cktough = this.cktough ? this.cktough : require('tough-cookie') 250 | this.ckjar = this.ckjar ? this.ckjar : new this.cktough.CookieJar() 251 | if (opts) { 252 | opts.headers = opts.headers ? opts.headers : {} 253 | if (undefined === opts.headers.Cookie && undefined === opts.cookieJar) { 254 | opts.cookieJar = this.ckjar 255 | } 256 | } 257 | } 258 | 259 | get(opts, callback = () => {}) { 260 | if (opts.header s) { 261 | delete opts.headers['Content-Type'] 262 | delete opts.headers['Content-Length'] 263 | } 264 | if (this.isSurge() || this.isLoon()) { 265 | if (this.isSurge() && this.isNeedRewrite) { 266 | opts.headers = opts.headers || {} 267 | Object.assign(opts.headers, { 'X-Surge-Skip-Scripting': false }) 268 | } 269 | $httpClient.get(opts, (err, resp, body) => { 270 | if (!err && resp) { 271 | resp.body = body 272 | resp.statusCode = resp.status 273 | } 274 | callback(err, resp, body) 275 | }) 276 | } else if (this.isQuanX()) { 277 | if (this.isNeedRewrite) { 278 | opts.opts = opts.opts || {} 279 | Object.assign(opts.opts, { hints: false }) 280 | } 281 | $task.fetch(opts).then( 282 | (resp) => { 283 | const { statusCode: status, statusCode, headers, body } = resp 284 | callback(null, { status, statusCode, headers, body }, body) 285 | }, 286 | (err) => callback(err) 287 | ) 288 | } else if (this.isNode()) { 289 | this.initGotEnv(opts) 290 | this.got(opts) 291 | .on('redirect', (resp, nextOpts) => { 292 | try { 293 | if (resp.headers['set-cookie']) { 294 | const ck = resp.headers['set-cookie'].map(this.cktough.Cookie.parse).toString() 295 | this.ckjar.setCookieSync(ck, null) 296 | nextOpts.cookieJar = this.ckjar 297 | } 298 | } catch (e) { 299 | this.logErr(e) 300 | } 301 | // this.ckjar.setCookieSync(resp.headers['set-cookie'].map(Cookie.parse).toString()) 302 | }) 303 | .then( 304 | (resp) => { 305 | const { statusCode: status, statusCode, headers, body } = resp 306 | callback(null, { status, statusCode, headers, body }, body) 307 | }, 308 | (err) => { 309 | const { message: error, response: resp } = err 310 | callback(error, resp, resp && resp.body) 311 | } 312 | ) 313 | } 314 | } 315 | 316 | post(opts, callback = () => {}) { 317 | // 如果指定了请求体, 但没指定`Content-Type`, 则自动生成 318 | if (opts.body && opts.headers && !opts.headers['Content-Type']) { 319 | opts.headers['Content-Type'] = 'application/x-www-form-urlencoded' 320 | } 321 | if (opts.headers) delete opts.headers['Content-Length'] 322 | if (this.isSurge() || this.isLoon()) { 323 | if (this.isSurge() && this.isNeedRewrite) { 324 | opts.headers = opts.headers || {} 325 | Object.assign(opts.headers, { 'X-Surge-Skip-Scripting': false }) 326 | } 327 | $httpClient.post(opts, (err, resp, body) => { 328 | if (!err && resp) { 329 | resp.body = body 330 | resp.statusCode = resp.status 331 | } 332 | callback(err, resp, body) 333 | }) 334 | } else if (this.isQuanX()) { 335 | opts.method = 'POST' 336 | if (this.isNeedRewrite) { 337 | opts.opts = opts.opts || {} 338 | Object.assign(opts.opts, { hints: false }) 339 | } 340 | $task.fetch(opts).then( 341 | (resp) => { 342 | const { statusCode: status, statusCode, headers, body } = resp 343 | callback(null, { status, statusCode, headers, body }, body) 344 | }, 345 | (err) => callback(err) 346 | ) 347 | } else if (this.isNode()) { 348 | this.initGotEnv(opts) 349 | const { url, ..._opts } = opts 350 | this.got.post(url, _opts).then( 351 | (resp) => { 352 | const { statusCode: status, statusCode, headers, body } = resp 353 | callback(null, { status, statusCode, headers, body }, body) 354 | }, 355 | (err) => { 356 | const { message: error, response: resp } = err 357 | callback(error, resp, resp && resp.body) 358 | } 359 | ) 360 | } 361 | } 362 | /** 363 | * 364 | * 示例:$.time('yyyy-MM-dd qq HH:mm:ss.S') 365 | * :$.time('yyyyMMddHHmmssS') 366 | * y:年 M:月 d:日 q:季 H:时 m:分 s:秒 S:毫秒 367 | * 其中y可选0-4位占位符、S可选0-1位占位符,其余可选0-2位占位符 368 | * @param {*} fmt 格式化参数 369 | * 370 | */ 371 | time(fmt) { 372 | let o = { 373 | 'M+': new Date().getMonth() + 1, 374 | 'd+': new Date().getDate(), 375 | 'H+': new Date().getHours(), 376 | 'm+': new Date().getMinutes(), 377 | 's+': new Date().getSeconds(), 378 | 'q+': Math.floor((new Date().getMonth() + 3) / 3), 379 | 'S': new Date().getMilliseconds() 380 | } 381 | if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (new Date().getFullYear() + '').substr(4 - RegExp.$1.length)) 382 | for (let k in o) 383 | if (new RegExp('(' + k + ')').test(fmt)) 384 | fmt = fmt.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length)) 385 | return fmt 386 | } 387 | 388 | /** 389 | * 系统通知 390 | * 391 | * > 通知参数: 同时支持 QuanX 和 Loon 两种格式, EnvJs根据运行环境自动转换, Surge 环境不支持多媒体通知 392 | * 393 | * 示例: 394 | * $.msg(title, subt, desc, 'twitter://') 395 | * $.msg(title, subt, desc, { 'open-url': 'twitter://', 'media-url': 'https://github.githubassets.com/images/modules/open_graph/github-mark.png' }) 396 | * $.msg(title, subt, desc, { 'open-url': 'https://bing.com', 'media-url': 'https://github.githubassets.com/images/modules/open_graph/github-mark.png' }) 397 | * 398 | * @param {*} title 标题 399 | * @param {*} subt 副标题 400 | * @param {*} desc 通知详情 401 | * @param {*} opts 通知参数 402 | * 403 | */ 404 | msg(title = name, subt = '', desc = '', opts) { 405 | const toEnvOpts = (rawopts) => { 406 | if (!rawopts) return rawopts 407 | if (typeof rawopts === 'string') { 408 | if (this.isLoon()) return rawopts 409 | else if (this.isQuanX()) return { 'open-url': rawopts } 410 | else if (this.isSurge()) return { url: rawopts } 411 | else return undefined 412 | } else if (typeof rawopts === 'object') { 413 | if (this.isLoon()) { 414 | let openUrl = rawopts.openUrl || rawopts.url || rawopts['open-url'] 415 | let mediaUrl = rawopts.mediaUrl || rawopts['media-url'] 416 | return { openUrl, mediaUrl } 417 | } else if (this.isQuanX()) { 418 | let openUrl = rawopts['open-url'] || rawopts.url || rawopts.openUrl 419 | let mediaUrl = rawopts['media-url'] || rawopts.mediaUrl 420 | return { 'open-url': openUrl, 'media-url': mediaUrl } 421 | } else if (this.isSurge()) { 422 | let openUrl = rawopts.url || rawopts.openUrl || rawopts['open-url'] 423 | return { url: openUrl } 424 | } 425 | } else { 426 | return undefined 427 | } 428 | } 429 | if (!this.isMute) { 430 | if (this.isSurge() || this.isLoon()) { 431 | $notification.post(title, subt, desc, toEnvOpts(opts)) 432 | } else if (this.isQuanX()) { 433 | $notify(title, subt, desc, toEnvOpts(opts)) 434 | } 435 | } 436 | let logs = ['', '==============📣系统通知📣=============='] 437 | logs.push(title) 438 | subt ? logs.push(subt) : '' 439 | desc ? logs.push(desc) : '' 440 | console.log(logs.join('\n')) 441 | this.logs = this.logs.concat(logs) 442 | } 443 | 444 | log(...logs) { 445 | if (logs.length > 0) { 446 | this.logs = [...this.logs, ...logs] 447 | } 448 | console.log(logs.join(this.logSeparator)) 449 | } 450 | 451 | logErr(err, msg) { 452 | const isPrintSack = !this.isSurge() && !this.isQuanX() && !this.isLoon() 453 | if (!isPrintSack) { 454 | this.log('', `❗️${this.name}, 错误!`, err) 455 | } else { 456 | this.log('', `❗️${this.name}, 错误!`, err.stack) 457 | } 458 | } 459 | 460 | wait(time) { 461 | return new Promise((resolve) => setTimeout(resolve, time)) 462 | } 463 | 464 | done(val = {}) { 465 | const endTime = new Date().getTime() 466 | const costTime = (endTime - this.startTime) / 1000 467 | this.log('', `🔔${this.name}, 结束! 🕛 ${costTime} 秒`) 468 | this.log() 469 | if (this.isSurge() || this.isQuanX() || this.isLoon()) { 470 | $done(val) 471 | } 472 | } 473 | })(name, opts) 474 | } 475 | --------------------------------------------------------------------------------