QX/js/bwcj.js
2024-11-10 23:43:19 +08:00

834 lines
30 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
脚本功能获取特定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);
}