This commit is contained in:
2026-04-02 21:02:16 +08:00
commit 75b01d3e58
56 changed files with 3714 additions and 0 deletions

View File

@@ -0,0 +1,129 @@
// ==== 配置项 ==== //
const PRODUCT_IDS = $persistentStore.read("TempProductId"); // 格式id1&id2
const MUID = $persistentStore.read("cart-x-authorization-muid");
const MS_CV = $persistentStore.read("cart-ms-cv");
// ==== 工具函数 ==== //
const generateRiskSessionId = () => "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, c => (c === "x" ? (Math.random()*16|0).toString(16) : (Math.random()*4|8).toString(16)));
// ==== 执行逻辑 ==== //
const productIdArray = PRODUCT_IDS?.split("&") || [];
const results = { success: [], failure: [] };
let currentIndex = 0;
const API_URL = "https://cart.production.store-web.dynamics.com/cart/v1.0/cart/loadCart?cartType=consumer&appId=StoreWeb";
const HEADERS = {
"content-type": "application/json",
"accept": "*/*",
"x-authorization-muid": MUID,
"sec-fetch-site": "cross-site",
"x-validation-field-1": "9pgbhbppjf2b",
"ms-cv": MS_CV,
"accept-language": "ha-Latn-NG,ha;q=0.9",
"accept-encoding": "gzip, deflate, br",
"sec-fetch-mode": "cors",
"origin": "https://www.microsoft.com",
"user-agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) EdgiOS/133.0.3065.54 Version/18.0 Mobile/15E148 Safari/604.1",
"referer": "https://www.microsoft.com/",
"x-ms-vector-id": "",
"sec-fetch-dest": "empty"
};
function sendRequest() {
if (currentIndex >= productIdArray.length) {
const successCount = results.success.length;
const failureList = results.failure.join("\n");
// 如果全部请求成功,则清空 TempProductId
if (results.failure.length === 0) {
$persistentStore.write("", "TempProductId");
}
$notification.post(
"🎮 操作完成",
`成功: ${successCount}`,
failureList || "所有请求均成功TempProductId已清空"
);
$done();
return;
}
const productId = productIdArray[currentIndex];
const riskSessionId = generateRiskSessionId();
const timeoutId = setTimeout(() => {
recordResult(productId, "超时");
proceed();
}, 30000);
// 发送skuId为0001的请求
$httpClient.put({
url: API_URL,
headers: HEADERS,
body: JSON.stringify({
locale: "en-ng",
market: "NG",
catalogClientType: "storeWeb",
friendlyName: "cart-NG",
riskSessionId: riskSessionId,
clientContext: { client: "UniversalWebStore.Cart", deviceType: "Pc" },
itemsToAdd: { items: [{ productId, skuId: "0001", quantity: 1 }] }
})
}, (error1, response1) => {
clearTimeout(timeoutId);
if (error1 || response1.status !== 200) {
recordResult(productId, `HTTP ${response1 ? response1.status : "Error1"}`);
proceed();
return;
}
// 发送skuId为0010的请求
$httpClient.put({
url: API_URL,
headers: HEADERS,
body: JSON.stringify({
locale: "en-ng",
market: "NG",
catalogClientType: "storeWeb",
friendlyName: "cart-NG",
riskSessionId: riskSessionId,
clientContext: { client: "UniversalWebStore.Cart", deviceType: "Pc" },
itemsToAdd: { items: [{ productId, skuId: "0010", quantity: 1 }] }
})
}, (error2, response2) => {
if (error2 || response2.status !== 200) {
recordResult(productId, `HTTP ${response2 ? response2.status : "Error2"}`);
} else {
// 两次请求都成功
handleSuccess(productId);
}
proceed();
});
});
}
// ==== 辅助函数 ==== //
function handleSuccess(id) {
results.success.push(id);
console.log(`${id}`);
}
function recordResult(id, reason) {
results.failure.push(`${id}: ${reason}`);
console.log(`${id} - ${reason}`);
}
function proceed() {
currentIndex++;
setTimeout(sendRequest, 10); // 请求间隔0.01秒
}
// ==== 启动检查 ==== //
if (!MUID || !PRODUCT_IDS) {
console.log("⚠️ 配置错误 - 缺少必要参数 MUID 或 PRODUCT_IDS");
$notification.post("配置错误", "缺少必要参数", "");
$done();
} else {
console.log("🚀 开始执行请求");
sendRequest();
}

View File

@@ -0,0 +1,24 @@
#!name=CoreHalo Link Collector
#!desc=Capture Xianyu/Goofish links, expose at https://corehalo.dump/
#!author=Ah Long
#!category=XBOX
#!version=1.4
#!homepage=https://github.com/XXhaos/Surge
[Host]
# 将 corehalo.dump 解析到 Surge 虚拟 IP
corehalo.dump = 198.18.0.1
[Script]
# 1. 捕获脚本 (保持原样,匹配闲鱼域名)
corehalo-capture = type=http-request, pattern=https?://h5\.m\.goofish\.com/.*reminderUrl=.+, requires-body=0, timeout=5, script-path=https://raw.githubusercontent.com/XXhaos/Surge/main/corehalo_capture.js
# 2. 导出脚本 (修改点)
# - 正则改为匹配 https://corehalo.dump/ (兼容 http)
# - 去掉了 fetch 路径,匹配根路径
corehalo-dump = type=http-request, pattern=^https?://corehalo\.dump/?$, requires-body=0, timeout=5, script-path=https://raw.githubusercontent.com/XXhaos/Surge/main/corehalo_dump.js
[MITM]
# 必须包含 corehalo.dump 才能解密 HTTPS 流量
# 同时也需要包含闲鱼域名(h5.m.goofish.com)以确保捕获功能正常
hostname = %APPEND% corehalo.dump, h5.m.goofish.com

View File

@@ -0,0 +1,41 @@
// Surge 脚本从特定URL中提取并更新 TempProductId
const key = "TempProductId";
// 获取已有 TempProductId 的值
const existingIds = $persistentStore.read(key);
// 从URL提取新的产品ID (仅匹配特定的 xbox 链接)
const url = $request.url;
// 改进正则表达式,匹配 store/ 后面的游戏ID支持大小写
const matches = url.match(/^https:\/\/www\.xbox\.com\/[eE][nN]-[uU][sS]\/games\/store\/[^\/]+\/([^\/?]+)/);
if (matches && matches[1]) {
const newProductId = matches[1];
// 将已有的 TempProductId 分割为数组
const existingIdArray = existingIds ? existingIds.split("&") : [];
if (!existingIdArray.includes(newProductId)) {
// 如果已有内容不为空,则先加入 '&' 再追加新ID
const finalProductId = existingIdArray.length > 0
? `${existingIdArray.join("&")}&${newProductId}`
: newProductId;
// 更新 TempProductId 的值
$persistentStore.write(finalProductId, key);
// 控制台输出操作
console.log(`✅ 已更新 TempProductId: ${finalProductId}`);
// 发送通知表示操作完成
$notification.post("✅ 操作成功", "已更新 TempProductId", finalProductId);
} else {
console.log(`⚠️ TempProductId 未更新,已存在: ${newProductId}`);
$notification.post("⚠️ 操作跳过", "TempProductId 已存在", newProductId);
}
}
// 结束脚本
$done();

View File

@@ -0,0 +1,51 @@
// corehalo_capture.js
//
// 触发:请求 URL 匹配 h5.m.goofish.com/...reminderUrl=...
// 逻辑:
// 1. 从 $request.url 提取 reminderUrl= 后的编码串
// 2. decodeURIComponent -> 真正目标链接 realLink
// 3. 保存到 persistentStore("corehalo_links")
// 4. 发通知,提示捕获到了什么
let reqUrl = $request && $request.url ? $request.url : "";
// 提取 reminderUrl= 后面的编码内容
let m = reqUrl.match(/reminderUrl=([^&]+)/);
if (m && m[1]) {
// decode 出真实外链
let realLink = decodeURIComponent(m[1]);
// 从持久化存储读现有列表
let raw = $persistentStore.read("corehalo_links") || "[]";
let list;
try {
list = JSON.parse(raw);
if (!Array.isArray(list)) {
list = [];
}
} catch (e) {
list = [];
}
// 去重后推入
let added = false;
if (!list.includes(realLink)) {
list.push(realLink);
$persistentStore.write(JSON.stringify(list), "corehalo_links");
added = true;
}
// 给你发一条通知,告诉你本次抓到的情况
// 标题CoreHalo Capture
// 副标题Added / Duplicate
// 正文:具体链接
$notification.post(
"CoreHalo Capture",
added ? "Added to list" : "Already in list",
realLink
);
}
// 放行原始请求
$done({});

View File

