Update NewAddToCart_Web.js

This commit is contained in:
XXhaos
2026-04-03 10:13:41 +08:00
committed by GitHub
parent fb4a8ce105
commit 3ea76b3a3c

View File

@@ -2,9 +2,11 @@
* Xbox Cart Web Runner * Xbox Cart Web Runner
* 远程路径: https://raw.githubusercontent.com/dragonisheep/Surge/refs/heads/master/Scripts/NewAddToCart_Web.js * 远程路径: https://raw.githubusercontent.com/dragonisheep/Surge/refs/heads/master/Scripts/NewAddToCart_Web.js
* *
* 优先级 * 流程
* 1. 远程待同步 Productfetch_and_clear单次只取一组 * 1. GET 读取远程当前组(服务端加锁
* 2. 本地 XboxProductList兜底 * 2. 执行加购
* 3. 加购完成后 GET clear服务端释放锁 + 弹出该组)
* 4. 若远程无数据,回退到本地 XboxProductList
*/ */
const MARKET = "NG"; const MARKET = "NG";
@@ -12,9 +14,9 @@ const LOCALE = "en-ng";
const FRIENDLY_NAME = `cart-${MARKET}`; const FRIENDLY_NAME = `cart-${MARKET}`;
const CLIENT_CONTEXT = { client: "UniversalWebStore.Cart", deviceType: "Pc" }; const CLIENT_CONTEXT = { client: "UniversalWebStore.Cart", deviceType: "Pc" };
const REMOTE_URL = 'https://cc.dragonisheep.com/surge?token=xbox123&action=fetch_and_clear'; const REMOTE_READ_URL = 'https://cc.dragonisheep.com/surge?token=xbox123';
const LOCAL_KEY = 'XboxProductList'; const REMOTE_CLEAR_URL = 'https://cc.dragonisheep.com/surge?token=xbox123&action=clear';
const LOCK_KEY = 'SyncXboxLock'; const LOCAL_KEY = 'XboxProductList';
const MUID = $persistentStore.read("cart-x-authorization-muid"); const MUID = $persistentStore.read("cart-x-authorization-muid");
const MS_CV = $persistentStore.read("cart-ms-cv"); const MS_CV = $persistentStore.read("cart-ms-cv");
@@ -24,7 +26,8 @@ const results = { success: [], failure: [] };
const successKeys = []; const successKeys = [];
let currentIndex = 0; let currentIndex = 0;
let productList = []; let productList = [];
let sourceLabel = ""; // 记录数据来源,用于最终通知 let sourceLabel = "";
let useRemote = false; // 是否使用了远程数据(决定加购后是否发 clear
function log(type, message, detail = "") { function log(type, message, detail = "") {
const icon = type === "success" ? "✅" : (type === "error" ? "❌" : ""); const icon = type === "success" ? "✅" : (type === "error" ? "❌" : "");
@@ -69,27 +72,45 @@ const HEADERS = {
function finalizeAndClean() { function finalizeAndClean() {
const successCount = results.success.length; const successCount = results.success.length;
const failureCount = results.failure.length; const failureCount = results.failure.length;
// 清理本地 XboxProductList 中已成功的 key仅本地模式
let remainingCount = 0; let remainingCount = 0;
if (!useRemote) {
try {
let storeObj; try { storeObj = JSON.parse($persistentStore.read(LOCAL_KEY) || "{}"); } catch { storeObj = {}; }
for (const k of successKeys) {
if (k && Object.prototype.hasOwnProperty.call(storeObj, k)) delete storeObj[k];
}
remainingCount = Object.keys(storeObj).filter(k => /^product\d+$/.test(k)).length;
$persistentStore.write(JSON.stringify(storeObj), LOCAL_KEY);
log("info", "本地清理完成", `剩余: ${remainingCount}`);
} catch (e) { log("error", "清理异常", String(e)); }
}
try { const doFinish = () => {
// 只清理本地 XboxProductList 中已成功的 key $notification.post(
let storeObj; try { storeObj = JSON.parse($persistentStore.read(LOCAL_KEY) || "{}"); } catch { storeObj = {}; } "🛒 Xbox 加购完成",
for (const k of successKeys) { `成功: ${successCount} / 失败: ${failureCount}`,
if (k && Object.prototype.hasOwnProperty.call(storeObj, k)) delete storeObj[k]; `来源: ${sourceLabel}${!useRemote ? ` | 剩余本地: ${remainingCount}` : ''}`
);
const html = `<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Xbox Cart</title></head><body style="font-family:sans-serif;padding:20px;"><h3>执行结果: 成功 ${successCount} / 失败 ${failureCount} | 来源: ${sourceLabel}</h3><div style="background:#f9f9f9;padding:10px;">${logBuffer.join("")}</div></body></html>`;
$done({ response: { status: 200, headers: { "Content-Type": "text/html;charset=utf-8" }, body: html } });
};
if (useRemote) {
if (failureCount === 0) {
// 全部成功:发 clear释放服务端锁并弹出该组
log("info", "加购全部成功,通知服务端 clear");
$httpClient.get(REMOTE_CLEAR_URL, () => doFinish());
} else {
// 有失败:不 clear保留服务端数据下次可重试
log("info", `${failureCount} 个失败,保留服务端数据以便重试`);
$notification.post("⚠️ 部分失败", `${failureCount} 个加购失败`, "服务端数据已保留,可重新执行");
doFinish();
} }
remainingCount = Object.keys(storeObj).filter(k => /^product\d+$/.test(k)).length; } else {
$persistentStore.write(JSON.stringify(storeObj), LOCAL_KEY); doFinish();
log("info", "清理完成", `剩余: ${remainingCount}`); }
} catch (e) { log("error", "清理异常", e); }
$notification.post(
"🛒 Xbox 加购完成",
`成功: ${successCount} / 失败: ${failureCount}`,
`来源: ${sourceLabel} | 剩余本地: ${remainingCount}`
);
const html = `<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Xbox Cart</title></head><body style="font-family:sans-serif;padding:20px;"><h3>执行结果: 成功 ${successCount} / 失败 ${failureCount} | 来源: ${sourceLabel}</h3><div style="background:#f9f9f9;padding:10px;">${logBuffer.join("")}</div></body></html>`;
$done({ response: { status: 200, headers: { "Content-Type": "text/html;charset=utf-8" }, body: html } });
} }
function startTask() { function startTask() {
@@ -102,7 +123,12 @@ function startTask() {
if (productList.length === 0) { if (productList.length === 0) {
log("info", "列表为空"); log("info", "列表为空");
$notification.post("⚠️ Xbox 脚本", "无需执行", `来源: ${sourceLabel} | 列表为空`); $notification.post("⚠️ Xbox 脚本", "无需执行", `来源: ${sourceLabel} | 列表为空`);
finalizeAndClean(); // 远程模式下列表为空也要释放锁
if (useRemote) {
$httpClient.get(REMOTE_CLEAR_URL, () => $done({}));
} else {
finalizeAndClean();
}
return; return;
} }
log("info", `开始任务 [${sourceLabel}]`, `数量: ${productList.length}`); log("info", `开始任务 [${sourceLabel}]`, `数量: ${productList.length}`);
@@ -137,38 +163,35 @@ function sendRequest() {
} }
// ========================= 主流程 ========================= // ========================= 主流程 =========================
// 防重入锁(和 SyncXboxCloud.js 共用同一个 lockKey // ★ 服务端有锁机制,客户端不再需要时间锁
const lockVal = $persistentStore.read(LOCK_KEY); // 第一步:尝试从远程读取当前组(服务端此时会加锁)
if (lockVal && Date.now() - parseInt(lockVal, 10) < 5000) { $httpClient.get(REMOTE_READ_URL, (error, response, data) => {
// 5 秒内重复触发,直接放行
$done({});
}
$persistentStore.write(String(Date.now()), LOCK_KEY);
// 第一步:尝试从远程取一组待同步 Product
$httpClient.get(REMOTE_URL, (error, response, data) => {
let remoteGroup = null; let remoteGroup = null;
let groupIndex = null;
if (!error && data) { if (!error && data) {
try { try {
const payload = JSON.parse((data || '').trim() || '{}'); const payload = JSON.parse((data || '').trim() || '{}');
if (payload.ok && payload.cleared && payload.currentGroup) { // 服务端有锁且有数据时返回 currentGroup
if (payload.ok && payload.currentGroup) {
const keys = Object.keys(payload.currentGroup); const keys = Object.keys(payload.currentGroup);
if (keys.length > 0) { if (keys.length > 0) {
remoteGroup = payload.currentGroup; remoteGroup = payload.currentGroup;
log("info", "使用远程待同步 Product", `${payload.currentGroupIndex} 组,共 ${keys.length}`); groupIndex = payload.currentGroupIndex;
log("info", "使用远程待同步 Product", `${groupIndex} 组,共 ${keys.length}`);
} }
} }
} catch (_) {} } catch (_) {}
} }
if (remoteGroup) { if (remoteGroup) {
// 使用远程数据,不写入本地 XboxProductList已由服务端 fetch_and_clear 删除) useRemote = true;
sourceLabel = "远程同步"; sourceLabel = `远程第 ${groupIndex}`;
productList = parseProductList(JSON.stringify(remoteGroup)); productList = parseProductList(JSON.stringify(remoteGroup));
startTask(); startTask();
} else { } else {
// 远程无数据或连接失败,回退到本地 // 远程无数据(队列为空或被锁)或连接失败,回退到本地
useRemote = false;
const localRaw = $persistentStore.read(LOCAL_KEY) || "{}"; const localRaw = $persistentStore.read(LOCAL_KEY) || "{}";
sourceLabel = "本地"; sourceLabel = "本地";
productList = parseProductList(localRaw); productList = parseProductList(localRaw);