/** * SyncXboxCloud.js * 两步流程:先读取,写入成功后再删除 * 顶部时间戳锁防止 Surge 规则重复触发脚本 */ const readUrl = 'https://locvps.dragonisheep.com/surge?token=xbox123'; const commitUrl = 'https://locvps.dragonisheep.com/surge/commit?token=xbox123'; const storeKey = 'XboxProductList'; function escapeHTML(str) { return String(str || '') .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } function renderUI(title, message, type = "success") { const colorMap = { success: "#107C10", warning: "#d83b01", error: "#c50f1f", info: "#0078d4" }; const color = colorMap[type] || "#0078d4"; const html = `
无法连接至服务器。
${escapeHTML(String(error))}
`, "error"); } let payload; try { payload = JSON.parse((data || '').trim() || '{}'); } catch (e) { $notification.post("❌ 同步失败", "返回 JSON 无法解析", ""); return renderUI("❌ 解析错误", `服务器返回内容不是合法 JSON。
${escapeHTML(String(e.message || e))}
`, "error"); } if (!payload || typeof payload !== 'object' || Array.isArray(payload)) { $notification.post("❌ 同步失败", "返回结构异常", ""); return renderUI("❌ 数据异常", `服务器返回的数据结构不正确。
`, "error"); } if (!payload.ok) { $notification.post("❌ 同步失败", "服务器返回 ok=false", ""); return renderUI("❌ 同步失败", `服务器返回失败状态。
`, "error"); } const currentGroup = payload.currentGroup || {}; const keys = Object.keys(currentGroup); if (!keys.length) { $notification.post("📭 当前无待同步组", "云端队列为空", ""); return renderUI("📭 没有可同步内容", `当前没有待同步的 Product 分组。
`, "warning"); } // 第二步:写入本地,成功后才删云端 const writeOK = $persistentStore.write(JSON.stringify(currentGroup), storeKey); if (!writeOK) { $notification.post("❌ 同步失败", "写入 Surge 存储失败", ""); // 写入失败,不发 clear,云端数据保留,下次可以重试 return renderUI("❌ 写入失败", `无法写入 Surge 本地存储,云端数据未删除,下次访问可重试。
`, "error"); } // 第三步:本地写入成功,删除云端当前组 $httpClient.post({ url: commitUrl, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ remaining: {} }) }, (clearError, clearResponse, clearData) => { if (clearError) { $notification.post("⚠️ 本地已写入", `第 ${payload.currentGroupIndex} 组已保存`, "但云端清理失败,下次访问仍是这一组"); return renderUI( "⚠️ 当前组已写入,但云端未清理", `已成功写入第 ${escapeHTML(String(payload.currentGroupIndex))} 组,共 ${keys.length} 个商品。
但清理云端当前组时失败,下次访问时仍会是这一组。
`, "warning" ); } let clearPayload = {}; try { clearPayload = JSON.parse((clearData || '').trim() || '{}'); } catch (_) {} const remainingGroups = typeof clearPayload.remainingGroups === 'number' ? clearPayload.remainingGroups : (typeof payload.remainingAfterCurrent === 'number' ? payload.remainingAfterCurrent : 0); const nextGroupIndex = clearPayload.nextGroupIndex ?? null; const nextGroupCount = clearPayload.nextGroupCount ?? 0; const list = keys.map(k => `本次只同步了第 ${escapeHTML(String(payload.currentGroupIndex))} 组,共 ${keys.length} 个商品。
当前这一组已经从云端队列删除。
现在还剩 ${remainingGroups} 组待处理。
`; if (remainingGroups > 0 && nextGroupIndex !== null) { message += `下次访问网页时,将同步第 ${escapeHTML(String(nextGroupIndex))} 组(共 ${escapeHTML(String(nextGroupCount))} 个商品)。
`; } else { message += `所有分组已经处理完毕。
`; } message += `