@@ -0,0 +1,84 @@
/*
* CoreHalo Web Dump
* 作用:访问 http://corehalo.dump/fetch 时,以网页形式显示捕获的链接并清空存储
*/
// 1. 读取数据
let raw = $persistentStore.read("corehalo_links") || "[]";
let list;
try {
list = JSON.parse(raw);
if (!Array.isArray(list)) list = [];
} catch (e) {
list = [];
}
const count = list.length;
// 2. 发送通知 (保持原有的系统通知功能)
if (count > 0) {
$notification.post("CoreHalo Dump", `捕获 ${count} 条链接`, "已在浏览器显示并清空");
} else {
$notification.post("CoreHalo Dump", "没有新链接", "列表为空");
}
// 3. 准备 HTML 内容
// 生成列表项 HTML
const listHtml = list.map((link, index) => `
<div class="item">
<span class="index">${index + 1}.</span>
<a href="${link}" target="_blank">${link}</a>
</div>
`).join("");
// 页面样式
const css = `
<style>
body { font-family: -apple-system, sans-serif; background-color: #f4f4f4; padding: 20px; display: flex; justify-content: center; }
.card { background: white; width: 100%; max-width: 600px; padding: 20px; border-radius: 12px; box-shadow: 0 4px 10px rgba(0,0,0,0.05); }
h1 { margin-top: 0; font-size: 20px; color: #333; border-bottom: 1px solid #eee; padding-bottom: 10px; }
.count-tag { background: #007aff; color: white; padding: 2px 8px; border-radius: 10px; font-size: 14px; margin-left: 8px; vertical-align: middle; }
.empty-state { text-align: center; color: #999; padding: 40px 0; }
.list-container { max-height: 70vh; overflow-y: auto; }
.item { padding: 12px 0; border-bottom: 1px solid #f0f0f0; display: flex; align-items: flex-start; word-break: break-all; }
.item:last-child { border-bottom: none; }
.index { color: #999; font-size: 12px; margin-right: 10px; min-width: 20px; margin-top: 2px; }
a { color: #333; text-decoration: none; font-size: 13px; line-height: 1.4; transition: color 0.2s; }
a:hover { color: #007aff; }
.footer { margin-top: 15px; font-size: 12px; color: #aaa; text-align: center; }
</style>
`;
const bodyContent = count > 0
? `<div class="list-container">${listHtml}</div>`
: `<div class="empty-state">📭 暂无捕获的链接</div>`;
const html = `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>CoreHalo Links</title>
${css}
</head>
<body>
<div class="card">
<h1>捕获列表 <span class="count-tag">${count}</span></h1>
${bodyContent}
<div class="footer">列表已自动清空</div>
</div>
</body>
</html>`;
// 4. 清空存储 (提取后即焚)
$persistentStore.write("[]", "corehalo_links");
// 5. 返回网页响应
$done({
response: {
status: 200,
headers: { "Content-Type": "text/html;charset=utf-8" },
body: html
}
});

View File

@@ -0,0 +1,12 @@
#!name=Xbox Buy Request Replace
#!desc=替换购买请求参数 (RequestParentalApproval)
#!category=XBOX
#!author=Ah Long
[Script]
# 替换购买请求参数
# 触发条件RequestParentalApproval 接口
BuyRequestReplace = type=http-request, pattern=^https://buynow\.production\.store-web\.dynamics\.com/v1\.0/Cart/RequestParentalApproval, script-path=https://raw.githubusercontent.com/XXhaos/Surge/refs/heads/main/Scripts/BuyRequestReplace.js, requires-body=1, max-size=0, binary-body-mode=0, script-update-interval=0
[MITM]
hostname = %APPEND% buynow.production.store-web.dynamics.com

View File

@@ -0,0 +1,11 @@
#!name=Microsoft Family Block
#!desc=拦截 Microsoft Family 中指定的 ProductId 购买请求 (9PNTSH5SKCL5等)
#!category=XBOX
#!author=Ah Long
[Script]
# logic: 检查 POST body 是否包含黑名单中的 ID
MS_Family_Block = type=http-request, pattern=^https://account\.microsoft\.com/family/api/buy/requests/complete(\?.*)?$, requires-body=1, max-size=0, script-path=https://raw.githubusercontent.com/XXhaos/Surge/refs/heads/main/Scripts/ms_family_block.js, script-update-interval=-1, timeout=10, debug=0, script-text=var targetIds=["9PNTSH5SKCL5","9nfmccp0pm67","9npbvj8lwsvn","9pcgszz8zpq2","9P54FF0VQD7R","9NCJZN3LBD3P","9P9CLTVLLHD6","9NHXDFLDBN6G"];var body=$request.body;if($request.method==="POST"&&body){var upperBody=body.toUpperCase();for(var i=0;i<targetIds.length;i++){if(upperBody.indexOf(targetIds[i].toUpperCase())!==-1){console.log("[MS-Block] Intercepted blocked ProductId: "+targetIds[i]);$done({response:{status:403,headers:{"Content-Type":"text/plain"},body:"Blocked by Surge Module"}});break}}$done({})}else{$done({})}
[MITM]
hostname = %APPEND% account.microsoft.com

View File

