Files
2026-01-13 15:10:03 +08:00

401 lines
13 KiB
JavaScript

/*
* @name 青龙 XYYYAPP 变量自动上传
* @version v1.0.0
* @description 自动提取 3rdSession 并上传到青龙面板 XYYYAPP 变量
* @author Antigravity
*
* [rewrite_local]
* ^https:\/\/gzpengru\.weimbo\.com\/api\/index\.php url script-request-header xyyx_qx.js
*
* [mitm]
* hostname = gzpengru.weimbo.com
*
* ⚠️ 使用说明:
* 1. 请并在下方配置青龙面板信息 QL_URL, QL_CLIENT_ID, QL_CLIENT_SECRET
* 2. 将脚本添加到 Rewrite
* 3. 触发请求即可自动上传
*/
const $ = new Env("XYYYAPP自动上传");
// ---------------------- 用户配置区域 ----------------------
// 请在下方填写您的青龙面板信息
const QL_URL = "http://chickliu.store:5732"; // 例如 http://192.168.1.1:5700
const QL_CLIENT_ID = "M2HkEY-8RRWq"; // 应用ID
const QL_CLIENT_SECRET = "-LHulEYdGa2TR7_th6oX4Zxb"; // 应用密钥
// ---------------------------------------------------------
const TARGET_URL_KEY = "gzpengru.weimbo.com/api/index.php";
(async () => {
try {
if (typeof $request === "undefined") {
$.msg($.name, "脚本运行环境错误", "请在 Quantumult X Rewrite 中使用 script-request-header");
return;
}
if ($request.url.indexOf(TARGET_URL_KEY) === -1) {
return;
}
$.log(`[${$.name}] 开始捕获...`);
// 1. 提取信息
const headers = $request.headers;
// 查找 3rdSession,处理大小写可能不一致的情况
let session = null;
for (const key in headers) {
if (key.toLowerCase() === '3rdsession') {
session = headers[key];
break;
}
}
if (!session) {
$.log(`[${$.name}] 未找到 3rdSession 头`);
return;
}
$.log(`获取到 3rdSession: ${session}`);
// 2. 登录青龙
const token = await qlLogin();
if (!token) return;
// 3. 获取现有变量
const envs = await qlGetEnvs(token);
let targetEnvId = null;
let targetEnvValue = "[]";
const existingEnv = envs.find(e => e.name === "XYYYAPP");
if (existingEnv) {
targetEnvId = existingEnv.id;
targetEnvValue = existingEnv.value || "[]";
}
// 4. 处理数据逻辑
let dataList = [];
try {
dataList = JSON.parse(targetEnvValue);
if (!Array.isArray(dataList)) dataList = [];
} catch (e) {
$.log("原有变量格式不是JSON,重置为空数组");
dataList = [];
}
// 检查是否存在
const existingIndex = dataList.findIndex(item => item.cookie === session);
let successMsg = "";
let needUpdate = false;
if (existingIndex !== -1) {
$.log(`检测到 Session 已存在: ${session}`);
$.msg($.name, "无需更新", "Session 已存在且一致");
return;
} else {
// 新增
$.log(`检测到新 Session,添加数据`);
dataList.push({ "cookie": session });
successMsg = `Session 添加成功`;
needUpdate = true;
}
if (!needUpdate) return;
const newEnvValue = JSON.stringify(dataList);
// 5. 上传/更新变量
if (targetEnvId) {
await qlUpdateEnv(token, targetEnvId, "XYYYAPP", newEnvValue, successMsg);
} else {
await qlAddEnv(token, [{ name: "XYYYAPP", value: newEnvValue }], successMsg);
}
} catch (e) {
$.logErr(e);
$.msg($.name, "运行时错误", e.message);
} finally {
$.done();
}
})();
// ---------------------- 辅助函数 ----------------------
// 青龙 API: 登录
async function qlLogin() {
const options = {
url: `${QL_URL}/open/auth/token?client_id=${QL_CLIENT_ID}&client_secret=${QL_CLIENT_SECRET}`,
timeout: 10000
};
return new Promise((resolve) => {
$.get(options, (err, resp, data) => {
try {
if (err) throw new Error(JSON.stringify(err));
const body = JSON.parse(data);
if (body.code === 200) {
resolve(body.data.token);
} else {
$.msg($.name, "青龙登录失败", body.message);
resolve(null);
}
} catch (e) {
$.msg($.name, "青龙登录异常", "请检查 URL 和 Client ID/Secret 设置");
$.logErr(e);
resolve(null);
}
});
});
}
// 青龙 API: 获取变量
async function qlGetEnvs(token) {
const options = {
url: `${QL_URL}/open/envs?searchValue=XYYYAPP`,
headers: {
"Authorization": `Bearer ${token}`
},
timeout: 10000
};
return new Promise((resolve) => {
$.get(options, (err, resp, data) => {
try {
if (err) throw new Error(JSON.stringify(err));
const body = JSON.parse(data);
if (body.code === 200) {
resolve(body.data);
} else {
resolve([]);
}
} catch (e) {
$.logErr(e);
resolve([]);
}
});
});
}
// 青龙 API: 更新变量
async function qlUpdateEnv(token, id, name, value, msg) {
const options = {
url: `${QL_URL}/open/envs`,
method: "PUT",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
id: id,
name: name,
value: value
}),
timeout: 10000
};
return new Promise((resolve) => {
$.put(options, (err, resp, data) => {
try {
if (err) throw new Error(JSON.stringify(err));
const body = JSON.parse(data);
if (body.code === 200) {
$.msg($.name, "操作成功", msg || "变量已更新");
} else {
$.msg($.name, "更新失败", body.message);
}
} catch (e) {
$.logErr(e);
$.msg($.name, "更新异常", e.message);
}
resolve();
});
});
}
// 青龙 API: 新增变量
async function qlAddEnv(token, envsArray, msg) {
const options = {
url: `${QL_URL}/open/envs`,
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json"
},
body: JSON.stringify(envsArray),
timeout: 10000
};
return new Promise((resolve) => {
$.post(options, (err, resp, data) => {
try {
if (err) throw new Error(JSON.stringify(err));
const body = JSON.parse(data);
if (body.code === 200) {
$.msg($.name, "操作成功", msg || "XYYYAPP 变量已创建");
} else {
$.msg($.name, "上传失败", body.message);
}
} catch (e) {
$.logErr(e);
$.msg($.name, "上传异常", e.message);
}
resolve();
});
});
}
// ---------------------- Env Class ----------------------
function Env(name, env) {
"undefined" != typeof process && JSON.stringify(process.env).indexOf("GITHUB") > -1 && process.exit(0);
return new class {
constructor(name, env) {
this.name = name;
this.notifyStr = '';
this.startTime = (new Date).getTime();
Object.assign(this, env);
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
}
get(options, callback) {
if (this.isQuanX()) {
if (typeof options == "string") options = { url: options }
options["method"] = "GET"
$task.fetch(options).then(response => {
callback(null, response, response.body)
}, reason => callback(reason.error, null, null))
}
if (this.isSurge() || this.isLoon()) {
$httpClient.get(options, (error, response, body) => {
callback(error, response, body)
})
}
if (this.isNode()) {
this.got = this.got ? this.got : require("got");
const { url, ...otherOptions } = options;
this.got.get(url, otherOptions).then(response => {
const { statusCode, headers, body } = response;
callback(null, { status: statusCode, statusCode, headers, body }, body)
}, error => {
const { message, response } = error;
callback(message, response, response && response.body)
})
}
}
post(options, callback) {
if (this.isQuanX()) {
if (typeof options == "string") options = { url: options }
options["method"] = "POST"
$task.fetch(options).then(response => {
callback(null, response, response.body)
}, reason => callback(reason.error, null, null))
}
if (this.isSurge() || this.isLoon()) {
$httpClient.post(options, (error, response, body) => {
callback(error, response, body)
})
}
if (this.isNode()) {
this.got = this.got ? this.got : require("got");
const { url, ...otherOptions } = options;
this.got.post(url, otherOptions).then(response => {
const { statusCode, headers, body } = response;
callback(null, { status: statusCode, statusCode, headers, body }, body)
}, error => {
const { message, response } = error;
callback(message, response, response && response.body)
})
}
}
put(options, callback) {
if (this.isQuanX()) {
if (typeof options == "string") options = { url: options }
options["method"] = "PUT"
$task.fetch(options).then(response => {
callback(null, response, response.body)
}, reason => callback(reason.error, null, null))
}
if (this.isSurge() || this.isLoon()) {
options.method = 'PUT';
$httpClient.put(options, (error, response, body) => {
callback(error, response, body)
})
}
if (this.isNode()) {
this.got = this.got ? this.got : require("got");
const { url, ...otherOptions } = options;
this.got.put(url, otherOptions).then(response => {
const { statusCode, headers, body } = response;
callback(null, { status: statusCode, statusCode, headers, body }, body)
}, error => {
const { message, response } = error;
callback(message, response, response && response.body)
})
}
}
msg(title = name, subtitle = '', body = '') {
const opts = (val) => {
if (this.isLoon()) return {
openUrl: val
}
if (this.isQuanX()) return {
'open-url': val
}
if (this.isSurge()) return {
url: val
}
return {}
}
if (this.isSurge() || this.isLoon()) {
$notification.post(title, subtitle, body)
}
if (this.isQuanX()) {
$notify(title, subtitle, body)
}
this.log('==============📣系统通知📣==============')
this.log(title)
if (subtitle) this.log(subtitle)
if (body) this.log(body)
}
log(...logs) {
if (logs.length > 0) {
this.logs = [...(this.logs || []), ...logs]
}
console.log(logs.join(this.logSeparator || "\n"))
}
logErr(err, msg) {
const isSurge = this.isSurge()
const isQuanX = this.isQuanX()
const isLoon = this.isLoon()
if (!isSurge && !isQuanX && !isLoon) {
this.log('', `❗️${this.name}, 错误!`, err.stack)
} else {
this.log('', `❗️${this.name}, 错误!`, err)
}
}
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, env)
}