/* Check in for Surge by Neurogram - 站点签到脚本 - 流量详情显示 - 多站签到支持 - 多类站点支持 使用说明:https://www.notion.so/neurogram/Check-in-0797ec9f9f3f445aae241d7762cf9d8b 关于作者 Telegram: Neurogram GitHub: Neurogram-R ———————————————————— 【机场签到Cookie版】修改自Neurogram Modified by evilbutcher 【仓库地址】https://github.com/evilbutcher/Quantumult_X/tree/master(欢迎star🌟) 【BoxJs】https://raw.githubusercontent.com/evilbutcher/Quantumult_X/master/evilbutcher.boxjs.json 【致谢】 使用Chavy的Env.js修改了原脚本,支持Quantumult X和Loon,并支持BoxJs ⚠️【免责声明】 ------------------------------------------ 1、此脚本仅用于学习研究,不保证其合法性、准确性、有效性,请根据情况自行判断,本人对此不承担任何保证责任。 2、由于此脚本仅用于学习研究,您必须在下载后 24 小时内将所有内容从您的计算机或手机或任何存储设备中完全删除,若违反规定引起任何事件本人对此均不负责。 3、请勿将此脚本用于任何商业或非法目的,若违反规定请自行对此负责。 4、此脚本涉及应用与本人无关,本人对因此引起的任何隐私泄漏或其他后果不承担任何责任。 5、本人对任何脚本引发的问题概不负责,包括但不限于由脚本错误引起的任何损失和损害。 6、如果任何单位或个人认为此脚本可能涉嫌侵犯其权利,应及时通知并提供身份证明,所有权证明,我们将在收到认证文件确认后删除此脚本。 7、所有直接或间接使用、查看此脚本的人均应该仔细阅读此声明。本人保留随时更改或补充此声明的权利。一旦您使用或复制了此脚本,即视为您已接受此免责声明。 【此版本为尝试用Cookie签到,针对有登陆验证或跳转的机场】 ⚠️【必读】⚠️【必读】⚠️【必读】⚠️ ‼️此处说明过的内容将不再解答‼️ ①需要将你的将机场域名加入mitm,例如cccat的域名为cccat.io,则hostname = cccat.io ②添加属于你自己的配置(重写/脚本),例如cccat👇 【Quantumult X】 ---------------- [rewrite_local] https:\/\/cccat\.io url script-request-header https://raw.githubusercontent.com/evilbutcher/Quantumult_X/master/check_in/glados/checkincookie_env.js (其中https:\/\/cccat\.io需要替换为你自己的机场链接) [task_local] 5 0 * * * https://raw.githubusercontent.com/evilbutcher/Quantumult_X/master/check_in/glados/checkincookie_env.js 【Surge】 ---------------- [Script] 获取Cookie = type=http-request, pattern=https:\/\/cccat\.io, script-path=https://raw.githubusercontent.com/evilbutcher/Quantumult_X/master/check_in/glados/checkincookie_env.js (其中https:\/\/cccat\.io需要替换为你自己的机场链接) 机场签到Cookie版 = type=cron,cronexp=5 0 * * *,wake-system=1,timeout=20,script-path=https://raw.githubusercontent.com/evilbutcher/Quantumult_X/master/check_in/glados/checkincookie_env.js 【Loon】 ---------------- [Script] http-request https:\/\/cccat\.io tag=获取Cookie, script-path=https://raw.githubusercontent.com/evilbutcher/Quantumult_X/master/check_in/glados/checkincookie_env.js (其中https:\/\/cccat\.io需要替换为你自己的机场链接) cron "5 0 * * *" tag=机场签到Cookie版, script-path=https://raw.githubusercontent.com/evilbutcher/Quantumult_X/master/check_in/glados/checkincookie_env.js ③BoxJs中,填入机场登陆链接。 ④配置好后,手动签到一次,提示Cookie获取成功,如无第二个机场即可禁用Cookie获取。 ⑤此时返回BoxJs中查看,Cookie和URL都有数据,即可保存会话。如有需要再重复1-4,获取第二个机场的Cookie(记得更改url为第二个机场对应的登陆链接)。 */ const $ = new Env("机场签到Cookie版"); const signurl = "evil_checkinurl"; const signcookie = "evil_checkincookie"; var siurl = $.getdata(signurl); var sicookie = $.getdata(signcookie); !(async () => { if (typeof $request != "undefined") { getCookie(); return; } if ( siurl == undefined || siurl == "" || sicookie == undefined || sicookie == "" ) { $.msg( "机场签到Cookie版", "", "❌请在 BoxJs 检查填写是否正确或是否获取到Cookie", "http://boxjs.com" ); } var name = $.getdata("evil_checkincktitle"); if (name == undefined || name == "") { name = "机场签到Cookie版"; } checkin(siurl, sicookie, name); })() .catch((e) => { $.log("", `❌失败! 原因: ${e}!`, ""); }) .finally(() => { $.done(); }); function checkin(url, cookie, name) { let checkinPath = url.indexOf("auth/login") != -1 ? "user/checkin" : "user/_checkin.php"; var checkinurl = url.replace(/(auth|user)\/login(.php)*/g, "") + checkinPath; var checkinrequest = { url: checkinurl, headers: { Cookie: cookie }, }; console.log(checkinrequest); $.post(checkinrequest, (error, response, data) => { if (error) { console.log(error); $.msg(name, "签到失败", error); } else { if (data.match(/\"msg\"\:/)) { dataResults(url, cookie, JSON.parse(data).msg, name); console.log(JSON.parse(data).msg); } else if (data.match(/login/)) { console.log(data); $.msg(name, "", "⚠️Cookie失效啦,请重新获取Cookie"); } else { console.log(data); $.msg(name, "", "⚠️签到失败,某些地方出错啦,请查看日志"); } } }); } function dataResults(url, cookie, checkinMsg, name) { let userPath = url.indexOf("auth/login") != -1 ? "user" : "user/index.php"; var datarequest = { url: url.replace(/(auth|user)\/login(.php)*/g, "") + userPath, headers: { Cookie: cookie }, }; console.log(datarequest); $.get(datarequest, (error, response, data) => { let resultData = ""; let result = []; if (data.match(/theme\/malio/)) { let flowInfo = data.match(/trafficDountChat\s*\(([^\)]+)/); if (flowInfo) { let flowData = flowInfo[1].match(/\d[^\']+/g); let usedData = flowData[0]; let todatUsed = flowData[1]; let restData = flowData[2]; result.push(`今日:${todatUsed}\n已用:${usedData}\n剩余:${restData}`); } let userInfo = data.match(/ChatraIntegration\s*=\s*({[^}]+)/); if (userInfo) { let user_name = userInfo[1].match(/name.+'(.+)'/)[1]; let user_class = userInfo[1].match(/Class.+'(.+)'/)[1]; let class_expire = userInfo[1].match(/Class_Expire.+'(.+)'/)[1]; let money = userInfo[1].match(/Money.+'(.+)'/)[1]; result.push( `用户名:${user_name}\n用户等级:lv${user_class}\n余额:${money}\n到期时间:${class_expire}` ); } if (result.length != 0) { resultData = result.join("\n\n"); } } else { let todayUsed = data.match(/>*\s*今日(已用|使用)*[^B]+/); if (todayUsed) { todayUsed = flowFormat(todayUsed[0]); result.push(`今日:${todayUsed}`); } let usedData = data.match( /(Used Transfer|>过去已用|>已用|>总已用|\"已用)[^B]+/ ); if (usedData) { usedData = flowFormat(usedData[0]); result.push(`已用:${usedData}`); } let restData = data.match( /(Remaining Transfer|>剩余流量|>流量剩余|>可用|\"剩余)[^B]+/ ); if (restData) { restData = flowFormat(restData[0]); result.push(`剩余:${restData}`); } if (result.length != 0) { resultData = result.join("\n"); } } let flowMsg = resultData == "" ? "流量信息获取失败" : resultData; $.msg(name, checkinMsg, flowMsg); }); } function flowFormat(data) { data = data.replace(/\d+(\.\d+)*%/, ""); let flow = data.match(/\d+(\.\d+)*\w*/); return flow[0] + "B"; } function getCookie() { if ($request && $request.method != "OPTIONS" && $request.url.match(/check/)) { const sicookie = $request.headers["Cookie"]; console.log(sicookie); $.setdata(sicookie, signcookie); $.msg("机场签到Cookie版", "", "获取Cookie成功🎉"); } } //From chavyleung's Env.js function Env(name, opts) { class Http { constructor(env) { this.env = env; } send(opts, method = "GET") { opts = typeof opts === "string" ? { url: opts } : opts; let sender = this.get; if (method === "POST") { sender = this.post; } return new Promise((resolve, reject) => { sender.call(this, opts, (err, resp, body) => { if (err) reject(err); else resolve(resp); }); }); } get(opts) { return this.send.call(this.env, opts); } post(opts) { return this.send.call(this.env, opts, "POST"); } } return new (class { constructor(name, opts) { this.name = name; this.http = new Http(this); this.data = null; this.dataFile = "box.dat"; this.logs = []; this.isMute = false; this.isNeedRewrite = false; this.logSeparator = "\n"; this.startTime = new Date().getTime(); Object.assign(this, opts); this.log("", `🔔${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; } toObj(str, defaultValue = null) { try { return JSON.parse(str); } catch { return defaultValue; } } toStr(obj, defaultValue = null) { try { return JSON.stringify(obj); } catch { return defaultValue; } } getjson(key, defaultValue) { let json = defaultValue; const val = this.getdata(key); if (val) { try { json = JSON.parse(this.getdata(key)); } catch {} } return json; } setjson(val, key) { try { return this.setdata(JSON.stringify(val), key); } catch { return false; } } getScript(url) { return new Promise((resolve) => { this.get({ url }, (err, resp, body) => resolve(body)); }); } runScript(script, runOpts) { return new Promise((resolve) => { let httpapi = this.getdata("@chavy_boxjs_userCfgs.httpapi"); httpapi = httpapi ? httpapi.replace(/\n/g, "").trim() : httpapi; let httpapi_timeout = this.getdata( "@chavy_boxjs_userCfgs.httpapi_timeout" ); httpapi_timeout = httpapi_timeout ? httpapi_timeout * 1 : 20; httpapi_timeout = runOpts && runOpts.timeout ? runOpts.timeout : httpapi_timeout; const [key, addr] = httpapi.split("@"); const opts = { url: `http://${addr}/v1/scripting/evaluate`, body: { script_text: script, mock_type: "cron", timeout: httpapi_timeout, }, headers: { "X-Key": key, Accept: "*/*" }, }; this.post(opts, (err, resp, body) => resolve(body)); }).catch((e) => this.logErr(e)); } loaddata() { 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); const rootDirDataFilePath = this.path.resolve( process.cwd(), this.dataFile ); const isCurDirDataFile = this.fs.existsSync(curDirDataFilePath); const isRootDirDataFile = !isCurDirDataFile && this.fs.existsSync(rootDirDataFilePath); if (isCurDirDataFile || isRootDirDataFile) { const datPath = isCurDirDataFile ? curDirDataFilePath : rootDirDataFilePath; try { return JSON.parse(this.fs.readFileSync(datPath)); } catch (e) { return {}; } } else return {}; } else 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); const rootDirDataFilePath = this.path.resolve( process.cwd(), this.dataFile ); const isCurDirDataFile = this.fs.existsSync(curDirDataFilePath); const isRootDirDataFile = !isCurDirDataFile && this.fs.existsSync(rootDirDataFilePath); const jsondata = JSON.stringify(this.data); if (isCurDirDataFile) { this.fs.writeFileSync(curDirDataFilePath, jsondata); } else if (isRootDirDataFile) { this.fs.writeFileSync(rootDirDataFilePath, jsondata); } else { this.fs.writeFileSync(curDirDataFilePath, jsondata); } } } lodash_get(source, path, defaultValue = undefined) { const paths = path.replace(/\[(\d+)\]/g, ".$1").split("."); let result = source; for (const p of paths) { result = Object(result)[p]; if (result === undefined) { return defaultValue; } } return result; } lodash_set(obj, path, value) { if (Object(obj) !== obj) return obj; if (!Array.isArray(path)) path = path.toString().match(/[^.[\]]+/g) || []; path .slice(0, -1) .reduce( (a, c, i) => Object(a[c]) === a[c] ? a[c] : (a[c] = Math.abs(path[i + 1]) >> 0 === +path[i + 1] ? [] : {}), obj )[path[path.length - 1]] = value; return obj; } getdata(key) { let val = this.getval(key); // 如果以 @ if (/^@/.test(key)) { const [, objkey, paths] = /^@(.*?)\.(.*?)$/.exec(key); const objval = objkey ? this.getval(objkey) : ""; if (objval) { try { const objedval = JSON.parse(objval); val = objedval ? this.lodash_get(objedval, paths, "") : val; } catch (e) { val = ""; } } } return val; } setdata(val, key) { let issuc = false; if (/^@/.test(key)) { const [, objkey, paths] = /^@(.*?)\.(.*?)$/.exec(key); const objdat = this.getval(objkey); const objval = objkey ? objdat === "null" ? null : objdat || "{}" : "{}"; try { const objedval = JSON.parse(objval); this.lodash_set(objedval, paths, val); issuc = this.setval(JSON.stringify(objedval), objkey); } catch (e) { const objedval = {}; this.lodash_set(objedval, paths, val); issuc = this.setval(JSON.stringify(objedval), objkey); } } else { issuc = this.setval(val, key); } return issuc; } getval(key) { if (this.isSurge() || this.isLoon()) { return $persistentStore.read(key); } else if (this.isQuanX()) { return $prefs.valueForKey(key); } else if (this.isNode()) { this.data = this.loaddata(); return this.data[key]; } else { return (this.data && this.data[key]) || null; } } setval(val, key) { if (this.isSurge() || this.isLoon()) { return $persistentStore.write(val, key); } else if (this.isQuanX()) { return $prefs.setValueForKey(val, key); } else if (this.isNode()) { this.data = this.loaddata(); this.data[key] = val; this.writedata(); return true; } else { return (this.data && this.data[key]) || null; } } initGotEnv(opts) { 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(); if (opts) { opts.headers = opts.headers ? opts.headers : {}; if (undefined === opts.headers.Cookie && undefined === opts.cookieJar) { opts.cookieJar = this.ckjar; } } } get(opts, callback = () => {}) { if (opts.headers) { delete opts.headers["Content-Type"]; delete opts.headers["Content-Length"]; } if (this.isSurge() || this.isLoon()) { if (this.isSurge() && this.isNeedRewrite) { opts.headers = opts.headers || {}; Object.assign(opts.headers, { "X-Surge-Skip-Scripting": false }); } $httpClient.get(opts, (err, resp, body) => { if (!err && resp) { resp.body = body; resp.statusCode = resp.status; } callback(err, resp, body); }); } else if (this.isQuanX()) { if (this.isNeedRewrite) { opts.opts = opts.opts || {}; Object.assign(opts.opts, { hints: false }); } $task.fetch(opts).then( (resp) => { const { statusCode: status, statusCode, headers, body } = resp; callback(null, { status, statusCode, headers, body }, body); }, (err) => callback(err) ); } else if (this.isNode()) { this.initGotEnv(opts); this.got(opts) .on("redirect", (resp, nextOpts) => { try { if (resp.headers["set-cookie"]) { const ck = resp.headers["set-cookie"] .map(this.cktough.Cookie.parse) .toString(); if (ck) { this.ckjar.setCookieSync(ck, null); } nextOpts.cookieJar = this.ckjar; } } catch (e) { this.logErr(e); } // this.ckjar.setCookieSync(resp.headers['set-cookie'].map(Cookie.parse).toString()) }) .then( (resp) => { const { statusCode: status, statusCode, headers, body } = resp; callback(null, { status, statusCode, headers, body }, body); }, (err) => { const { message: error, response: resp } = err; callback(error, resp, resp && resp.body); } ); } } post(opts, callback = () => {}) { // 如果指定了请求体, 但没指定`Content-Type`, 则自动生成 if (opts.body && opts.headers && !opts.headers["Content-Type"]) { opts.headers["Content-Type"] = "application/x-www-form-urlencoded"; } if (opts.headers) delete opts.headers["Content-Length"]; if (this.isSurge() || this.isLoon()) { if (this.isSurge() && this.isNeedRewrite) { opts.headers = opts.headers || {}; Object.assign(opts.headers, { "X-Surge-Skip-Scripting": false }); } $httpClient.post(opts, (err, resp, body) => { if (!err && resp) { resp.body = body; resp.statusCode = resp.status; } callback(err, resp, body); }); } else if (this.isQuanX()) { opts.method = "POST"; if (this.isNeedRewrite) { opts.opts = opts.opts || {}; Object.assign(opts.opts, { hints: false }); } $task.fetch(opts).then( (resp) => { const { statusCode: status, statusCode, headers, body } = resp; callback(null, { status, statusCode, headers, body }, body); }, (err) => callback(err) ); } else if (this.isNode()) { this.initGotEnv(opts); const { url, ..._opts } = opts; this.got.post(url, _opts).then( (resp) => { const { statusCode: status, statusCode, headers, body } = resp; callback(null, { status, statusCode, headers, body }, body); }, (err) => { const { message: error, response: resp } = err; callback(error, resp, resp && resp.body); } ); } } /** * * 示例:$.time('yyyy-MM-dd qq HH:mm:ss.S') * :$.time('yyyyMMddHHmmssS') * y:年 M:月 d:日 q:季 H:时 m:分 s:秒 S:毫秒 * 其中y可选0-4位占位符、S可选0-1位占位符,其余可选0-2位占位符 * @param {string} fmt 格式化参数 * @param {number} 可选: 根据指定时间戳返回格式化日期 * */ time(fmt, ts = null) { const date = ts ? new Date(ts) : new Date(); 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(), }; if (/(y+)/.test(fmt)) fmt = fmt.replace( RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length) ); for (let k in o) if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace( RegExp.$1, RegExp.$1.length == 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length) ); return fmt; } /** * 系统通知 * * > 通知参数: 同时支持 QuanX 和 Loon 两种格式, EnvJs根据运行环境自动转换, Surge 环境不支持多媒体通知 * * 示例: * $.msg(title, subt, desc, 'twitter://') * $.msg(title, subt, desc, { 'open-url': 'twitter://', 'media-url': 'https://github.githubassets.com/images/modules/open_graph/github-mark.png' }) * $.msg(title, subt, desc, { 'open-url': 'https://bing.com', 'media-url': 'https://github.githubassets.com/images/modules/open_graph/github-mark.png' }) * * @param {*} title 标题 * @param {*} subt 副标题 * @param {*} desc 通知详情 * @param {*} opts 通知参数 * */ msg(title = name, subt = "", desc = "", opts) { const toEnvOpts = (rawopts) => { if (!rawopts) return rawopts; if (typeof rawopts === "string") { if (this.isLoon()) return rawopts; else if (this.isQuanX()) return { "open-url": rawopts }; else if (this.isSurge()) return { url: rawopts }; else return undefined; } else if (typeof rawopts === "object") { if (this.isLoon()) { let openUrl = rawopts.openUrl || rawopts.url || rawopts["open-url"]; let mediaUrl = rawopts.mediaUrl || rawopts["media-url"]; return { openUrl, mediaUrl }; } else if (this.isQuanX()) { let openUrl = rawopts["open-url"] || rawopts.url || rawopts.openUrl; let mediaUrl = rawopts["media-url"] || rawopts.mediaUrl; return { "open-url": openUrl, "media-url": mediaUrl }; } else if (this.isSurge()) { let openUrl = rawopts.url || rawopts.openUrl || rawopts["open-url"]; return { url: openUrl }; } } else { return undefined; } }; if (!this.isMute) { if (this.isSurge() || this.isLoon()) { $notification.post(title, subt, desc, toEnvOpts(opts)); } else if (this.isQuanX()) { $notify(title, subt, desc, toEnvOpts(opts)); } } if (!this.isMuteLog) { let logs = ["", "==============📣系统通知📣=============="]; logs.push(title); subt ? logs.push(subt) : ""; desc ? logs.push(desc) : ""; console.log(logs.join("\n")); this.logs = this.logs.concat(logs); } } log(...logs) { if (logs.length > 0) { this.logs = [...this.logs, ...logs]; } console.log(logs.join(this.logSeparator)); } logErr(err, msg) { const isPrintSack = !this.isSurge() && !this.isQuanX() && !this.isLoon(); if (!isPrintSack) { this.log("", `❗️${this.name}, 错误!`, err); } else { this.log("", `❗️${this.name}, 错误!`, err.stack); } } wait(time) { return new Promise((resolve) => setTimeout(resolve, time)); } done(val = {}) { const endTime = new Date().getTime(); const costTime = (endTime - this.startTime) / 1000; this.log("", `🔔${this.name}, 结束! 🕛 ${costTime} 秒`); this.log(); if (this.isSurge() || this.isQuanX() || this.isLoon()) { $done(val); } } })(name, opts); }