@@ -0,0 +1,171 @@
#!name=Q-Search Plus
#!desc=Bing搜索重定向
#引用自墨鱼@ddgksf2013转换为Surge格式并加入自用重写修改日期2024-07-23
##############################################
# - Safari 内输入以下格式命令快速指定搜索引擎
# - 【命令+空格+关键词】或者【关键词+空格+命令】
# - 注:请先进入设置更改 Safari 默认搜索为 Bing
# - 更新时间2024-01-31
# - 墨鱼自用全能搜索V2.0(135)
# - 墨鱼手记
# - 如需引用请注明出处-> https://t.me/ddgksf2021 谢谢合作!
# - https://github.com/ddgksf2013/Rewrite/raw/master/Html/Q-Search.conf
##############################################
[URL Rewrite]
#>>>>>>>>>>>>>>>>>>>>>>>自用
# TMDB
^https:\/\/.*bing.com\/search\?q=tmdb\+([^&]+).+ https://www.themoviedb.org/search?query=$1 302
^https:\/\/.*bing.com\/search\?q=([^+]+)\+tmdb.+ https://www.themoviedb.org/search?query=$1 302
# ng (切换至尼日利亚区)
^https:\/\/.*bing.com\/search\?q=ng&.+ https://itunes.apple.com/WebObjects/MZStore.woa/wa/resetAndRedirect?dsf=143561&mt=8&url=/WebObjects/MZStore.woa/wa/viewSoftware?mt=8&id=1108187390&cc=ng&urlDesc= 302
# pp xxx (perplexity)
^https:\/\/.*bing.com\/search\?q=pp\+([^&]+).+ https://www.perplexity.ai/search?q=$1 302
^https:\/\/.*bing.com\/search\?q=([^+]+)\+pp.+ https://www.perplexity.ai/search?q=$1 302
#tf (TestFlight)
^https:\/\/.*bing.com\/search\?q=tf(\+|%20)([^&]+).+ https://www.google.com/search?as_q=$2&as_sitesearch=testflight.apple.com 302
#>>>>>>>>>>>>>>>>>>>>>>>翻译
# yd xxx (有道词典)
^https:\/\/.*bing.com\/search\?q=yd\+([^&]+).+ http://dict.youdao.com/search?q=$1 302
^https:\/\/.*bing.com\/search\?q=([^+]+)\+yd.+ http://dict.youdao.com/search?q=$1 302
# trc xxx (Google 译至中)
^https:\/\/.*bing.com\/search\?q=trc\+([^&]+).+ https://translate.google.com/#view=home&op=translate&sl=auto&tl=zh-CN&text=$1 302
^https:\/\/.*bing.com\/search\?q=([^+]+)\+trc.+ https://translate.google.com/#view=home&op=translate&sl=auto&tl=zh-CN&text=$1 302
# tre xxx (Google 译至英)
^https:\/\/.*bing.com\/search\?q=tre\+([^&]+).+ https://translate.google.com/#view=home&op=translate&sl=auto&tl=en&text=$1 302
^https:\/\/.*bing.com\/search\?q=([^+]+)\+tre.+ https://translate.google.com/#view=home&op=translate&sl=auto&tl=en&text=$1 302
# trj xxx (Google 译至日)
^https:\/\/.*bing.com\/search\?q=trj\+([^&]+).+ https://translate.google.com/#view=home&op=translate&sl=auto&tl=ja&text=$1 302
^https:\/\/.*bing.com\/search\?q=([^+]+)\+trj.+ https://translate.google.com/#view=home&op=translate&sl=auto&tl=ja&text=$1 302
#>>>>>>>>>>>>>>>>>>>>>>>社区
# tt xxx (头条)
^https:\/\/.*bing.com\/search\?q=tt\+([^&]+).+ https://so.toutiao.com/search?keyword=$1 302
^https:\/\/.*bing.com\/search\?q=([^+]+)\+tt.+ https://so.toutiao.com/search?keyword=$1 302
# db xxx (豆瓣)
^https:\/\/.*bing.com\/search\?q=db\+([^&]+).+ https://m.douban.com/search?query=$1 302
^https:\/\/.*bing.com\/search\?q=([^+]+)\+db.+ https://m.douban.com/search?query=$1 302
# zh xxx (知乎)
^https:\/\/.*bing.com\/search\?q=zh\+([^&]+).+ http://www.zhihu.com/search?q=$1 302
^https:\/\/.*bing.com\/search\?q=([^+]+)\+zh.+ http://www.zhihu.com/search?q=$1 302
# wb xxx (微博)
^https:\/\/.*bing.com\/search\?q=wb\+([^&]+).+ https://s.weibo.com/weibo/$1 302
^https:\/\/.*bing.com\/search\?q=([^+]+)\+wb.+ https://s.weibo.com/weibo/$1 302
# wx xxx (微信)
^https:\/\/.*bing.com\/search\?q=wx\+([^&]+).+ https://weixin.sogou.com/weixinwap?query=$1 302
^https:\/\/.*bing.com\/search\?q=([^+]+)\+wx.+ https://weixin.sogou.com/weixinwap?query=$1 302
# up xxx (Unsplash)
^https:\/\/.*bing.com\/search\?q=up\+([^&]+).+ https://unsplash.com/s/photos/$1 302
^https:\/\/.*bing.com\/search\?q=([^+]+)\+up.+ https://unsplash.com/s/photos/$1 302
# sspai xxx (少数派站内搜索)
^https:\/\/.*bing.com\/search\?q=sspai\+([^&]+).+ https://sspai.com/search/post/$1 302
^https:\/\/.*bing.com\/search\?q=([^+]+)\+sspai.+ https://sspai.com/search/post/$1 302
# ssp xxx (Google 搜索少数派)
^https:\/\/.*bing.com\/search\?q=ssp\+([^&]+).+ https://www.google.com/search?as_q=$1&as_sitesearch=sspai.com 302
^https:\/\/.*bing.com\/search\?q=([^+]+)\+ssp.+ https://www.google.com/search?as_q=$1&as_sitesearch=sspai.com 302
# tw xxx (Twitter)
^https:\/\/.*bing.com\/search\?q=tw\+([^&]+).+ https://twitter.com/search?q=$1 302
^https:\/\/.*bing.com\/search\?q=([^+]+)\+tw.+ https://twitter.com/search?q=$1 302
# gh xxx (GitHub)
^https:\/\/.*bing.com\/search\?q=gh\+([^&]+).+ https://github.com/search?q=$1 302
^https:\/\/.*bing.com\/search\?q=([^+]+)\+gh.+ https://github.com/search?q=$1 302
# gu xxx (GitHub User)
^https:\/\/.*bing.com\/search\?q=gu\+([^&]+).+ https://github.com/search?q=$1&type=users 302
^https:\/\/.*bing.com\/search\?q=([^+]+)\+gu.+ https://github.com/search?q=$1&type=users 302
# gc xxx (GitHub Code)
^https:\/\/.*bing.com\/search\?q=gc\+([^&]+).+ https://github.com/search?o=desc&q=$1&s=indexed&type=Code 302
^https:\/\/.*bing.com\/search\?q=([^+]+)\+gc.+ https://github.com/search?o=desc&q=$1&s=indexed&type=Code 302
# so xxx (Stack Overflow)
^https:\/\/.*bing.com\/search\?q=so\+([^&]+).+ https://stackoverflow.com/search?q=$1 302
^https:\/\/.*bing.com\/search\?q=([^+]+)\+so.+ https://stackoverflow.com/search?q=$1 302
# se xxx (StackExchange)
^https:\/\/.*bing.com\/search\?q=se\+([^&]+).+ https://stackexchange.com/search?q=$1 302
^https:\/\/.*bing.com\/search\?q=([^+]+)\+se.+ https://stackexchange.com/search?q=$1 302
# wa xxx (WolframAlpha)
^https:\/\/.*bing.com\/search\?q=wa\+([^&]+).+ https://www.wolframalpha.com/input/?i=$1 302
^https:\/\/.*bing.com\/search\?q=([^+]+)\+wa.+ https://www.wolframalpha.com/input/?i=$1 302
# rd xxx (Reddit)
^https:\/\/.*bing.com\/search\?q=rd\+([^&]+).+ https://www.reddit.com/search?q=$1 302
^https:\/\/.*bing.com\/search\?q=([^+]+)\+rd.+ https://www.reddit.com/search?q=$1 302
#>>>>>>>>>>>>>>>>>>>>>>>购物
# zdm xxx (什么值得买)
^https:\/\/.*bing.com\/search\?q=zdm\+([^&]+).+ https://search.m.smzdm.com/?v=b&s=$1 302
^https:\/\/.*bing.com\/search\?q=([^+]+)\+zdm.+ https://search.m.smzdm.com/?v=b&s=$1 302
# jd xxx (京东)
^https:\/\/.*bing.com\/search\?q=jd\+([^&]+).+ openapp.jdmobile://virtual?params={"des":"productList","keyWord":"$1","from":"search","category":"jump"} 302
^https:\/\/.*bing.com\/search\?q=([^+]+)\+jd.+ openapp.jdmobile://virtual?params={"des":"productList","keyWord":"$1","from":"search","category":"jump"} 302
# tb xxx (淘宝)
^https:\/\/.*bing.com\/search\?q=tb\+([^&]+).+ taobao://s.taobao.com?q=$1 302
^https:\/\/.*bing.com\/search\q=([^+]+)\+tb.+ taobao://s.taobao.com?q=$1 302
#>>>>>>>>>>>>>>>>>>>>>>>视频
# yt xxx (YouTube)
^https:\/\/.*bing.com\/search\?q=yt\+([^&]+).+ https://www.youtube.com/results?search_query=$1 302
^https:\/\/.*bing.com\/search\?q=([^+]+)\+yt.+ https://www.youtube.com/results?search_query=$1 302
# bli xxx (哔哩哔哩)
^https:\/\/.*bing.com\/search\?q=bli\+([^&]+).+ https://m.bilibili.com/search?keyword=$1 302
^https:\/\/.*bing.com\/search\?q=([^+]+)\+bli.+ https://m.bilibili.com/search?keyword=$1 302
# gd xxx (Google 搜索 Google Drive 资源)
^https:\/\/.*bing.com\/search\?q=gd\+([^&]+).+ https://www.google.com/search?q=%22Google+Drive%22+$1 302
^https:\/\/.*bing.com\/search\?q=([^+]+)\+gd.+ https://www.google.com/search?q=%22Google+Drive%22+$1 302
# tgd xxx (t.me/gdurl 搜索 Google Drive 资源)
^https:\/\/.*bing.com\/search\?q=tgd\+([^&]+).+ https://t.me/s/gdurl?q=$1 302
^https:\/\/.*bing.com\/search\?q=([^+]+)\+tgd.+ https://t.me/s/gdurl?q=$1 302
# ph xxx (PornHub)
^https:\/\/.*bing.com\/search\?q=ph\+([^&]+).+ https://cn.pornhub.com/video/search?search=$1 302
^https:\/\/.*bing.com\/search\?q=([^+]+)\+ph.+ https://cn.pornhub.com/video/search?search=$1 302
# af xxx (Acfun)
^https:\/\/.*bing.com\/search\?q=af\+([^&]+).+ https://www.acfun.cn/search?keyword=$1 302
^https:\/\/.*bing.com\/search\?q=([^+]+)\+af.+ https://www.acfun.cn/search?keyword=$1 302
# ys xxx (搜片)
^https:\/\/.*bing.com\/search\?q=ys\+([^&]+).+ https://soupian.icu/search?key=$1 302
^https:\/\/.*bing.com\/search\?q=([^+]+)\+ys.+ https://soupian.icu/search?key=$1 302
#>>>>>>>>>>>>>>>>>>>>>>>苹果商店切换
# cn (切换至中国区)
^https:\/\/.*bing.com\/search\?q=cn&.+ https://itunes.apple.com/WebObjects/MZStore.woa/wa/resetAndRedirect?dsf=143465&mt=8&url=/WebObjects/MZStore.woa/wa/viewSoftware?mt=8&id=1108187390&cc=cn&urlDesc= 302
# hk (切换至香港区)
^https:\/\/.*bing.com\/search\?q=hk&.+ https://itunes.apple.com/WebObjects/MZStore.woa/wa/resetAndRedirect?dsf=143463&mt=8&url=/WebObjects/MZStore.woa/wa/viewSoftware?mt=8&id=1108187390&cc=hk&urlDesc= 302
# tw (切换至台湾区)
^https:\/\/.*bing.com\/search\?q=tw&.+ https://itunes.apple.com/WebObjects/MZStore.woa/wa/resetAndRedirect?dsf=143470&mt=8&url=/WebObjects/MZStore.woa/wa/viewSoftware?mt=8&id=1108187390&cc=tw&urlDesc= 302
# us (切换至美国区)
^https:\/\/.*bing.com\/search\?q=us&.+ https://itunes.apple.com/WebObjects/MZStore.woa/wa/resetAndRedirect?dsf=143441&mt=8&url=/WebObjects/MZStore.woa/wa/viewSoftware?mt=8&id=1108187390&cc=us&urlDesc= 302
# jp (切换至日本区)
^https:\/\/.*bing.com\/search\?q=jp&.+ https://itunes.apple.com/WebObjects/MZStore.woa/wa/resetAndRedirect?dsf=143462&mt=8&url=/WebObjects/MZStore.woa/wa/viewSoftware?mt=8&id=1108187390&cc=jp&urlDesc= 302
# kr (切换至韩国区)
^https:\/\/.*bing.com\/search\?q=kr&.+ https://itunes.apple.com/WebObjects/MZStore.woa/wa/resetAndRedirect?dsf=143466&mt=8&url=/WebObjects/MZStore.woa/wa/viewSoftware?mt=8&id=1108187390&cc=kr&urlDesc= 302
# tr (切换至土耳其区)
^https:\/\/.*bing.com\/search\?q=tr&.+ https://itunes.apple.com/WebObjects/MZStore.woa/wa/resetAndRedirect?dsf=143480&mt=8&url=/WebObjects/MZStore.woa/wa/viewSoftware?mt=8&id=1108187390&cc=tr&urlDesc= 302
# sg (切换至新加坡区)
^https:\/\/.*bing.com\/search\?q=sg&.+ https://itunes.apple.com/WebObjects/MZStore.woa/wa/resetAndRedirect?dsf=143464&mt=8&url=/WebObjects/MZStore.woa/wa/viewSoftware?mt=8&id=1108187390&cc=tr&urlDesc= 302
#>>>>>>>>>>>>>>>>>>>>>>>搜索
# bd xxx (百度搜索)
^https:\/\/.*bing.com\/search\?q=bd\+([^&]+).+ https://www.baidu.com/s?wd=$1 302
^https:\/\/.*bing.com\/search\?q=((.(?!bd))+)\+bd.+ https://www.baidu.com/s?wd=$1 302
# wk xxx (维基搜索)
^https:\/\/.*bing.com\/search\?q=wk\+([^&]+).+ https://zh.wikipedia.org/wiki/$1 302
^https:\/\/.*bing.com\/search\?q=([^+]+)\+wk.+ https://zh.wikipedia.org/wiki/$1 302
# wz xxx (无追搜索)
^https:\/\/.*bing.com\/search\?q=wz\+([^&]+).+ https://www.wuzhuiso.com/s?q=$1 302
^https:\/\/.*bing.com\/search\?q=([^+]+)\+wz.+ https://www.wuzhuiso.com/s?q=$1 302
# yh xxx (油猴搜索)
^https:\/\/.*bing.com\/search\?q=yh\+([^&]+).+ https://greasyfork.org/zh-CN/scripts?q=$1 302
^https:\/\/.*bing.com\/search\?q=([^+]+)\+yh.+ https://greasyfork.org/zh-CN/scripts?q=$1 302
# gi xxx (Google 图片)
^https:\/\/.*bing.com\/search\?q=gi\+([^&]+).+ https://www.google.com/search?&tbm=isch&q=$1 302
^https:\/\/.*bing.com\/search\?q=([^+]+)\+gi.+ https://www.google.com/search?&tbm=isch&q=$1 302
# ios xxx (苹果应用搜索)
^https:\/\/.*bing.com\/search\?q=ios\+([^&]+).+ https://www.qimai.cn/search/index/search/$1 302
^https:\/\/.*bing.com\/search\?q=([^+]+)\+ios.+ https://www.qimai.cn/search/index/search/$1 302
# xxx (无指令默认为 Google
^https:\/\/.*bing.com\/search\?q=([^&]+).+ https://www.google.com/search?q=$1 302
[MITM]
hostname = %APPEND% *.bing.com

