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

828 lines
30 KiB
JavaScript
Raw Permalink 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.

/*
脚本功能获取搜搜小奖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);
}