commit 747176c1cd5675dcd6378f56a867616270dc12e4 Author: chickliu Date: Sun Nov 10 23:43:19 2024 +0800 上传文件至 js diff --git a/js/bwcj.js b/js/bwcj.js new file mode 100644 index 0000000..8fd84ef --- /dev/null +++ b/js/bwcj.js @@ -0,0 +1,834 @@ +/* +脚本功能:获取特定URL返回体中的token和id,并自动同步至青龙面板 +[rewrite_local] +^https://webapi2\.qmai\.cn/web/seller/oauth/flash-sale-login url script-response-body bwcj.js +[mitm] +hostname = webapi2.qmai.cn +*/ + +const $ = new Env("霸王茶姬"); +//===== 修改下面你的青龙容器========== +const ql_host = "http://127.0.0.1:5700"; +const ql_client_id = "12345"; +const ql_client_secret = "54321"; +//================================ +let ql_envs = []; // 用于存储从青龙获取的所有环境变量 +let ql_token = null; // 用于认证青龙API的token + +!(async () => { + // 获取响应体 + const response = JSON.parse($response.body); + if (response.status === true && response.data) { + // 提取token和id + const token = response.data.token; + const id = response.data.user.id; + + // 格式化为token#id + const formattedTokenId = `${token}#${id}`; + + // 检查是否提取到数据 + if (formattedTokenId) { + // 发送通知,包含提取到的信息 + $.msg("通知", "数据提取成功", formattedTokenId); + + // 获取青龙的认证token + await get_ql_token(); + + // 如果成功获取到青龙token + if (ql_token) { + // 获取与openId相关的环境变量 + await get_ql_envs(); + + // 如果从青龙获取到了环境变量 + if (ql_envs.length > 0) { + // 找出所有的'bwcj_ck'环境变量,并合并它们的数据 + let allBwcjCkEnvs = ql_envs.filter(envInfo => envInfo.name === 'bwcj_ck'); + let mergedData = allBwcjCkEnvs.reduce((acc, curr) => { + let parsedData = curr.value.split('\n').filter(line => line.trim() !== ''); + return [...acc, ...parsedData]; + }, []); + + // 更新数据 + const newData = updateData(formattedTokenId, mergedData); + + // 更新环境变量 + let updateEnv = { + id: allBwcjCkEnvs[0].id, // 使用第一个'bwcj_ck'环境变量的ID + name: "bwcj_ck", + value: newData.join('\n'), + remarks: `Updated at ${formatCST(new Date())}` + }; + + // 使用PUT方法更新现有的环境变量 + await set_ql_envs(updateEnv, "PUT"); + + // 发送通知 + $.msg("通知", "青龙变量更新", `更新成功! +tokens: ${formattedTokenId}`); + } else { + console.log("No bwcj_ck environment variable found, creating a new one."); + // 如果不存在'bwcj_ck'环境变量,则创建一个新的环境变量 + let createEnv = { + name: "bwcj_ck", + value: formattedTokenId, + remarks: `Created at ${formatCST(new Date())}` + }; + + // 使用POST方法创建新的环境变量 + await set_ql_envs(createEnv, "POST"); + + // 发送通知 + $.msg("通知", "青龙变量创建", `创建成功! +tokens: ${formattedTokenId}`); + } + } else { + console.log("Failed to get ql token."); + } + } else { + console.log("No valid tokens extracted from the response."); + } + } else { + console.log("Failed to fetch data from the specified URL."); + } +})() +.catch(e => { + $.log("", `❌失败! 原因: ${e}!`, ""); +}) +.finally(() => { + $.done(); +}); + +// 青龙 API 相关函数 +async function get_ql_token() { + return new Promise((resolve) => { + let options = { + url: `${ql_host}/open/auth/token?client_id=${ql_client_id}&client_secret=${ql_client_secret}`, + method: "GET", + headers: { + "Content-Type": "application/json;charset=UTF-8" + } + }; + $.get(options, (err, resp, data) => { + try { + if (err) { + console.log("⛔️API查询请求失败,请检查自身设备网络情况"); + console.log(JSON.stringify(err)); + $.logErr(err); + } else { + let res = JSON.parse(data); + if (res.code === 200) { + ql_token = res.data.token; + } + } + } catch (e) { + $.logErr(e, resp); + } finally { + resolve(); + } + }); + }); +} + +async function get_ql_envs() { + return new Promise((resolve) => { + let options = { + url: `${ql_host}/open/envs?t=${Date.now()}`, + method: "GET", + headers: { + "Content-Type": "application/json;charset=UTF-8", + Authorization: `Bearer ${ql_token}` + } + }; + $.get(options, (err, resp, data) => { + try { + if (err) { + console.log("⛔️API查询请求失败,请检查自身设备网络情况"); + console.log(JSON.stringify(err)); + $.logErr(err); + } else { + let res = JSON.parse(data); + if (res.code === 200) { + ql_envs = res.data; + } + } + } catch (e) { + $.logErr(e, resp); + } finally { + resolve(); + } + }); + }); +} + +async function set_ql_envs(data, method) { + return new Promise((resolve) => { + let options = { + url: `${ql_host}/open/envs?t=${Date.now()}`, + method: method, + headers: { + "Content-Type": "application/json;charset=UTF-8", + Authorization: `Bearer ${ql_token}` + }, + body: JSON.stringify(data) + }; + $.post(options, (err, resp, data) => { + try { + if (err) { + console.log("⛔️API查询请求失败,请检查自身设备网络情况"); + console.log(JSON.stringify(err)); + $.logErr(err); + } else { + let res = JSON.parse(data); + if (res.code === 200) { + if (method === "PUT") { + console.log("更新青龙变量成功!"); + } else { + console.log("新增青龙变量成功!"); + } + } + } + } catch (e) { + $.logErr(e, resp); + } finally { + resolve(); + } + }); + }); +} + +// 更新数据的函数 +function updateData(tokens, currentValue) { + const [newToken, newId] = tokens.split('#'); + + // 创建一个对象数组,方便操作 + const existingTokens = currentValue.map(item => item.split('#')); + + // 查找是否存在相同的id + const existingEntry = existingTokens.find(entry => entry[1] === newId); + + if (existingEntry) { + // 如果存在,则更新token + const index = existingTokens.indexOf(existingEntry); + existingTokens[index][0] = newToken; + } else { + // 如果不存在,则添加新的token和id + existingTokens.push([newToken, newId]); + } + + // 将对象数组转换回字符串数组 + const updatedValue = existingTokens.map(entry => entry.join('#')); + + return updatedValue; +} + +// 格式化时间为北京时间 +function formatCST(date) { + const cstOffset = 8 * 60 * 60 * 1000; // Beijing is UTC+8 + const cstDate = new Date(date.getTime() - cstOffset + cstOffset); // 调整偏移量 + const year = cstDate.getFullYear(); + const month = String(cstDate.getMonth() + 1).padStart(2, '0'); + const day = String(cstDate.getDate()).padStart(2, '0'); + const hours = String(cstDate.getHours()).padStart(2, '0'); + const minutes = String(cstDate.getMinutes()).padStart(2, '0'); + const seconds = String(cstDate.getSeconds()).padStart(2, '0'); + + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; +} + + + + +//++++++++++++++++++++++++++++++++++++++++ + +//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()) { + 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); +} \ No newline at end of file diff --git a/js/ssxj.js b/js/ssxj.js new file mode 100644 index 0000000..c6a3081 --- /dev/null +++ b/js/ssxj.js @@ -0,0 +1,828 @@ +/* +脚本功能:获取搜搜小奖cookie 自动同步青龙 +[rewrite_local] +^https://search-api\.lingxiaojiang\.com url script-request-body ssxj.js +[mitm] +hostname = search-api.lingxiaojiang.com +*/ + +const $ = new Env("搜搜小奖"); +//===== 修改下面你的青龙容器========== +const ql_host = "http://127.0.0.1:5700"; +const ql_client_id = "12345"; +const ql_client_secret = "54321"; +//================================ +let ql_envs = []; // 用于存储从青龙获取的所有环境变量 +let ql_token = null; // 用于认证青龙API的token +!(async () => { + // 获取请求URL + const url = $request.url; + console.log('请求地址:', url); + + // 直接从请求头中提取ticket和token + const ticket = $request.headers.ticket; + const token = $request.headers.token; + + // 检查ticket和token是否非空 + if (!ticket || !token) { + console.log("Ticket or Token is empty, skipping upload."); + return; + } + + // 发送通知,包含提取到的信息 + $.msg("通知", "搜搜小奖数据", `ticket: ${ticket}\ntoken: ${token}`); + + // 获取青龙的认证token + await get_ql_token(); + + // 如果成功获取到青龙token + if (ql_token) { + // 获取与openId相关的环境变量 + await get_ql_envs(); + + // 如果从青龙获取到了环境变量 + if (ql_envs.length > 0) { + // 找出所有的'SSXJAPP'环境变量,并合并它们的数据 + let allSsxjEnvs = ql_envs.filter(envInfo => envInfo.name === 'SSXJAPP'); + let mergedData = allSsxjEnvs.reduce((acc, curr) => { + let parsedData = JSON.parse(curr.value); + return [...acc, ...parsedData]; + }, []); + + // 更新数据 + const newData = updateData(ticket, token, mergedData); + + // 更新环境变量 + let updateEnv = { + id: allSsxjEnvs[0].id, // 使用第一个'SSXJAPP'环境变量的ID + name: "SSXJAPP", + value: JSON.stringify(newData), + remarks: `Updated at ${new Date().toISOString()}` + }; + + // 使用PUT方法更新现有的环境变量 + await set_ql_envs(updateEnv, "PUT"); + // 发送通知 + $.msg("通知", "青龙变量更新", `更新成功!\nticket: ${ticket}\ntoken: ${token}`); + return; + } else { + console.log("No SSXJAPP environment variable found, creating a new one."); + // 如果不存在'SSXJAPP'环境变量,则创建一个新的环境变量 + const newData = [ + { + nickname: 1, + ticket: ticket, + token: token + } + ]; + + let createEnv = { + name: "SSXJAPP", + value: JSON.stringify(newData), + remarks: `Created at ${new Date().toISOString()}` + }; + + // 使用POST方法创建新的环境变量 + await set_ql_envs(createEnv, "POST"); + // 发送通知 + $.msg("通知", "青龙变量创建", `创建成功!\nticket: ${ticket}\ntoken: ${token}`); + } + } +})() +.catch((e) => { + $.log("", `❌失败! 原因: ${e}!`, ""); +}) +.finally(() => { + $.done(); +}); + +// 青龙 API 相关函数 +async function get_ql_token() { + return new Promise((resolve) => { + let options = { + url: `${ql_host}/open/auth/token?client_id=${ql_client_id}&client_secret=${ql_client_secret}`, + method: "GET", + headers: { + "Content-Type": "application/json;charset=UTF-8" + } + }; + $.get(options, (err, resp, data) => { + try { + if (err) { + console.log("⛔️API查询请求失败,请检查自身设备网络情况"); + console.log(JSON.stringify(err)); + $.logErr(err); + } else { + let res = JSON.parse(data); + if (res.code === 200) { + ql_token = res.data.token; + } + } + } catch (e) { + $.logErr(e, resp); + } finally { + resolve(); + } + }); + }); +} + +async function get_ql_envs() { + return new Promise((resolve) => { + let options = { + url: `${ql_host}/open/envs?t=${Date.now()}`, + method: "GET", + headers: { + "Content-Type": "application/json;charset=UTF-8", + Authorization: `Bearer ${ql_token}` + } + }; + $.get(options, (err, resp, data) => { + try { + if (err) { + console.log("⛔️API查询请求失败,请检查自身设备网络情况"); + console.log(JSON.stringify(err)); + $.logErr(err); + } else { + let res = JSON.parse(data); + if (res.code === 200) { + ql_envs = res.data; + } + } + } catch (e) { + $.logErr(e, resp); + } finally { + resolve(); + } + }); + }); +} + +async function set_ql_envs(data, method) { + return new Promise((resolve) => { + let options = { + url: `${ql_host}/open/envs?t=${Date.now()}`, + method: method, + headers: { + "Content-Type": "application/json;charset=UTF-8", + Authorization: `Bearer ${ql_token}` + }, + body: JSON.stringify(data) + }; + $.post(options, (err, resp, data) => { + try { + if (err) { + console.log("⛔️API查询请求失败,请检查自身设备网络情况"); + console.log(JSON.stringify(err)); + $.logErr(err); + } else { + let res = JSON.parse(data); + if (res.code === 200) { + if (method === "PUT") { + console.log("更新青龙变量成功!"); + } else { + console.log("新增青龙变量成功!"); + } + } + } + } catch (e) { + $.logErr(e, resp); + } finally { + resolve(); + } + }); + }); +} + +// 更新数据的函数 +function updateData(ticket, token, currentValue) { + try { + const newData = currentValue.map(item => { + if (item.ticket === ticket) { + // 如果找到匹配的ticket,更新token + item.token = token; + return item; + } + return item; + }); + + // 添加新数据项(如果ticket不存在) + const existingTickets = newData.map(item => item.ticket); + if (!existingTickets.includes(ticket)) { + newData.push({ + nickname: newData.length + 1, // 生成新的昵称 + ticket: ticket, + token: token + }); + } + + return newData; + } catch (error) { + console.error("无法解析现有环境变量的值,将使用新数据替换"); + return [ + { + nickname: 1, // 生成新的昵称 + ticket: ticket, + token: token + } + ]; + } +} + + + + +//++++++++++++++++++++++++++++++++++++++++ + +//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()) { + 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); +} \ No newline at end of file