View File

@@ -0,0 +1,16 @@
#!name=Xbox Cart Web
#!desc=访问 https://addtocart.com 触发远程脚本
#!author=Ah Long
#!category=XBOX
[Host]
# 核心:将 addtocart.com 解析到 Surge 内部 IP
# 这样手机发出的请求会被 Surge 直接截获,不会去公网找服务器
addtocart.com = 198.18.0.1
[Script]
# 正则匹配:支持 http 和 https域名为 addtocart.com
xbox_web_runner = type=http-request, pattern=^https?://addtocart\.com/?.*, script-path=https://raw.githubusercontent.com/XXhaos/Surge/refs/heads/main/Scripts/NewAddToCart_Web.js, timeout=60
[MITM]
hostname = %APPEND% addtocart.com

View File

@@ -0,0 +1,26 @@
#!name=Xbox Param Capturer
#!desc=抓取 Xbox 参数 (全区/防缓存/忽略大小写)
#!author=Ah Long
#!category=XBOX
[Script]
# 1. 抓 Product 参数 (Response)
# 修复:优化正则写法,忽略大小写
XboxProductList = type=http-response, pattern=(?i)^https://emerald\.xboxservices\.com/xboxcomfd/productActions/.*locale=en-us, requires-body=1, max-size=0, binary-body-mode=0, timeout=60, script-update-interval=0, script-path=https://raw.githubusercontent.com/XXhaos/Surge/refs/heads/main/Scripts/XboxProductList.js
# 2. 获取 Authorization & CartId
# 修复:加上 (?i) 忽略 Cart/cart 大小写,去掉强制结尾的 \?
Xbox_Auth_CartId = type=http-request, pattern=(?i)^https://cart\.production\.store-web\.dynamics\.com/v1\.0/cart/eligibilityCheck, requires-body=0, script-update-interval=0, script-path=https://raw.githubusercontent.com/XXhaos/Surge/refs/heads/main/Scripts/authorization%26cartId.js
# 3. 获取 CartParameter
# 修复:加上 (?i) 忽略大小写
Xbox_Mscv_MUID = type=http-request, pattern=(?i)^https://cart\.production\.store-web\.dynamics\.com/v1\.0/cart/loadCart, requires-body=0, script-update-interval=0, script-path=https://raw.githubusercontent.com/XXhaos/Surge/refs/heads/main/Scripts/CartParameter.js
[Header Rewrite]
# 【防缓存】强制删除 emerald 域名的协商缓存头
# 修复:简化正则,确保匹配命中
^https://emerald\.xboxservices\.com/xboxcomfd/productActions/ header-del If-Modified-Since
^https://emerald\.xboxservices\.com/xboxcomfd/productActions/ header-del If-None-Match
[MITM]
hostname = %APPEND% emerald.xboxservices.com, *.dynamics.com

View File

@@ -0,0 +1,26 @@
#!name=Xbox Web Controller
#!desc=通过访问虚拟域名触发脚本:\n1. 访问 https://addtocart.com -> 触发添加/购买脚本\n2. 访问 https://clearlist.com -> 清空本地 ProductList\n3. 访问 https://clearapprovalcartid.com -> 清空 ApproveCartId\n4. 访问 https://syncxbox.com -> 从云端同步数据并清理云端队列
#!author=Ah Long & XXhaos
#!category=XBOX
[Host]
addtocart.com = 198.18.0.1
clearlist.com = 198.18.0.1
clearapprovalcartid.com = 198.18.0.1
syncxbox.com = 198.18.0.1
[Script]
# 1. 触发远程添加/购买脚本
xbox_web_runner = type=http-request, pattern=^https?://addtocart\.com/?.*, script-path=https://raw.githubusercontent.com/XXhaos/Surge/refs/heads/main/Scripts/NewAddToCart_Web.js, timeout=60
# 2. 清空本地 XboxProductList
xbox_clear_web = type=http-request, pattern=^https?://clearlist\.com/?.*, script-path=https://raw.githubusercontent.com/XXhaos/Surge/main/Scripts/ClearXboxProductList_Web.js, timeout=10
# 3. 清空 ApprovalCartId
clear_cart_id = type=http-request, pattern=^https?://clearapprovalcartid\.com/?.*, script-path=https://raw.githubusercontent.com/XXhaos/Surge/refs/heads/main/Scripts/ClearApprovalCartId.js, timeout=10
# 4. 从云端同步数据 (确保 GitHub 上的文件名和路径 100% 正确)
xbox_sync_cloud = type=http-request, pattern=^https?://syncxbox\.com/?.*, script-path=https://raw.githubusercontent.com/XXhaos/Surge/refs/heads/main/Scripts/SyncXboxCloud.js, timeout=30
[MITM]
hostname = %APPEND% addtocart.com, clearlist.com, clearapprovalcartid.com, syncxbox.com

View File

@@ -0,0 +1,17 @@
#!name=Xbox Rewrite Collection
#!desc=Win 商店链接重定向 & 强制锁美区 (保留阿根廷区)
#!category=XBOX
#!author=Ah Long
[URL Rewrite]
# Microsoft Store -> 对应的 CoreHalo 游戏页(提取 12 位 ID
(?i)^https?:\/\/(?:www\.)?microsoft\.com\/en-us\/store\/.*?([a-z0-9]{12})(?:[\/\?#]|$) https://www.xbox.com/en-us/games/store/corehalo/$1 302
# 非 en-us 的 xbox.com 统一跳转到 en-us排除 es-ar / es-AR / en-us
^https:\/\/www\.xbox\.com\/(?!es-ar|es-AR|en-us)([a-zA-Z]{2}-[a-zA-Z]{2})(\/.*)$ https://www.xbox.com/en-us$2 302
# app.corehalo.com 的 r 区域参数统一改为 en-us保留其他 query 参数
"^https:\/\/app\.corehalo\.com\/ms\/link\/go\?(.*&)?r=(?!es-ar|es-AR|en-us)([a-zA-Z]{2}-[a-zA-Z]{2})(&.*)?$" "https://app.corehalo.com/ms/link/go?$1r=en-us$3" 302
[MITM]
hostname = %APPEND% www.microsoft.com, www.xbox.com, app.corehalo.com

View File

@@ -0,0 +1,13 @@
#!name=Microsoft Family Batch Approve
#!desc=自动抓取CartId并替换批量购买流程购买完成后自动清理
#!category=XBOX
#!author=Ah Long
[Script]
ApprovalCartId = type=http-request, pattern=^https:\/\/buynow\.production\.store-web\.dynamics\.com\/v1\.0\/cart\/updateCart.*, script-path=https://raw.githubusercontent.com/XXhaos/Surge/refs/heads/main/Scripts/ApprovalCartId.js, requires-body=0
batch_purchase_send = type=http-request, pattern=^https:\/\/buynow\.production\.store-web\.dynamics\.com\/v1\.0\/Cart\/purchase\?appId=BuyNow, script-path=https://raw.githubusercontent.com/XXhaos/Surge/refs/heads/main/Scripts/batch_purchase_send.js, requires-body=1, max-size=1048576, timeout=60, binary-body-mode=0, script-update-interval=0
ApprovalCartClean = type=http-request, pattern=^https:\/\/account\.microsoft\.com\/family\/api\/buy\/requests\/complete, script-path=https://raw.githubusercontent.com/XXhaos/Surge/refs/heads/main/Scripts/AutoClearApprovalCartId.js, requires-body=0
[MITM]
hostname = %APPEND% buynow.production.store-web.dynamics.com, production.store-web.dynamics.com, account.microsoft.com

2
Surge-main/README.md Normal file
View File

@@ -0,0 +1,2 @@
# Surge
自用

View File

@@ -0,0 +1,103 @@
/**
* Surge Script - 微软购物车批量添加
* 特性:动态生成 riskSessionId
*/
// ==== 配置项 ==== //
const PRODUCT_IDS = $persistentStore.read("productId"); // 格式id1&id2
const MUID = $persistentStore.read("cart-x-authorization-muid");
const MS_CV = $persistentStore.read("cart-ms-cv");
// ==== 工具函数 ==== //
const generateRiskSessionId = () => "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, c => (c === "x" ? (Math.random()*16|0).toString(16) : (Math.random()*4|8).toString(16)));
// ==== 执行逻辑 ==== //
const productIdArray = PRODUCT_IDS?.split("&") || [];
const results = { success: [], failure: [] };
let currentIndex = 0;
const API_URL = "https://cart.production.store-web.dynamics.com/cart/v1.0/cart/loadCart?cartType=consumer&appId=StoreWeb";
const HEADERS = {
"content-type": "application/json",
"accept": "*/*",
"x-authorization-muid": MUID,
"sec-fetch-site": "cross-site",
"x-validation-field-1": "9pgbhbppjf2b",
"ms-cv": MS_CV,
"accept-language": "ha-Latn-NG,ha;q=0.9",
"accept-encoding": "gzip, deflate, br",
"sec-fetch-mode": "cors",
"origin": "https://www.microsoft.com",
"user-agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) EdgiOS/133.0.3065.54 Version/18.0 Mobile/15E148 Safari/604.1",
"referer": "https://www.microsoft.com/",
"x-ms-vector-id": "",
"sec-fetch-dest": "empty"
};
function sendRequest() {
if (currentIndex >= productIdArray.length) {
const successCount = results.success.length;
const failureList = results.failure.join("\n");
$notification.post(
"🎮 操作完成",
`成功: ${successCount}`,
failureList || "所有请求均成功"
);
$done();
return;
}
const productId = productIdArray[currentIndex];
const riskSessionId = generateRiskSessionId();
const timeoutId = setTimeout(() => {
recordResult(productId, "超时");
proceed();
}, 30000);
$httpClient.put({
url: API_URL,
headers: HEADERS,
body: JSON.stringify({
locale: "en-ng",
market: "NG",
catalogClientType: "storeWeb",
friendlyName: "cart-NG",
riskSessionId: riskSessionId,
clientContext: { client: "UniversalWebStore.Cart", deviceType: "Pc" },
itemsToAdd: { items: [{ productId, skuId: "0010", quantity: 1 }] }
})
}, (error, response) => {
clearTimeout(timeoutId);
error ? recordResult(productId, error)
: response.status === 200 ? handleSuccess(productId)
: recordResult(productId, `HTTP ${response.status}`);
proceed();
});
}
// ==== 辅助函数 ==== //
function handleSuccess(id) {
results.success.push(id);
console.log(`${id}`);
}
function recordResult(id, reason) {
results.failure.push(`${id}: ${reason}`);
console.log(`${id} - ${reason}`);
}
function proceed() {
currentIndex++;
setTimeout(sendRequest, 100); // 请求间隔0.0秒
}
// ==== 启动检查 ==== //
if (!MUID || !PRODUCT_IDS) {
console.log("⚠️ 配置错误 - 缺少必要参数 MUID 或 PRODUCT_IDS");
$notification.post("配置错误", "缺少必要参数", "");
$done();
} else {
console.log("🚀 开始执行请求");
sendRequest();
}

View File

@@ -0,0 +1,56 @@
/**
* Surge 脚本:从 buynow 链接中提取 cartId 并存入 ApprovalCartId
* 存储格式id1&id2&id3...
*/
const key = "ApprovalCartId";
// 1. 获取已有 ApprovalCartId 的值
const existingRaw = $persistentStore.read(key);
// 2. 获取当前请求 URL
const url = $request.url;
// 3. 正则表达式提取 cartId
// 说明:[?&] 匹配开始的 ? 或中间的 &cartId= 匹配参数名,([^&]+) 捕获直到下一个 & 或字符串结束的内容
const matches = url.match(/[?&]cartId=([^&]+)/);
if (matches && matches[1]) {
const newCartId = matches[1];
// 将已有的字符串分割为数组 (过滤掉空字符串,防止 split 产生 bug)
let existingIdArray = existingRaw ? existingRaw.split("&").filter(Boolean) : [];
// 4. 判重逻辑
if (!existingIdArray.includes(newCartId)) {
// 追加新 ID 到数组
existingIdArray.push(newCartId);
// 重新组合成字符串
const finalString = existingIdArray.join("&");
// 5. 写入 Persistent Store
$persistentStore.write(finalString, key);
// 控制台日志
console.log(`✅ [CartId提取] 已追加: ${newCartId}`);
console.log(`📄 当前列表: ${finalString}`);
// 发送通知
$notification.post(
"✅ CartId 抓取成功",
`已存入第 ${existingIdArray.length} 个 ID`,
newCartId
);
} else {
console.log(`⚠️ [CartId提取] 跳过,已存在: ${newCartId}`);
// 如果需要重复时也弹窗,取消下面这行的注释
// $notification.post("⚠️ 跳过重复 ID", "该 CartId 已在列表中", newCartId);
}
} else {
console.log("⚠️ URL 中未找到 cartId 参数");
}
// 结束脚本,继续请求
$done({});

View File

@@ -0,0 +1,22 @@
/**
* Surge 脚本Clear_ApprovalCartId.js
* 作用:监测到 Family Safety 的 Complete 请求时,自动清空 ApprovalCartId
*/
const STORE_KEY = "ApprovalCartId";
(function() {
// 只有 POST 方法才触发清理
if ($request.method === "POST") {
const oldValue = $persistentStore.read(STORE_KEY);
if (oldValue) {
$persistentStore.write("", STORE_KEY);
console.log("🧹 [Batch Approve] 购买流程结束,已自动清空 ApprovalCartId 缓存。");
$notification.post("Microsoft Family", "购买流程结束", "ApprovalCartId 缓存已清空");
}
}
$done({});
})();

View File

@@ -0,0 +1,83 @@
// Surge脚本拦截 RequestParentalApproval 并替换 CartId/Auth
// 增加了通知功能:成功或失败都会弹窗提示
// 1. 判断 HTTP 请求方法是否为 POST
if ($request.method !== "POST") {
$done({});
} else {
// 定义状态标记,用于最后发通知
let status = { cartId: false, auth: false };
let errorMsg = [];
// 2. 读取持久化存储
let newCartId = $persistentStore.read("cartId");
let newAuth = $persistentStore.read("authorization");
if (!newCartId) {
console.log("警告Store 中缺少 cartId");
errorMsg.push("缺少 cartId");
}
if (!newAuth) {
console.log("警告Store 中缺少 authorization");
errorMsg.push("缺少 Auth");
}
// 3. 解析并修改 Request Body (CartId)
let bodyStr = $request.body || "";
let bodyObj;
try {
bodyObj = JSON.parse(bodyStr);
} catch (e) {
console.log("JSON 解析失败:" + e);
errorMsg.push("JSON 解析错误");
bodyObj = null;
}
// 4. 执行替换CartId
if (bodyObj && newCartId) {
bodyObj.cartId = newCartId;
bodyStr = JSON.stringify(bodyObj);
status.cartId = true; // 标记成功
}
// 5. 执行替换Authorization
let headers = $request.headers;
if (newAuth) {
let authHeaderKey = Object.keys(headers).find(k => k.toLowerCase() === "authorization");
if (authHeaderKey) {
headers[authHeaderKey] = newAuth;
} else {
headers["Authorization"] = newAuth;
}
status.auth = true; // 标记成功
}
// 6. 修正 Content-Length
if (bodyStr !== $request.body) {
let lenKey = Object.keys(headers).find(k => k.toLowerCase() === "content-length");
if (lenKey) delete headers[lenKey];
}
// 7. 发送通知逻辑
if (status.cartId && status.auth) {
// 情况A完美两个都替换了
$notification.post("✅ 替换成功", "Xbox 购买参数", "CartId 和 Authorization 均已更新");
} else if (status.cartId || status.auth) {
// 情况B部分成功 (比如只有Auth没有CartId)
let details = [];
if (status.cartId) details.push("CartId OK");
if (status.auth) details.push("Auth OK");
$notification.post("⚠️ 部分替换成功", "Xbox 购买参数", `仅完成: ${details.join(", ")} (请检查参数)`);
} else {
// 情况C完全失败
let reason = errorMsg.length > 0 ? errorMsg.join(" & ") : "未知原因";
$notification.post("❌ 替换失败", "Xbox 购买参数", reason);
}
// 8. 返回结果
if (newAuth) {
$done({ body: bodyStr, headers: headers });
} else {
$done({ body: bodyStr });
}
}

View File

@@ -0,0 +1,89 @@
/**
* Surge 脚本:捕获特定 URL 请求的参数
* 包含: x-authorization-muid, ms-cv, x-ms-vector-id, x-ms-reference-id
* 新增: x-ms-tracking-id, x-ms-correlation-id
* 并存储到持久化存储($persistentStore
* 只针对 PUT 请求
*/
// 修改正则表达式,捕获新的请求 URL
const pattern = /^https:\/\/cart\.production\.store-web\.dynamics\.com\/v1\.0\/cart\/loadCart\?/;
const url = $request.url;
// 判断是否是 PUT 请求并且 URL 匹配
if ($request.method === "PUT" && pattern.test(url)) {
try {
// 从请求头中获取所需参数
const xAuthorizationMuid = $request.headers['x-authorization-muid'];
const msCv = $request.headers['ms-cv'];
const xMsVectorId = $request.headers['x-ms-vector-id'];
const xMsReferenceId = $request.headers['x-ms-reference-id'];
// 新增参数获取
const xMsTrackingId = $request.headers['x-ms-tracking-id'];
const xMsCorrelationId = $request.headers['x-ms-correlation-id'];
// 更新存储逻辑(仅在新值有效时更新)
let hasUpdate = false;
// 1. x-authorization-muid
if (xAuthorizationMuid && xAuthorizationMuid !== $persistentStore.read("cart-x-authorization-muid")) {
$persistentStore.write(xAuthorizationMuid, "cart-x-authorization-muid");
console.log(`Stored x-authorization-muid: ${xAuthorizationMuid}`);
hasUpdate = true;
}
// 2. ms-cv
if (msCv && msCv !== $persistentStore.read("cart-ms-cv")) {
$persistentStore.write(msCv, "cart-ms-cv");
console.log(`Stored ms-cv: ${msCv}`);
hasUpdate = true;
}
// 3. x-ms-vector-id
if (xMsVectorId && xMsVectorId !== $persistentStore.read("cart-x-ms-vector-id")) {
$persistentStore.write(xMsVectorId, "cart-x-ms-vector-id");
console.log(`Stored x-ms-vector-id: ${xMsVectorId}`);
hasUpdate = true;
}
// 4. x-ms-reference-id
if (xMsReferenceId && xMsReferenceId !== $persistentStore.read("cart-x-ms-reference-id")) {
$persistentStore.write(xMsReferenceId, "cart-x-ms-reference-id");
console.log(`Stored x-ms-reference-id: ${xMsReferenceId}`);
hasUpdate = true;
}
// 5. [新增] x-ms-tracking-id
if (xMsTrackingId && xMsTrackingId !== $persistentStore.read("cart-x-ms-tracking-id")) {
$persistentStore.write(xMsTrackingId, "cart-x-ms-tracking-id");
console.log(`Stored x-ms-tracking-id: ${xMsTrackingId}`);
hasUpdate = true;
}
// 6. [新增] x-ms-correlation-id
if (xMsCorrelationId && xMsCorrelationId !== $persistentStore.read("cart-x-ms-correlation-id")) {
$persistentStore.write(xMsCorrelationId, "cart-x-ms-correlation-id");
console.log(`Stored x-ms-correlation-id: ${xMsCorrelationId}`);
hasUpdate = true;
}
// 仅在成功捕获新值时发送通知(如果需要开启通知,取消下方注释)
// if (hasUpdate) {
// $notification.post(
// "Surge 信息存储",
// "已捕获并存储所有加购请求参数",
// "包含 tracking-id 和 correlation-id"
// );
// }
} catch (error) {
console.log(`Error capturing parameters: ${error}`);
// $notification.post(
// "Surge 脚本错误",
// "无法捕获所需购物车参数",
// `${error}`
// );
}
}
$done({});

View File

@@ -0,0 +1,48 @@
/*
* Surge 脚本:清空 ApprovalCartId
* 功能1. 清空数据 2. 系统弹窗 3. 网页显示纯净结果
* 触发地址http://clear_list.com
*/
const key = "ApprovalCartId";
// 1. 执行清空
$persistentStore.write("", key);
// 2. 控制台日志
console.log("✅ 操作成功 - 已清空 ApprovalCartId");
// 3. 发送系统通知 (手机顶部依然会弹窗)
$notification.post("🗑️ 清单已清空", "操作成功", "ApprovalCartId 已重置为空对象");
// 4. 生成网页 HTML (去掉了提示文字)
const html = `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Clear List</title>
<style>
body { font-family: -apple-system, sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background-color: #f4f4f4; }
.card { background: white; padding: 30px; border-radius: 12px; box-shadow: 0 4px 10px rgba(0,0,0,0.1); text-align: center; }
h1 { color: #28a745; margin: 0 0 10px; }
p { color: #555; margin: 0; }
</style>
</head>
<body>
<div class="card">
<h1>✅ 操作成功</h1>
<p>ApprovalCartId 已被清空</p>
</div>
</body>
</html>`;
// 5. 返回给浏览器
$done({
response: {
status: 200,
headers: { "Content-Type": "text/html;charset=utf-8" },
body: html
}
});

View File

@@ -0,0 +1,48 @@
/*
* Surge 脚本:清空 XboxProductList
* 功能1. 清空数据 2. 系统弹窗 3. 网页显示纯净结果
* 触发地址http://clear_list.com
*/
const key = "XboxProductList";
// 1. 执行清空
$persistentStore.write("{}", key);
// 2. 控制台日志
console.log("✅ 操作成功 - 已清空 XboxProductList");
// 3. 发送系统通知 (手机顶部依然会弹窗)
$notification.post("🗑️ 清单已清空", "操作成功", "XboxProductList 已重置为空对象");
// 4. 生成网页 HTML (去掉了提示文字)
const html = `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Clear List</title>
<style>
body { font-family: -apple-system, sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background-color: #f4f4f4; }
.card { background: white; padding: 30px; border-radius: 12px; box-shadow: 0 4px 10px rgba(0,0,0,0.1); text-align: center; }
h1 { color: #28a745; margin: 0 0 10px; }
p { color: #555; margin: 0; }
</style>
</head>
<body>
<div class="card">
<h1>✅ 操作成功</h1>
<p>XboxProductList 已被清空</p>
</div>
</body>
</html>`;
// 5. 返回给浏览器
$done({
response: {
status: 200,
headers: { "Content-Type": "text/html;charset=utf-8" },
body: html
}
});

View File

@@ -0,0 +1,156 @@
const MARKET = "NG";
const LOCALE = "en-ng";
const FRIENDLY_NAME = `cart-${MARKET}`;
const CLIENT_CONTEXT = { client: "UniversalWebStore.Cart", deviceType: "Pc" };
const LIST_RAW = $persistentStore.read("XboxProductList") || "{}";
const MUID = $persistentStore.read("cart-x-authorization-muid");
const MS_CV = $persistentStore.read("cart-ms-cv");
const generateRiskSessionId = () =>
"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, c =>
(c === "x" ? (Math.random() * 16 | 0) : ((Math.random() * 4 | 8) | 0)).toString(16)
);
const toNum = k => {
const m = /^product(\d+)$/.exec(k);
return m ? parseInt(m[1], 10) : Number.MAX_SAFE_INTEGER;
};
const normEntry = v => {
if (!v || typeof v !== "object") return null;
const productId = String(v.ProductId ?? v.productId ?? "").trim();
const skuId = String(v.SkuId ?? v.skuId ?? "").trim();
const availabilityId = String(v.AvailabilityId ?? v.availabilityId ?? "").trim();
if (!productId || !skuId || !availabilityId) return null;
return { productId, skuId, availabilityId };
};
let parsed; try { parsed = JSON.parse(LIST_RAW); } catch { parsed = {}; }
const productList = Object.keys(parsed)
.filter(k => /^product\d+$/.test(k))
.sort((a,b) => toNum(a) - toNum(b))
.map(k => {
const norm = normEntry(parsed[k]);
return norm ? { key: k, ...norm } : null;
})
.filter(Boolean);
const results = { success: [], failure: [] };
const successKeys = [];
let currentIndex = 0;
const API_URL = "https://cart.production.store-web.dynamics.com/cart/v1.0/cart/loadCart?cartType=consumer&appId=StoreWeb";
const HEADERS = {
"content-type": "application/json",
"accept": "*/*",
"x-authorization-muid": MUID,
"sec-fetch-site": "cross-site",
"x-validation-field-1": "9pgbhbppjf2b",
"ms-cv": MS_CV,
"accept-language": "en-US,en;q=0.9",
"accept-encoding": "gzip, deflate, br",
"sec-fetch-mode": "cors",
"origin": "https://www.microsoft.com",
"user-agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) EdgiOS/133.0.3065.54 Version/18.0 Mobile/15E148 Safari/604.1",
"referer": "https://www.microsoft.com/",
"x-ms-vector-id": "",
"sec-fetch-dest": "empty"
};
function finalizeAndClean() {
const successCount = results.success.length;
const failureList = results.failure.join("\n");
let remainingCount = 0;
try {
let storeObj; try { storeObj = JSON.parse($persistentStore.read("XboxProductList") || "{}"); } 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), "XboxProductList");
} catch (e) {
console.log("清理存储异常:" + e);
}
const subtitle = `成功: ${successCount} 个 / 共 ${productList.length}`;
const body = (failureList || "所有请求均成功") + `\n剩余待处理 product${remainingCount}`;
$notification.post("🎮 操作完成", subtitle, body);
$done();
}
function sendRequest() {
if (currentIndex >= productList.length) return finalizeAndClean();
const { key, productId, skuId, availabilityId } = productList[currentIndex];
const riskSessionId = generateRiskSessionId();
const idTriple = `${productId}/${skuId}/${availabilityId}`;
const timeoutId = setTimeout(() => {
recordResult(idTriple, "超时");
proceed();
}, 30000);
const bodyObj = {
locale: LOCALE,
market: MARKET,
catalogClientType: "storeWeb",
friendlyName: FRIENDLY_NAME,
riskSessionId,
clientContext: CLIENT_CONTEXT,
itemsToAdd: {
items: [
{
productId,
skuId,
availabilityId,
campaignId: "xboxcomct",
quantity: 1
}
]
}
};
$httpClient.put(
{ url: API_URL, headers: HEADERS, body: JSON.stringify(bodyObj) },
(error, response) => {
clearTimeout(timeoutId);
if (error || response.status !== 200) {
recordResult(idTriple, `HTTP ${response ? response.status : "Error"}`);
} else {
handleSuccess(idTriple, key);
}
proceed();
}
);
}
function handleSuccess(id, key) {
results.success.push(id);
if (key) successKeys.push(key);
console.log(`${id} (${key})`);
}
function recordResult(id, reason) {
results.failure.push(`${id}: ${reason}`);
console.log(`${id} - ${reason}`);
}
function proceed() {
currentIndex++;
setTimeout(sendRequest, 10);
}
if (!MUID || !MS_CV) {
console.log("缺少必要参数 MUID 或 MS_CV");
$notification.post("配置错误", "缺少必要参数 MUID 或 MS_CV", "");
$done();
} else if (productList.length === 0) {
console.log("XboxProductList 为空或无有效条目");
$notification.post("无任务", "XboxProductList 为空或无有效条目", "");
$done();
} else {
console.log(`开始执行请求(共 ${productList.length} 个)`);
sendRequest();
}

View File

@@ -0,0 +1,102 @@
/**
* Xbox Cart Web Runner
* 远程路径: https://raw.githubusercontent.com/XXhaos/Surge/refs/heads/main/NewAddToCart_Web.js
*/
const MARKET = "NG";
const LOCALE = "en-ng";
const FRIENDLY_NAME = `cart-${MARKET}`;
const CLIENT_CONTEXT = { client: "UniversalWebStore.Cart", deviceType: "Pc" };
// 读取配置
const LIST_RAW = $persistentStore.read("XboxProductList") || "{}";
const MUID = $persistentStore.read("cart-x-authorization-muid");
const MS_CV = $persistentStore.read("cart-ms-cv");
// 日志缓存
let logBuffer = [];
const results = { success: [], failure: [] };
const successKeys = [];
let currentIndex = 0;
function log(type, message, detail = "") {
const icon = type === "success" ? "✅" : (type === "error" ? "❌" : "");
const color = type === "success" ? "green" : (type === "error" ? "red" : "#666");
console.log(`${icon} ${message} ${detail}`);
logBuffer.push(`<div style="color:${color}; border-bottom:1px solid #eee; padding:5px;">${icon} ${message} <small>${detail}</small></div>`);
}
const generateRiskSessionId = () => "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, c => (c === "x" ? (Math.random() * 16 | 0) : ((Math.random() * 4 | 8) | 0)).toString(16));
const toNum = k => { const m = /^product(\d+)$/.exec(k); return m ? parseInt(m[1], 10) : Number.MAX_SAFE_INTEGER; };
const normEntry = v => {
if (!v || typeof v !== "object") return null;
const productId = String(v.ProductId ?? v.productId ?? "").trim();
const skuId = String(v.SkuId ?? v.skuId ?? "").trim();
const availabilityId = String(v.AvailabilityId ?? v.availabilityId ?? "").trim();
if (!productId || !skuId || !availabilityId) return null;
return { productId, skuId, availabilityId };
};
let parsed; try { parsed = JSON.parse(LIST_RAW); } catch { parsed = {}; }
const productList = Object.keys(parsed).filter(k => /^product\d+$/.test(k)).sort((a,b) => toNum(a) - toNum(b)).map(k => { const norm = normEntry(parsed[k]); return norm ? { key: k, ...norm } : null; }).filter(Boolean);
const API_URL = "https://cart.production.store-web.dynamics.com/cart/v1.0/cart/loadCart?cartType=consumer&appId=StoreWeb";
const HEADERS = { "content-type": "application/json", "accept": "*/*", "x-authorization-muid": MUID, "ms-cv": MS_CV, "origin": "https://www.microsoft.com", "user-agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1" };
function finalizeAndClean() {
const successCount = results.success.length;
const failureCount = results.failure.length;
let remainingCount = 0;
try {
let storeObj; try { storeObj = JSON.parse($persistentStore.read("XboxProductList") || "{}"); } 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), "XboxProductList");
log("info", "清理完成", `剩余: ${remainingCount}`);
} catch (e) { log("error", "清理异常", e); }
// 【新增】发送系统通知
$notification.post(
"🛒 Xbox 加购完成",
`成功: ${successCount} / 失败: ${failureCount}`,
`剩余库存: ${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} / 剩余 ${remainingCount}</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 sendRequest() {
if (currentIndex >= productList.length) return finalizeAndClean();
const { key, productId, skuId, availabilityId } = productList[currentIndex];
const bodyObj = { locale: LOCALE, market: MARKET, catalogClientType: "storeWeb", friendlyName: FRIENDLY_NAME, riskSessionId: generateRiskSessionId(), clientContext: CLIENT_CONTEXT, itemsToAdd: { items: [{ productId, skuId, availabilityId, campaignId: "xboxcomct", quantity: 1 }] } };
$httpClient.put({ url: API_URL, headers: HEADERS, body: JSON.stringify(bodyObj) }, (error, response) => {
const idStr = `${productId}`;
if (error || response.status !== 200) {
results.failure.push(idStr); log("error", "失败", idStr);
} else {
results.success.push(idStr); if (key) successKeys.push(key); log("success", "成功", idStr);
}
currentIndex++; setTimeout(sendRequest, 50);
});
}
if (!MUID || !MS_CV) {
log("error", "缺少 MUID/CV");
// 即使配置错误也发个通知提醒你
$notification.post("❌ Xbox 脚本错误", "缺少必要参数", "请检查 MUID 或 MS_CV");
finalizeAndClean();
}
else if (productList.length === 0) {
log("info", "列表为空");
$notification.post("⚠️ Xbox 脚本", "无需执行", "列表为空");
finalizeAndClean();
}
else {
log("info", "开始任务", `数量: ${productList.length}`);
sendRequest();
}

View File

@@ -0,0 +1,171 @@
/**
* SyncXboxCloud.js
* 两步流程:先读取,写入成功后再删除
* 顶部时间戳锁防止 Surge 规则重复触发脚本
*/
const readUrl = 'https://cc.dragonisheep.com/surge?token=xbox123';
const clearUrl = 'https://cc.dragonisheep.com/surge?token=xbox123&action=clear';
const storeKey = 'XboxProductList';
const lockKey = 'SyncXboxLock';
// ★ 防重入5 秒内若已执行过,直接放行不处理
// Surge 规则会同时拦截 read 和 clear 两个请求,第二次触发在此被阻断
const lockVal = $persistentStore.read(lockKey);
if (lockVal && Date.now() - parseInt(lockVal, 10) < 5000) {
$done({});
}
$persistentStore.write(String(Date.now()), lockKey);
function escapeHTML(str) {
return String(str || '')
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
function renderUI(title, message, type = "success") {
const colorMap = {
success: "#107C10",
warning: "#d83b01",
error: "#c50f1f",
info: "#0078d4"
};
const color = colorMap[type] || "#0078d4";
const html = `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>${escapeHTML(title)}</title>
<style>
body {
margin: 0; min-height: 100vh; display: flex; align-items: center;
justify-content: center; background: #0f1115; color: #fff;
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "PingFang SC", sans-serif;
padding: 20px;
}
.card {
width: 100%; max-width: 560px; background: #171a21; border-radius: 16px;
border: 1px solid #252a35; border-top: 5px solid ${color};
padding: 24px 22px 20px; box-sizing: border-box;
}
h1 { margin: 0 0 14px; font-size: 22px; color: ${color}; }
.msg { color: #cfd6e4; font-size: 14px; line-height: 1.7; word-break: break-word; }
.msg p { margin: 0 0 10px; }
.msg ul { margin: 10px 0 0; padding-left: 20px; text-align: left; }
.msg li { margin: 6px 0; }
.sub { color: #8e99ab; font-size: 12px; margin-top: 14px; }
button {
margin-top: 18px; border: none; background: ${color};
color: white; padding: 10px 22px; border-radius: 999px; font-size: 14px;
}
</style>
</head>
<body>
<div class="card">
<h1>${escapeHTML(title)}</h1>
<div class="msg">${message}</div>
<div class="sub">SyncXbox Cloud Queue</div>
<button onclick="history.back()">确定</button>
</div>
</body>
</html>`;
$done({
response: {
status: 200,
headers: {
"Content-Type": "text/html; charset=utf-8",
"Cache-Control": "no-store, no-cache, must-revalidate",
"Pragma": "no-cache"
},
body: html
}
});
}
// 第一步:读取当前组
$httpClient.get(readUrl, (error, response, data) => {
if (error) {
$notification.post("❌ 同步失败", "无法连接服务器", String(error));
return renderUI("❌ 连接失败", `<p>无法连接至服务器。</p><p>${escapeHTML(String(error))}</p>`, "error");
}
let payload;
try {
payload = JSON.parse((data || '').trim() || '{}');
} catch (e) {
$notification.post("❌ 同步失败", "返回 JSON 无法解析", "");
return renderUI("❌ 解析错误", `<p>服务器返回内容不是合法 JSON。</p><p>${escapeHTML(String(e.message || e))}</p>`, "error");
}
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
$notification.post("❌ 同步失败", "返回结构异常", "");
return renderUI("❌ 数据异常", `<p>服务器返回的数据结构不正确。</p>`, "error");
}
if (!payload.ok) {
$notification.post("❌ 同步失败", "服务器返回 ok=false", "");
return renderUI("❌ 同步失败", `<p>服务器返回失败状态。</p>`, "error");
}
const currentGroup = payload.currentGroup || {};
const keys = Object.keys(currentGroup);
if (!keys.length) {
$notification.post("📭 当前无待同步组", "云端队列为空", "");
return renderUI("📭 没有可同步内容", `<p>当前没有待同步的 Product 分组。</p>`, "warning");
}
// 第二步:写入本地,成功后才删云端
const writeOK = $persistentStore.write(JSON.stringify(currentGroup), storeKey);
if (!writeOK) {
$notification.post("❌ 同步失败", "写入 Surge 存储失败", "");
// 写入失败,不发 clear云端数据保留下次可以重试
return renderUI("❌ 写入失败", `<p>无法写入 Surge 本地存储,云端数据未删除,下次访问可重试。</p>`, "error");
}
// 第三步:本地写入成功,删除云端当前组
$httpClient.get(clearUrl, (clearError, clearResponse, clearData) => {
if (clearError) {
$notification.post("⚠️ 本地已写入", `${payload.currentGroupIndex} 组已保存`, "但云端清理失败,下次访问仍是这一组");
return renderUI(
"⚠️ 当前组已写入,但云端未清理",
`<p>已成功写入第 <b>${escapeHTML(String(payload.currentGroupIndex))}</b> 组,共 <b>${keys.length}</b> 个商品。</p>
<p>但清理云端当前组时失败,下次访问时仍会是这一组。</p>`,
"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 => `<li>${escapeHTML(k)}</li>`).join('');
let message =
`<p>本次只同步了第 <b>${escapeHTML(String(payload.currentGroupIndex))}</b> 组,共 <b>${keys.length}</b> 个商品。</p>
<p>当前这一组已经从云端队列删除。</p>
<p>现在还剩 <b>${remainingGroups}</b> 组待处理。</p>`;
if (remainingGroups > 0 && nextGroupIndex !== null) {
message += `<p>下次访问网页时,将同步第 <b>${escapeHTML(String(nextGroupIndex))}</b> 组(共 <b>${escapeHTML(String(nextGroupCount))}</b> 个商品)。</p>`;
} else {
message += `<p>所有分组已经处理完毕。</p>`;
}
message += `<ul>${list}</ul>`;
$notification.post("✅ 当前组同步成功", `${payload.currentGroupIndex} 组已同步`, `剩余 ${remainingGroups}`);
return renderUI("✅ 当前组同步完成", message, "success");
});
});

View File

@@ -0,0 +1,113 @@
// Surge: script-response-body
// 递归提取 GET 响应中 actionType == "Cart" 的 {ProductId,SkuId,AvailabilityId}
// 以 product1/product2… 顺序写入 XboxProductListProductId重复则跳过
// 通知:🆕 新增 / 🔁 已存在(不含 URL副标题仅显示「当前共有X个商品📦」
(function () {
const STORE_KEY = 'XboxProductList';
try {
if ($request?.method?.toUpperCase() !== 'GET') return $done({});
if (!$response?.body || typeof $response.body !== 'string') return $done({ body: $response?.body });
// 解析 JSON失败则尝试从文本抠出 JSON 片段
let data;
try {
data = JSON.parse($response.body);
} catch {
const m = $response.body.match(/(\{[\s\S]*\}|\[[\s\S]*\])/);
if (m) { try { data = JSON.parse(m[1]); } catch {} }
if (!data) return $done({ body: $response.body });
}
// 读取已有 store
let store = {};
try {
const raw = $persistentStore.read(STORE_KEY);
if (raw) store = JSON.parse(raw) || {};
} catch { store = {}; }
// 现有条目(仅 productN按数字排序
const entries = Object.keys(store)
.filter(k => /^product\d+$/.test(k))
.sort((a, b) => parseInt(a.slice(7)) - parseInt(b.slice(7)))
.map(k => store[k]);
// 【修改点1】简化判定逻辑只要 ProductId 一致,即判定为相同商品
const same = (a, b) =>
a && b &&
String(a.ProductId||'') === String(b.ProductId||'');
// 递归收集 Cart去重当前响应内
const found = [];
const seen = new Set();
const isObj = v => v && typeof v === 'object';
const visit = (node) => {
if (!isObj(node)) return;
if (typeof node.actionType === 'string' && node.actionType.toLowerCase() === 'cart') {
const args = node.actionArguments;
if (isObj(args)) {
const r = {
ProductId: String(args.ProductId ?? '').trim(),
SkuId: String(args.SkuId ?? '').trim(),
AvailabilityId: String(args.AvailabilityId ?? '').trim()
};
if (r.ProductId && r.SkuId && r.AvailabilityId) {
// 【修改点2】当前响应内部的去重 Set 也只记录 ProductId
const key = r.ProductId;
if (!seen.has(key)) { seen.add(key); found.push(r); }
}
}
}
if (Array.isArray(node)) node.forEach(visit);
else Object.keys(node).forEach(k => visit(node[k]));
};
visit(data);
if (found.length === 0) return $done({ body: $response.body });
// 拆分新增/已存在
const toAdd = [];
const existed = [];
for (const r of found) (entries.some(e => same(e, r)) ? existed : toAdd).push(r);
let title = '';
let detail = null;
if (toAdd.length > 0) {
// 追加为下一个 productN
let maxIndex = 0;
const keys = Object.keys(store).filter(k => /^product\d+$/.test(k));
if (keys.length) maxIndex = Math.max(...keys.map(k => parseInt(k.slice(7), 10) || 0));
for (const r of toAdd) {
const key = `product${++maxIndex}`;
store[key] = { ProductId: r.ProductId, SkuId: r.SkuId, AvailabilityId: r.AvailabilityId };
entries.push(store[key]);
}
$persistentStore.write(JSON.stringify(store), STORE_KEY);
title = '🆕 XboxProductList 已新增';
detail = toAdd[0];
} else {
title = '🔁 XboxProductList 已存在,跳过';
detail = existed[0];
}
// 统计当前商品总数(仅 productN 键)
const productCount = Object.keys(store).filter(k => /^product\d+$/.test(k)).length;
if (detail) {
$notification.post(
title,
`当前共有 ${productCount} 个商品📦`,
`信息如下:${JSON.stringify(detail)}`
);
}
return $done({ body: $response.body });
} catch (err) {
$notification.post('❌ Xbox 抓取脚本异常', '', String(err));
return $done({ body: $response?.body });
}
})();

View File

@@ -0,0 +1,44 @@
/**
* Surge 脚本:捕获特定 URL 请求的 Authorization 和 cartId仅限 PUT 请求)
* 并存储到持久化存储($persistentStore只有在成功捕获到新值时才更新
*/
const pattern = /^https:\/\/cart\.production\.store-web\.dynamics\.com\/v1\.0\/Cart\/eligibilityCheck\?/;
const url = $request.url;
// 仅处理 PUT 请求
if ($request.method === "PUT" && pattern.test(url)) {
try {
// 获取 Authorization 请求头的值
const authorization = $request.headers['authorization'];
// 解析 URL 以提取 cartId 参数
const urlObj = new URL(url);
const cartId = urlObj.searchParams.get('cartId');
// 只有在成功捕获到新值时,才更新 $persistentStore 中的值
if (authorization && authorization !== $persistentStore.read("authorization")) {
$persistentStore.write(authorization, "authorization");
console.log(`Stored authorization: ${authorization}`);
}
if (cartId && cartId !== $persistentStore.read("cartId")) {
$persistentStore.write(cartId, "cartId");
console.log(`Stored cartId: ${cartId}`);
}
// 发送通知,成功捕获并存储 Authorization 和 CartId
$notification.post(
"Surge 信息存储",
"已捕获并存储 authorization 和 cartId"
);
} catch (error) {
console.log(`Error capturing authorization & cartId: ${error}`);
$notification.post(
"Surge 脚本错误",
"无法捕获 authorization 和 cartId",
`${error}`
);
}
}
$done({});

View File

@@ -0,0 +1,141 @@
/**
* Surge 脚本:微软家庭组批量购买 (防风控增强版)
* * 特性 1: 随机抖动延迟 (1.5s ~ 3.5s 之间随机),模拟真人。
* * 特性 2: 智能熔断,遇到 429/403 立即停止,保护账号。
*/
const STORE_KEY = "ApprovalCartId";
const BASE_DELAY = 1500; // 基础间隔
const JITTER_MAX = 2000; // 最大随机附加间隔 (0~2000ms)
(async () => {
// 1. 严格限制仅允许 POST
if ($request.method !== "POST") {
$done({});
return;
}
// 2. 读取 Store
const rawIds = $persistentStore.read(STORE_KEY);
if (!rawIds) {
console.log(" [旁路] 未读取到 ApprovalCartId放行。");
$done({});
return;
}
const targetIds = rawIds.split("&").filter(Boolean);
if (targetIds.length === 0) {
$done({});
return;
}
// 3. 启动通知
$notification.post(
"🛡️ 防风控批量启动",
`队列: ${targetIds.length} 个请求`,
`启用随机延迟与熔断保护机制...`
);
let originalBodyTemplate;
try {
originalBodyTemplate = JSON.parse($request.body);
} catch (e) {
$done({});
return;
}
const baseHeaders = { ...$request.headers };
delete baseHeaders["Content-Length"];
delete baseHeaders["content-length"];
let successCount = 0;
let failCount = 0;
let isBanned = false; // 标记是否被风控
// ==========================================
// 串行循环
// ==========================================
for (let i = 0; i < targetIds.length; i++) {
const id = targetIds[i];
// 构造请求
let currentBody = JSON.parse(JSON.stringify(originalBodyTemplate));
currentBody.cartId = id;
const options = {
url: $request.url,
method: "POST",
headers: baseHeaders,
body: JSON.stringify(currentBody)
};
console.log(`🔄 [${i + 1}/${targetIds.length}] 处理 ${id.substring(0,6)}...`);
const result = await sendRequest(options);
// --- 结果判定与熔断逻辑 ---
if (result && result.status >= 200 && result.status < 300) {
console.log(` ✅ 成功`);
successCount++;
} else if (result.status === 429 || result.status === 403) {
// 429: Too Many Requests (请求太快)
// 403: Forbidden (可能鉴权失败或被封禁)
console.log(` ⛔️ 触发风控 (Code: ${result.status})! 立即停止后续任务!`);
$notification.post("⛔️ 任务熔断停止", `检测到微软风控 (${result.status})`, "已停止后续请求以保护账号");
isBanned = true;
failCount++;
break; // 👈 核心:立即跳出循环,不再发包
} else {
console.log(` ❌ 失败 (Code: ${result ? result.status : 'unknown'})`);
failCount++;
}
// --- 随机抖动延迟 ---
// 只有不是最后一个且没有被Ban才等待
if (i < targetIds.length - 1 && !isBanned) {
// 生成 1500ms 到 3500ms 之间的随机时间
const randomTime = BASE_DELAY + Math.floor(Math.random() * JITTER_MAX);
console.log(` ⏳ 随机等待 ${(randomTime/1000).toFixed(2)}s...`);
await sleep(randomTime);
}
}
// 4. 清空 Store
$persistentStore.write(null, STORE_KEY);
// 5. 最终处理
if (!isBanned) {
const msg = `成功 ${successCount} | 失败 ${failCount}`;
$notification.post("✅ 批量完成", msg, "原始请求已拦截,请刷新页面");
}
// 拦截并返回伪造成功
$done({
response: {
status: 200,
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
status: "Complete",
message: "Processed by Surge (Anti-Ban Mode)"
})
}
});
})();
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function sendRequest(opts) {
return new Promise((resolve) => {
$httpClient.post(opts, (err, resp, data) => {
if (err) {
console.log(`❌ 网络错误: ${err}`);
resolve({ status: 0, error: err });
} else {
resolve({ status: resp.status, body: data });
}
});
});
}

View File

@@ -0,0 +1,58 @@
/**
* 脚本名称: Microsoft Family ProductId Block
* 功能: 拦截 Microsoft Family 中指定的 ProductId 购买请求
*/
// 定义需要拦截的目标 ID 列表
const targetIds = [
"9PNTSH5SKCL5", "9nfmccp0pm67", "9npbvj8lwsvn", "9pcgszz8zpq2",
"9P54FF0VQD7R", "9NCJZN3LBD3P", "9P9CLTVLLHD6", "9NHXDFLDBN6G"
];
function main() {
// 1. 检查是否为 POST 请求 (对应 Fiddler 的 oSession.HTTPMethodIs("POST"))
if ($request.method !== "POST") {
$done({});
return;
}
// 2. 获取请求体 (对应 oSession.GetRequestBodyAsString())
// 注意:需要在模块配置中开启 requires-body=1
const body = $request.body;
if (!body) {
$done({});
return;
}
// 3. 检查是否包含目标 ID (对应 IndexOf + StringComparison.OrdinalIgnoreCase)
// 为了实现忽略大小写匹配,我们将 body 和 targetId 都转换为大写进行比较
const upperBody = body.toUpperCase();
let isMatch = false;
for (let id of targetIds) {
if (upperBody.includes(id.toUpperCase())) {
isMatch = true;
break;
}
}
// 4. 如果匹配,则拦截 (对应 oSession.oRequest.FailSession(403...))
if (isMatch) {
console.log(`[MS-Block] 检测到拦截目标,已阻断请求。`);
$done({
response: {
status: 403,
headers: {
"Content-Type": "text/plain; charset=utf-8"
},
body: "Blocked by Surge script (Microsoft Family ProductId Rule)"
}
});
} else {
// 未匹配,放行
$done({});
}
}
main();