]*>\s*\/\s*([\d,]+)\s*<\/span>/i)
|| searchRowMatch[1].match(/([\d,]+)\s*\/\s*([\d,]+)/))
: null;
if (searchHtmlMatch) {
pcCur = toNum(searchHtmlMatch[1]);
pcMax = toNum(searchHtmlMatch[2]);
searchQuotaFound = pcMax > 0;
todayDetails.push({ title: '必应搜索', points: pcCur, max: pcMax });
Utils.log("🔍", `页面表格配额: PC ${pcCur}/${pcMax}`);
}
const searchRscMatch = clean.match(/"combinedSearch":\{[^}]*"progress":(\d+)[^}]*"max":(\d+)/);
if (searchRscMatch && !todayDetails.some(d => d.title === '必应搜索')) {
pcCur = toNum(searchRscMatch[1]);
pcMax = toNum(searchRscMatch[2]);
searchQuotaFound = pcMax > 0;
todayDetails.push({ title: '必应搜索', points: pcCur, max: pcMax });
}
if (dailyOffer > 0) todayDetails.push({ title: '优惠', points: dailyOffer });
const otherActivityRegex = /([^<]+)<\/p><\/div>
]*>(\d+)<\/div>/g;
let otherMatch;
while ((otherMatch = otherActivityRegex.exec(clean)) !== null) {
const title = otherMatch[1];
const points = parseInt(otherMatch[2]);
if (points > 0 && !todayDetails.some(d => d.title === title)) {
todayDetails.push({ title, points });
}
}
const history = { month: 0, year: 0, lifetime: 0 };
const historyRscMatch = clean.match(/"pointsHistory":\{[^}]*"thisMonth":\{"earn":(\d+)[^}]*"thisYear":\{"earn":(\d+)[^}]*"lifetime":\{"earn":(\d+)/);
if (historyRscMatch) {
history.month = parseInt(historyRscMatch[1]);
history.year = parseInt(historyRscMatch[2]);
history.lifetime = parseInt(historyRscMatch[3]);
} else {
const monthHtmlMatch = clean.match(/本月.*?(\d[\d,]*)<\/div>/);
const yearHtmlMatch = clean.match(/今年.*?(\d[\d,]*)<\/div>/);
const lifetimeHtmlMatch = clean.match(/生存期.*?(\d[\d,]*)<\/div>/);
const monthJsonMatch = clean.match(/"monthlyPoints":(\d+)/);
const yearJsonMatch = clean.match(/"yearlyPoints":(\d+)/);
const lifetimeJsonMatch = clean.match(/"lifetimePoints":(\d+)/);
if (monthHtmlMatch) history.month = parseInt(monthHtmlMatch[1].replace(/,/g, ''));
else if (monthJsonMatch) history.month = parseInt(monthJsonMatch[1]);
if (yearHtmlMatch) history.year = parseInt(yearHtmlMatch[1].replace(/,/g, ''));
else if (yearJsonMatch) history.year = parseInt(yearJsonMatch[1]);
if (lifetimeHtmlMatch) history.lifetime = parseInt(lifetimeHtmlMatch[1].replace(/,/g, ''));
else if (lifetimeJsonMatch) history.lifetime = parseInt(lifetimeJsonMatch[1]);
}
if (!searchQuotaFound) {
const userInfoResult = await this.getSearchQuotaFromUserInfo();
if (userInfoResult) {
Utils.log("🟢", "页面未命中搜索配额,使用 getuserinfo 兜底");
return userInfoResult;
}
}
if (!searchQuotaFound && RewardsAuto.state.token) {
const apiResult = await this.getSearchQuotaFromAPI();
if (apiResult) {
Utils.log("🟢", "页面未命中搜索配额,使用 DAPI 兜底");
return apiResult;
}
}
return { balance, pc: { progress: pcCur, max: pcMax }, mobile: { progress: mobCur, max: mobMax }, dailyOffer, todayDetails, history };
} catch (e) {
if (attempt < maxRetries) { await Utils.delay(3210); continue; }
Utils.log("🔴", `仪表盘获取失败: ${e.message}`);
return false;
}
}
return false;
},
async signApp() {
const region = GM_getValue("Config.lock", true) ? "cn" : RewardsAuto.state.region.toLowerCase();
try {
const res = await this.withTokenRetry(token => Utils.xhr({
method: "POST",
url: "https://prod.rewardsplatform.microsoft.com/dapi/me/activities",
headers: {
"content-type": "application/json; charset=UTF-8",
"user-agent": RewardsAuto.ua.app,
"authorization": `Bearer ${token}`,
"x-rewards-appid": RewardsAuto.appConfig.rewardsAppId,
"x-rewards-ismobile": "true",
"x-rewards-country": region,
"x-rewards-language": "zh",
"x-rewards-partnerid": "startapp",
"x-rewards-flights": "rwgobig"
},
data: JSON.stringify({
amount: 1, id: Utils.getRandomUUID().replace(/-/g, '') + Utils.getRandomUUID().replace(/-/g, '').slice(0, 24),
type: 103, country: region, channel: RewardsAuto.appConfig.channel
})
}));
if (Utils.isJSON(res)) {
const data = JSON.parse(res);
const response = data.response || {};
if (response.activity) return Number(response.activity.p || response.activity.points || 0);
if (response.isDuplicate || response.activity === null) return 0;
Utils.log("🟡", `App签入响应未确认: ${String(res).slice(0, 120)}`);
}
} catch (e) {
Utils.log("🔴", `App签入失败: ${e.message}`);
}
return -1;
},
async getRequestVerificationToken(pageUrl = "https://rewards.bing.com/") {
try {
const html = await Utils.xhr({
url: pageUrl,
headers: { "user-agent": RewardsAuto.ua.pc, "referer": "https://rewards.bing.com/" },
anonymous: false
});
const tokenMatch = html.match(/name=["']__RequestVerificationToken["'][^>]*value=["']([^"']+)["']/i)
|| html.match(/RequestVerificationToken.*?value=["']([^"']+)["']/i)
|| html.match(/"verificationToken"\s*:\s*"([^"]+)"/i)
|| html.match(/"__RequestVerificationToken"\s*:\s*"([^"]+)"/i);
return tokenMatch ? tokenMatch[1].replace(/&/g, "&") : "";
} catch (e) {
Utils.log("🟡", `活动Token获取失败: ${e.message}`);
return "";
}
},
async reportActivity(offerId, hash, referer = "https://rewards.bing.com/") {
const token = await this.getRequestVerificationToken(referer);
const params = new URLSearchParams({ id: offerId, hash: hash || "1", activityAmount: "1" });
if (token) params.set("__RequestVerificationToken", token);
const headers = {
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
"user-agent": RewardsAuto.ua.pc,
"referer": referer,
"origin": "https://rewards.bing.com",
"x-requested-with": "XMLHttpRequest"
};
if (token) headers["RequestVerificationToken"] = token;
return await Utils.xhr({
method: "POST",
url: "https://rewards.bing.com/api/reportactivity?X-Requested-With=XMLHttpRequest",
headers, data: params.toString(), anonymous: false
});
},
async reportDailyActivity(searchUrl) {
try {
const fullUrl = searchUrl.startsWith("http") ? searchUrl : `https://cn.bing.com${searchUrl}`;
const urlObj = new URL(fullUrl);
const ig = Utils.getRandomUUID().replace(/-/g, '').substring(0, 32).toUpperCase();
const cnUrl = fullUrl.replace(/^https?:\/\/www\.bing\.com/, "https://cn.bing.com")
.replace(/^https:\/\/bing\.com/, "https://cn.bing.com");
const cnUrlObj = new URL(cnUrl);
const cnSp = cnUrlObj.searchParams;
const reportParams = new URLSearchParams();
reportParams.set("IG", ig);
reportParams.set("IID", "commerce.5067");
if (cnSp.get("form")) reportParams.set("form", cnSp.get("form"));
if (cnSp.get("ocid") || cnSp.get("OCID")) reportParams.set("ocid", cnSp.get("ocid") || cnSp.get("OCID"));
if (cnSp.get("rnoreward")) reportParams.set("rnoreward", cnSp.get("rnoreward"));
try {
await Utils.xhr({
method: "GET", url: cnUrl,
headers: {
"user-agent": RewardsAuto.ua.pc,
"referer": "https://rewards.bing.com/",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"accept-language": "zh-CN,zh;q=0.9,en;q=0.8"
}
});
} catch (_) {}
const ncheaderParams = new URLSearchParams();
ncheaderParams.set("ver", String(Date.now()).substring(0, 8));
ncheaderParams.set("IID", "commerce.5057");
ncheaderParams.set("IG", ig);
try {
await Utils.xhr({
method: "POST",
url: `https://cn.bing.com/rewardsapp/ncheader?${ncheaderParams.toString()}`,
headers: {
"content-type": "application/x-www-form-urlencoded",
"user-agent": RewardsAuto.ua.pc,
"referer": cnUrl,
"origin": "https://cn.bing.com",
"accept": "*/*",
"accept-language": "zh-CN,zh;q=0.9,en;q=0.8"
},
data: "wb=1;i=1;v=1"
});
} catch (_) {}
const bodyParams = new URLSearchParams();
bodyParams.set("url", cnUrl);
bodyParams.set("V", "web");
await Utils.xhr({
method: "POST",
url: `https://cn.bing.com/rewardsapp/reportActivity?${reportParams.toString()}`,
headers: {
"content-type": "application/x-www-form-urlencoded",
"user-agent": RewardsAuto.ua.pc,
"referer": cnUrl,
"origin": "https://cn.bing.com",
"accept": "*/*",
"accept-language": "zh-CN,zh;q=0.9,en;q=0.8"
},
data: bodyParams.toString()
});
return true;
} catch (e) {
Utils.log("🟡", `每日活动上报失败: ${e.message}`);
return false;
}
},
async signPC() {
try {
const res = await this.reportActivity("Gamification_DailyCheckIn", "1", "https://rewards.bing.com/");
if (Utils.isJSON(res)) {
const data = JSON.parse(res);
return Number(data.points || data.response?.activity?.p || 0);
}
} catch (e) {
if (e.message?.includes("401")) RewardsAuto.state.pc401 = true;
Utils.log("🟡", `PC签入失败: ${e.message}`);
}
return -1;
},
async appActivity(type, offerid) {
const region = GM_getValue("Config.lock", true) ? "cn" : RewardsAuto.state.region.toLowerCase();
const body = {
amount: 1, country: region,
id: Utils.getRandomUUID().replace(/-/g, '') + Utils.getRandomUUID().replace(/-/g, '').slice(0, 24),
type: type, channel: RewardsAuto.appConfig.channel
};
if (offerid) body.attributes = { offerid: offerid };
try {
const res = await this.withTokenRetry(token => Utils.xhr({
method: "POST",
url: "https://prod.rewardsplatform.microsoft.com/dapi/me/activities",
headers: {
"content-type": "application/json; charset=utf-8",
"user-agent": RewardsAuto.ua.app,
"authorization": `Bearer ${token}`,
"x-rewards-appid": RewardsAuto.appConfig.rewardsAppId,
"x-rewards-ismobile": "true",
"x-rewards-country": region,
"x-rewards-language": "zh"
},
data: JSON.stringify(body)
}));
if (Utils.isJSON(res)) {
const data = JSON.parse(res);
return {
points: data.response?.activity?.p || 0,
isDuplicate: data.response?.isDuplicate || false,
balance: data.response?.balance || 0
};
}
} catch (e) {
Utils.log("🔴", `App活动失败(${offerid}): ${e.message}`);
}
return null;
},
async getReadProgress() {
try {
const res = await this.withTokenRetry(token => Utils.xhr({
url: "https://prod.rewardsplatform.microsoft.com/dapi/me?channel=SAAndroid&options=613",
headers: {
"content-type": "application/json; charset=UTF-8",
"user-agent": RewardsAuto.ua.app,
"authorization": `Bearer ${token}`,
"x-rewards-appid": RewardsAuto.appConfig.rewardsAppId,
"x-rewards-ismobile": "true",
"x-rewards-country": "cn",
"x-rewards-language": "zh"
}
}));
if (Utils.isJSON(res)) {
const promos = JSON.parse(res).response?.promotions || [];
const readOfferId = RewardsAuto.appConfig.offerIds.readArticle;
const task = promos.find(x => x.attributes?.offerid === readOfferId);
if (task && task.attributes) {
const progress = parseInt(task.attributes.progress) || 0;
const max = parseInt(task.attributes.max) || 30;
Utils.log("📊", `阅读进度查询: ${progress}/${max} (offerid: ${readOfferId})`);
return { progress, max };
} else {
Utils.log("🟡", `阅读任务未找到 (offerid: ${readOfferId})`);
}
} else {
Utils.log("🟡", `DAPI 响应不是 JSON: ${String(res).substring(0, 100)}`);
}
} catch (e) {
Utils.log("🔴", `阅读进度获取失败: ${e.message}`);
}
return false;
},
async getRewardsToken() {
try {
const html = await Utils.xhr({
url: "https://rewards.bing.com/",
headers: { "user-agent": RewardsAuto.ua.pc, "referer": "https://rewards.bing.com/" }
});
if (html) {
const match = html.replace(/\s/g, "").match(/RequestVerificationToken(.*?)value="(.*?)"/);
if (match) return match[2];
}
} catch (e) {
Utils.log("🟡", `RequestVerificationToken 获取失败: ${e.message}`);
}
return false;
},
async getSearchQuotaFromUserInfo() {
try {
const res = await Utils.xhr({
url: `https://rewards.bing.com/api/getuserinfo?type=1&X-Requested-With=XMLHttpRequest&_=${Date.now()}`,
headers: {
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
"user-agent": RewardsAuto.ua.pc,
"referer": "https://rewards.bing.com/",
"x-requested-with": "XMLHttpRequest"
},
anonymous: false
});
if (!Utils.isJSON(res)) return false;
const data = JSON.parse(res);
const dashboard = data.dashboard || data;
const userStatus = dashboard.userStatus || {};
const counters = userStatus.counters || {};
const sumCounter = items => {
if (!Array.isArray(items)) return { progress: 0, max: 0 };
return items.reduce((acc, item) => {
acc.progress += Number(item.pointProgress || 0);
acc.max += Number(item.pointProgressMax || item.pointMax || 0);
return acc;
}, { progress: 0, max: 0 });
};
const pc = sumCounter(counters.pcSearch);
if (pc.max === 0) return false;
const balance = Number(userStatus.availablePoints || dashboard.availablePoints || 0);
let readProgress = 0, readMax = 30;
try {
const readInfo = await API.getReadProgress();
if (readInfo) { readProgress = readInfo.progress; readMax = readInfo.max; }
} catch (e) {}
Utils.log("📊", `getuserinfo查询: PC ${pc.progress}/${pc.max}, 阅读 ${readProgress}/${readMax}, 积分 ${balance}`);
return {
balance, pc, readProgress, readMax, dailyOffer: 0,
todayDetails: pc.max > 0 ? [{ title: "必应搜索", points: pc.progress, max: pc.max }] : [],
history: null
};
} catch (e) {
if (GM_getValue("Config.debugDAPI", false)) {
Utils.log("🟡", `getuserinfo 查询失败: ${e.message}`);
}
return false;
}
},
async getSearchQuotaFromAPI() {
try {
const res = await this.withTokenRetry(token => Utils.xhr({
url: "https://prod.rewardsplatform.microsoft.com/dapi/me?channel=SAAndroid&options=613",
headers: {
"content-type": "application/json; charset=UTF-8",
"user-agent": RewardsAuto.ua.app,
"authorization": `Bearer ${token}`,
"x-rewards-appid": RewardsAuto.appConfig.rewardsAppId,
"x-rewards-ismobile": "true",
"x-rewards-country": "cn",
"x-rewards-language": "zh"
}
}));
if (Utils.isJSON(res)) {
const data = JSON.parse(res);
const response = data.response || {};
const promos = response.promotions || [];
const counters = response.counters || response.userStatus?.counters;
if (!counters) return false;
let pcCur = 0, pcMax = 0;
if (counters?.pcSearch && counters.pcSearch.length > 0) {
pcCur = counters.pcSearch[0].pointProgress || 0;
pcMax = counters.pcSearch[0].pointProgressMax || 0;
}
if (pcMax === 0) return false;
const balance = response.balance || response.userStatus?.availablePoints || 0;
let readProgress = 0, readMax = 30;
try {
const readInfo = await API.getReadProgress();
if (readInfo) { readProgress = readInfo.progress; readMax = readInfo.max; }
} catch (e) {}
Utils.log("📊", `DAPI查询: PC ${pcCur}/${pcMax}, 阅读 ${readProgress}/${readMax}, 积分 ${balance}`);
return { balance, pc: { progress: pcCur, max: pcMax }, readProgress, readMax, dailyOffer: 0, todayDetails: [], history: null };
}
} catch (e) {
Utils.log("🟡", `DAPI查询失败: ${e.message}`);
}
return false;
},
async getBalance() {
try {
const res = await this.withTokenRetry(token => Utils.xhr({
url: "https://prod.rewardsplatform.microsoft.com/dapi/me?channel=SAAndroid&options=105",
headers: {
"content-type": "application/json; charset=UTF-8",
"user-agent": RewardsAuto.ua.app,
"authorization": `Bearer ${token}`,
"x-rewards-appid": RewardsAuto.appConfig.rewardsAppId,
"x-rewards-ismobile": "true",
"x-rewards-country": "cn",
"x-rewards-language": "zh"
}
}));
if (Utils.isJSON(res)) {
const data = JSON.parse(res);
return data.response?.balance || 0;
}
} catch (e) {}
try {
const res2 = await Utils.xhr({
url: `https://rewards.bing.com/api/getuserinfo?type=1&X-Requested-With=XMLHttpRequest&_=${Date.now()}`,
headers: {
"user-agent": RewardsAuto.ua.pc,
"referer": "https://rewards.bing.com/",
"x-requested-with": "XMLHttpRequest"
},
});
if (Utils.isJSON(res2)) {
const data2 = JSON.parse(res2);
return data2.dashboard?.availablePoints || data2.balance || 0;
}
} catch (e) {}
return 0;
},
async doRead() {
const region = GM_getValue("Config.lock", true) ? "cn" : RewardsAuto.state.region.toLowerCase();
try {
const id = Utils.getRandomUUID().replace(/-/g, '') + Utils.getRandomUUID().replace(/-/g, '').slice(0, 24);
const res = await this.withTokenRetry(token => Utils.xhr({
method: "POST",
url: "https://prod.rewardsplatform.microsoft.com/dapi/me/activities",
headers: {
"content-type": "application/json; charset=utf-8",
"user-agent": RewardsAuto.ua.app,
"authorization": `Bearer ${token}`,
"x-rewards-appid": RewardsAuto.appConfig.rewardsAppId,
"x-rewards-ismobile": "true",
"x-rewards-country": region,
"x-rewards-language": "zh"
},
data: JSON.stringify({
amount: 1, country: region, id: id, type: 101,
attributes: { offerid: RewardsAuto.appConfig.offerIds.readArticle }
})
}));
if (Utils.isJSON(res)) {
const data = JSON.parse(res);
return { points: data.response?.activity?.p || 0, isDuplicate: data.response?.isDuplicate || false };
}
return null;
} catch (e) {
Utils.log("🔴", `阅读请求失败: ${e.message}`);
return false;
}
},
async discoverCards() {
const cards = [];
const seenCardKeys = new Set();
try {
const html = await Utils.xhr({ url: "https://rewards.bing.com/earn" });
if (!html) { Utils.log("🔴", "earn 页面返回空"); return cards; }
const clean = html.replace(/\\"/g, '"');
const naMatch = html.match(/name":"next-action"[^}]*"value":"([a-f0-9]{40,})"/)
|| html.match(/next-action["']\s*:\s*["']([a-f0-9]{40,})["']/)
|| clean.match(/"next-action"[^"]*"([a-f0-9]{40,})"/);
if (naMatch) {
RewardsAuto._nextAction = naMatch[1];
Utils.log("🟢", `动态 next-action: ${naMatch[1].slice(0, 12)}…`);
}
const pushCard = (card) => {
if (!card || !card.offerId || !card.hash || card.points <= 0) return;
const key = `${card.offerId}:${card.hash}`;
if (seenCardKeys.has(key)) return;
seenCardKeys.add(key);
cards.push(card);
};
const inferKind = (offerId, title = "") => {
const text = `${offerId} ${title}`;
if (/quiz|trivia/i.test(text)) return "quiz";
if (/puzzle/i.test(text)) return "puzzle";
if (/image/i.test(text)) return "image_creator";
if (/explore|search/i.test(text)) return "explore_search";
if (/dailyset|daily/i.test(text)) return "daily";
if (/streak/i.test(text)) return "streak";
return "open_only";
};
const parseCard = (obj) => {
const offerIdMatch = obj.match(/"offerId":"([^"]+)"/i)
|| obj.match(/"offerid":"([^"]+)"/i)
|| obj.match(/"offer_id":"([^"]+)"/i);
const hashMatch = obj.match(/"hash":"([^"]+)"/)
|| obj.match(/"activityId":"([^"]+)"/)
|| obj.match(/"id":"([^"]+)"/);
if (!offerIdMatch || !hashMatch) return null;
const offerId = offerIdMatch[1];
const hash = hashMatch[1];
const pointsMatch = obj.match(/"points":(\d+)/);
const isCompletedMatch = obj.match(/"isCompleted":(true|false)/i)
|| obj.match(/"completed":(true|false)/i)
|| obj.match(/"state":"(completed|CLAIMED)"/i);
const titleMatch = obj.match(/"title":"([^"]+)"/)
|| obj.match(/"name":"([^"]+)"/)
|| obj.match(/"displayName":"([^"]+)"/);
const points = pointsMatch ? parseInt(pointsMatch[1]) : 0;
const isCompleted = isCompletedMatch
? (isCompletedMatch[1] === "true" || isCompletedMatch[1] === "completed" || isCompletedMatch[1] === "CLAIMED")
: false;
if (isCompleted) return null;
const title = titleMatch ? titleMatch[1] : "";
const skip = RewardsAuto.skipPatterns.some(p =>
title.toLowerCase().includes(p.toLowerCase()) ||
offerId.toLowerCase().includes(p.toLowerCase())
);
if (skip) return null;
return { title, points, offerId, hash, kind: inferKind(offerId, title) };
};
try {
const userInfo = await Utils.xhr({
url: `https://rewards.bing.com/api/getuserinfo?type=1&X-Requested-With=XMLHttpRequest&_=${Date.now()}`,
headers: {
"user-agent": RewardsAuto.ua.pc,
"referer": "https://rewards.bing.com/",
"x-requested-with": "XMLHttpRequest"
},
anonymous: false
});
if (Utils.isJSON(userInfo)) {
const data = JSON.parse(userInfo);
const dashboard = data.dashboard || data;
const now = new Date();
const todayKeys = new Set([
`${String(now.getMonth() + 1).padStart(2, "0")}/${String(now.getDate()).padStart(2, "0")}/${now.getFullYear()}`,
`${now.getMonth() + 1}/${now.getDate()}/${now.getFullYear()}`
]);
const normalizeDashboardCard = (item, kind) => {
if (!item) return null;
const offerId = item.offerId || item.offerid || item.id || item.name;
const hash = item.hash || item.activityId;
const title = item.title || item.name || item.description || "";
const points = Number(item.points ?? item.pointProgressMax ?? item.max ?? 0);
const doneMax = Number(item.pointProgressMax || 0);
const doneCur = Number(item.pointProgress || 0);
const isCompleted = item.isCompleted || item.complete || item.completed || (doneMax > 0 && doneCur >= doneMax);
if (!offerId || !hash || points <= 0 || isCompleted) return null;
const skip = RewardsAuto.skipPatterns.some(p =>
title.toLowerCase().includes(p.toLowerCase()) ||
offerId.toLowerCase().includes(p.toLowerCase())
);
if (skip) return null;
return {
title, points, offerId, hash,
kind: kind || inferKind(offerId, title),
source: "getuserinfo",
url: item.destinationUrl || item.destination || "https://rewards.bing.com/"
};
};
const dailySetPromotions = dashboard.dailySetPromotions || {};
for (const dateKey of todayKeys) {
const dailyItems = dailySetPromotions[dateKey];
if (Array.isArray(dailyItems)) {
for (const item of dailyItems) pushCard(normalizeDashboardCard(item, "daily"));
}
}
const morePromotions = dashboard.morePromotions || dashboard.promotions || [];
if (Array.isArray(morePromotions)) {
for (const item of morePromotions) pushCard(normalizeDashboardCard(item));
}
if (cards.length > 0) {
Utils.log("🧩", `getuserinfo 命中 ${cards.length} 个活动卡片`);
}
}
} catch (e) {
Utils.log("🟡", `getuserinfo 活动解析跳过: ${e.message}`);
}
const m1 = clean.match(/"activityCards":\[([\s\S]*?)\](?=,"|,"[a-z]|}$)/i);
if (m1) {
Utils.log("🧩", "命中 activityCards 数组");
const cardObjRegex = /\{[^{}]*\}/g;
let m;
while ((m = cardObjRegex.exec(m1[1])) !== null) {
const card = parseCard(m[0]);
pushCard(card);
}
}
if (cards.length === 0) {
const m2 = clean.match(/"(?:promotionCards|promotions|dailySet|cards)":\[([\s\S]*?)\](?=,"|,"[a-z]|}$)/i);
if (m2) {
Utils.log("🧩", "命中 promotionCards/promotions 数组");
const cardObjRegex = /\{[^{}]*\}/g;
let m;
while ((m = cardObjRegex.exec(m2[1])) !== null) {
const card = parseCard(m[0]);
pushCard(card);
}
}
}
if (cards.length === 0) {
Utils.log("🧩", "全局扫描 RSC payload 中的卡片对象");
const seen = new Set();
const offerIdRe = /"offerId"|"offerid"|"offer_id"/gi;
let m;
while ((m = offerIdRe.exec(clean)) !== null) {
let start = m.index;
while (start > 0 && clean[start] !== '{') start--;
if (clean[start] !== '{') continue;
let depth = 0, end = start;
for (let i = start; i < clean.length; i++) {
if (clean[i] === '{') depth++;
if (clean[i] === '}') { depth--; if (depth === 0) { end = i + 1; break; } }
}
if (depth !== 0) continue;
const obj = clean.slice(start, end);
if (!/"(?:hash|activityId|id)"\s*:/.test(obj)) continue;
const key = obj.slice(0, 80);
if (seen.has(key)) continue;
seen.add(key);
const card = parseCard(obj);
pushCard(card);
}
}
if (cards.length === 0) {
Utils.log("🧩", "尝试解析 RSC flight payload");
const flightRegex = /\d+:(\{[\s\S]*?"(?:offerId|offerid)"[\s\S]*?\})\n/g;
let m;
const seen = new Set();
while ((m = flightRegex.exec(clean)) !== null) {
const key = m[1].slice(0, 80);
if (seen.has(key)) continue;
seen.add(key);
const card = parseCard(m[1]);
pushCard(card);
}
}
if (cards.length === 0) {
Utils.log("🧩", "尝试从 HTML data 属性中提取卡片");
const dataRegex = /data-offer-id="([^"]+)"[^>]*data-hash="([^"]+)"/gi;
let m;
while ((m = dataRegex.exec(html)) !== null) {
const offerId = m[1];
const hash = m[2];
const skip = RewardsAuto.skipPatterns.some(p => offerId.toLowerCase().includes(p.toLowerCase()));
if (skip) continue;
pushCard({ title: "", points: 1, offerId, hash, kind: "open_only" });
}
}
if (cards.length === 0) {
const snippet = clean.slice(0, 500).replace(/[\r\n]+/g, ' ');
Utils.log("🟡", `所有方法均未命中,页面前500字符: ${snippet}`);
}
} catch (e) {
Utils.log("🔴", `卡片解析失败: ${e.message}`);
}
return cards;
},
async claimCard(card) {
const nextAction = RewardsAuto._nextAction || "70babbc81d2724f60d29a95c03b3d739cba77cea92";
const url = card.url || "https://rewards.bing.com/earn";
const referer = card.url || "https://rewards.bing.com/";
try {
const token = await this.getRewardsToken();
if (token) {
await Utils.xhr({
method: "POST",
url: "https://rewards.bing.com/api/reportactivity?X-Requested-With=XMLHttpRequest",
headers: {
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
"user-agent": RewardsAuto.ua.pc,
"referer": referer,
"origin": "https://rewards.bing.com",
"x-requested-with": "XMLHttpRequest"
},
data: new URLSearchParams({
id: card.offerId, hash: card.hash, timeZone: 480,
activityAmount: 1, dbs: 0, form: "", type: "",
__RequestVerificationToken: token
}).toString()
});
return true;
}
} catch (e1) {
Utils.log("🟡", `reportactivity+token 失败: ${card.offerId}`);
}
try {
await Utils.xhr({
method: "POST", url,
headers: { "content-type": "text/plain;charset=UTF-8", "next-action": nextAction, referer },
data: JSON.stringify([card.hash, 11, { offerid: card.offerId, isPromotional: "$undefined", timezoneOffset: "-480" }])
});
return true;
} catch (e2) {
Utils.log("🟡", `Server Action 失败: ${card.offerId}`);
}
try {
await this.reportActivity(card.offerId, card.hash, referer);
return true;
} catch (e3) {
Utils.log("🟡", `reportActivity 失败: ${card.offerId}`);
}
try {
await this.reportActivity(card.offerId, "1", referer);
return true;
} catch (e4) {
Utils.log("🟡", `卡片领取失败(${card.offerId}): 所有策略均失败`);
return false;
}
},
async getSearchPage(query, isMobile = false) {
const mkt = GM_getValue("Config.lock", true) ? "&mkt=zh-CN" : "";
const deviceType = isMobile ? "m" : "d";
return Utils.xhr({
url: `https://${RewardsAuto.state.host}/search?q=${encodeURIComponent(query)}&form=QBLH${mkt}`,
headers: {
"user-agent": isMobile ? RewardsAuto.ua.mobile : RewardsAuto.ua.pc,
"cookie": `_Rwho=u=${deviceType}&ts=${RewardsAuto.state.dateNowStr}`,
"referer": `https://${RewardsAuto.state.host}/?form=QBLH`
}
});
},
async reportSearch(html, query, isMobile = false) {
try {
const ig = Utils.getRandomUUID();
const mkt = GM_getValue("Config.lock", true) ? "&mkt=zh-CN" : "";
const params = `q=${encodeURIComponent(query)}&form=QBLH${mkt}`;
const deviceType = isMobile ? "m" : "d";
const headers = {
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
"user-agent": isMobile ? RewardsAuto.ua.mobile : RewardsAuto.ua.pc,
"referer": `https://${RewardsAuto.state.host}/?form=QBLH`,
"cookie": `_Rwho=u=${deviceType}&ts=${RewardsAuto.state.dateNowStr}`
};
await Utils.xhr({
method: "POST",
url: `https://${RewardsAuto.state.host}/rewardsapp/ncheader?ver=88888888&IID=SERP.5047&IG=${ig}&ajaxreq=1`,
headers, data: "wb=1%3bi%3d1%3bv%3d1"
});
await Utils.xhr({
method: "POST",
url: `https://${RewardsAuto.state.host}/rewardsapp/reportActivity?IG=${ig}&IID=SERP.5047&${params}&ajaxreq=1`,
headers,
data: `url=${encodeURIComponent(`https://${RewardsAuto.state.host}/search?${params}`)}&V=web`
});
return true;
} catch (e) {
Utils.log("🟡", `搜索上报失败: ${e.message}`);
return false;
}
},
async checkRegion(retryCount = 0) {
if (!GM_getValue("Config.lock", true)) return true;
try {
const html = await Utils.xhr({ url: `https://${RewardsAuto.state.host}/` });
if (!html) {
if (retryCount < 2) {
Utils.log("🟡", `地区检测返回空,第${retryCount + 1}次重试...`);
await Utils.randomDelay(3000, 8000);
return await this.checkRegion(retryCount + 1);
}
Utils.log("🔴", "地区检测失败(无响应)");
return false;
}
const match = html.replace(/\s/g, "").match(/Region:"(.*?)"(.*?)RevIpCC:"(.*?)"/);
if (match) {
RewardsAuto.state.region = match[3].toUpperCase();
if (RewardsAuto.state.region !== "CN") {
await this.getIPInfo();
Utils.log("🔴", `IP非大陆(${RewardsAuto.state.region}),已停止\n${RewardsAuto.state.ipInfo}`, true);
return false;
}
Utils.log("🟢", `地区检测通过: ${RewardsAuto.state.region}`);
return true;
}
if (retryCount < 2) {
Utils.log("🟡", `地区检测格式异常,第${retryCount + 1}次重试...`);
await Utils.randomDelay(3000, 8000);
return await this.checkRegion(retryCount + 1);
}
Utils.log("🔴", "地区检测失败(格式不匹配)");
return false;
} catch (e) {
if (retryCount < 2) {
Utils.log("🟡", `地区检测异常: ${e.message},第${retryCount + 1}次重试...`);
await Utils.randomDelay(3000, 8000);
return await this.checkRegion(retryCount + 1);
}
Utils.log("🔴", `地区检测失败: ${e.message}`);
return false;
}
},
async getIPInfo() {
try {
const qryResult = await Utils.xhr({
url: "https://disp-qryapi.3g.qq.com/v1/dispatch",
headers: { "referer": "https://3g.qq.com/" }
});
if (qryResult && Utils.isJSON(qryResult)) {
const resJSON = JSON.parse(qryResult);
RewardsAuto.state.ip = (resJSON.code == 0 && resJSON.extra && resJSON.extra.ip) ? resJSON.extra.ip : "";
let rawInfo = (resJSON.code == 0 && resJSON.ipInfo) ? String(resJSON.ipInfo) : "";
rawInfo = rawInfo.replace(/[#*]+/g, " ").trim();
RewardsAuto.state.ipInfo = rawInfo ? `🌏所在地区:${rawInfo}` : "";
}
} catch {
console.debug("获取附加 IP 信息失败");
}
},
async getHotSearchWord() {
const keywords = ["天气预报", "今日新闻", "体育赛事", "股票行情", "电影推荐", "科技资讯", "美食食谱", "旅游攻略", "历史上的今天", "健康常识"];
const baseWord = keywords[Utils.randomRange(0, keywords.length - 1)];
const randomSuffix = Math.random().toString(36).slice(2, 6);
let sentence = `${baseWord} ${randomSuffix}`;
if (RewardsAuto.apiConfig.mode !== "offline") {
if (RewardsAuto.apiConfig.wordIndex < 1 || RewardsAuto.apiConfig.wordList.length < 1) {
const apiArr = RewardsAuto.apiConfig.arr;
const lastApiIndex = parseInt(GM_getValue("Config.apiIndex", -1));
const filteredArr = apiArr.filter((_, index) => index !== lastApiIndex);
const randomIndex = Utils.randomRange(0, filteredArr.length - 1);
GM_setValue("Config.apiIndex", randomIndex);
const [apiName, apiConfig] = filteredArr[randomIndex];
RewardsAuto.apiConfig.url = apiConfig.url;
RewardsAuto.apiConfig.hot = apiConfig.hot;
try {
const hotSource = RewardsAuto.apiConfig.hot[Utils.randomRange(0, RewardsAuto.apiConfig.hot.length - 1)];
const result = await Utils.xhr({ url: RewardsAuto.apiConfig.url + hotSource });
if (result && Utils.isJSON(result)) {
const res = JSON.parse(result);
if (res.code == 200) {
RewardsAuto.apiConfig.wordIndex = 1;
RewardsAuto.apiConfig.wordList = [];
for (let i = 0; i < res.data.length; i++) {
RewardsAuto.apiConfig.wordList.push(res.data[i].title);
}
RewardsAuto.apiConfig.wordList.sort(() => Math.random() - 0.5);
sentence = RewardsAuto.apiConfig.wordList[RewardsAuto.apiConfig.wordIndex];
sentence = sentence.substring(0, Utils.randomRange(20, 32));
return sentence;
}
}
} catch (e) {
Utils.log("🟡", `热搜词获取失败: ${e.message}`);
}
} else {
RewardsAuto.apiConfig.wordIndex++;
if (RewardsAuto.apiConfig.wordIndex > RewardsAuto.apiConfig.wordList.length - 1) {
RewardsAuto.apiConfig.wordIndex = 0;
}
sentence = RewardsAuto.apiConfig.wordList[RewardsAuto.apiConfig.wordIndex];
sentence = sentence.substring(0, Utils.randomRange(20, 32));
return sentence;
}
Utils.log("🟡", "热搜词接口异常,已使用随机搜索词");
}
return sentence;
},
async checkSearchRestricted() {
const info = await this.getRewardsInfo();
const currentTotal = info ? info.pc.progress : RewardsAuto.state.pcProgress;
const lastTotal = RewardsAuto.state.lastSearchProgress;
if (lastTotal !== -1) {
if (currentTotal === lastTotal && currentTotal < RewardsAuto.state.pcMax) {
RewardsAuto.state.restrictedTimes++;
} else {
RewardsAuto.state.restrictedTimes = 0;
}
}
RewardsAuto.state.lastSearchProgress = currentTotal;
GM_setValue("Config.lastSearchProgress", currentTotal);
GM_setValue("Config.restrictedTimes", RewardsAuto.state.restrictedTimes);
if (RewardsAuto.state.restrictedTimes >= 3) {
Utils.log("🔴", "搜索受限或账号异常,已中断今日搜索!", true);
return true;
}
return false;
}
};
const TaskManager = {
signDate: 0, readDate: 0, promosDate: 0, searchDate: 0, streakDays: 0,
signPoint: -1, signTimes: 0, readTimes: 0, promosTimes: 0,
init() {
RewardsAuto.state.dateNowNum = Utils.getTodayNum();
RewardsAuto.state.dateNowStr = Utils.getTodayStr();
const tasks = GM_getValue("Config.tasks", {});
this.signDate = tasks.sign || 0;
this.readDate = tasks.read || 0;
this.promosDate = tasks.promos || 0;
this.searchDate = tasks.search || 0;
this.streakDays = tasks.streakDays || 0;
this.signPoint = GM_getValue("Config.signPoint", -1);
},
save() {
GM_setValue("Config.tasks", {
sign: this.signDate, read: this.readDate,
promos: this.promosDate, search: this.searchDate,
streakDays: this.streakDays
});
},
async doSign() {
if (!GM_getValue("Tasks.sign", true) || this.signTimes > 2) return;
if (this.signPoint >= 0 && this.signDate === RewardsAuto.state.dateNowNum) {
Utils.log("✅", `签入已完成(${this.signPoint}积分)`);
return;
}
await Utils.randomDelay();
let totalPoint = 0;
let signOk = false;
const appPoint = await API.signApp();
if (appPoint >= 0) {
signOk = true;
if (appPoint > 0) {
GM_log(`📱 App签入静默成功 +${appPoint}积分`);
totalPoint += appPoint;
} else {
GM_log("📱 App签入已确认,无新增积分");
}
}
await Utils.randomDelay(3000, 8000);
const pcPoint = await API.signPC();
if (pcPoint >= 0) {
signOk = true;
Utils.log("💻", `PC签入成功!+${pcPoint}积分`);
totalPoint += pcPoint;
}
if (signOk) {
this.signPoint = totalPoint;
this.signDate = RewardsAuto.state.dateNowNum;
GM_setValue("Config.signPoint", totalPoint);
this.save();
Utils.log("🔵", `签入任务完成!总积分 +${totalPoint}`, true);
} else {
this.signTimes++;
Utils.log("🟡", `签入失败,稍后重试`);
}
},
async doRead() {
if (!GM_getValue("Tasks.read", true) || this.readTimes > 2) return;
if (this.readDate === RewardsAuto.state.dateNowNum) {
const verifyProgress = await API.getReadProgress();
if (verifyProgress && verifyProgress.progress >= verifyProgress.max) {
Utils.log("✅", `阅读任务已完成(已验证 ${verifyProgress.progress}/${verifyProgress.max})`);
return;
} else if (verifyProgress) {
Utils.log("🟡", `阅读标记有误(${verifyProgress.progress}/${verifyProgress.max}),重置并继续`);
this.readDate = 0;
this.save();
} else {
Utils.log("🟡", "无法验证阅读进度,跳过");
return;
}
}
const progress = await API.getReadProgress();
if (!progress) {
this.readTimes++;
Utils.log("🟡", "无法获取阅读进度,稍后重试");
return;
}
const { progress: cur, max } = progress;
Utils.log("📖", `阅读进度: ${cur}/${max}`);
if (cur >= max) {
this.readDate = RewardsAuto.state.dateNowNum;
this.save();
Utils.log("✅", "阅读任务已完成");
return;
}
let successCount = 0;
const maxPerDay = 10;
const remaining = Math.min(max - cur, maxPerDay);
Utils.log("📖", `今日还可阅读 ${remaining} 篇(上限 ${maxPerDay} 篇/天)`);
for (let i = 0; i < remaining; i++) {
const result = await API.doRead();
if (!result) { Utils.log("🟡", `阅读第 ${i + 1} 篇失败,中止`); break; }
successCount++;
Utils.log("📖", `阅读文章 ${i + 1}/${remaining} +${result.points}积分`);
await Utils.randomDelay(3000, 8000);
}
if (successCount === 0) {
this.readTimes++;
Utils.log("🟡", "阅读全部失败,稍后重试");
return;
}
const verify = await API.getReadProgress();
if (verify && verify.progress >= verify.max) {
this.readDate = RewardsAuto.state.dateNowNum;
this.save();
Utils.log("🔵", `阅读任务完成!共 ${successCount} 篇`, true);
} else {
this.readTimes++;
Utils.log("🟡", `阅读已执行但未完成,下次继续`);
}
},
async doPromos() {
if (!GM_getValue("Tasks.promos", true) || this.promosTimes > 2) return;
if (this.promosDate === RewardsAuto.state.dateNowNum) {
Utils.log("✅", "活动卡片已完成");
return;
}
Utils.log("🧩", "扫描活动卡片...");
const cards = await API.discoverCards();
if (cards.length === 0) {
this.promosDate = RewardsAuto.state.dateNowNum;
this.save();
Utils.log("✅", "无新活动卡片");
return;
}
Utils.log("🧩", `发现 ${cards.length} 个卡片`);
let ok = 0, fail = 0;
for (const card of cards) {
Utils.log(" ", `[${card.kind}] ${card.title} +${card.points}p`);
if (card.kind === "quiz" && !GM_getValue("Tasks.quiz", true)) continue;
await Utils.randomDelay(3000, 8000);
const result = await API.claimCard(card);
result ? ok++ : fail++;
}
this.promosDate = RewardsAuto.state.dateNowNum;
this.save();
Utils.log("🔵", `活动完成: ${ok}成功/${fail}失败`, true);
},
async doSearch() {
if (!GM_getValue("Tasks.search", true)) return;
const info = await API.getRewardsInfo();
if (!info) { Utils.log("🔴", "无法获取积分信息"); return; }
RewardsAuto.state.pcProgress = info.pc.progress;
RewardsAuto.state.pcMax = info.pc.max;
Utils.log("🔍", `搜索配额: PC ${info.pc.progress}/${info.pc.max}`);
const pcDone = info.pc.progress >= info.pc.max;
if (pcDone) {
this.searchDate = RewardsAuto.state.dateNowNum;
this.save();
Utils.log("✅", `搜索配额已满 PC: ${info.pc.progress}/${info.pc.max}`);
return;
}
if (this.searchDate === RewardsAuto.state.dateNowNum) {
Utils.log("🟡", "搜索配额未满,继续执行搜索任务");
this.searchDate = 0;
}
const isRestricted = await API.checkSearchRestricted();
if (isRestricted) {
this.searchDate = RewardsAuto.state.dateNowNum;
this.save();
return;
}
const limit = Utils.randomRange(4, 7);
for (let i = 0; i < limit; i++) {
if (RewardsAuto.state.pcProgress >= RewardsAuto.state.pcMax) break;
let query;
if (RewardsAuto.apiConfig.mode !== "offline") {
query = await API.getHotSearchWord();
} else {
query = RewardsAuto.searchPool[Utils.randomRange(0, RewardsAuto.searchPool.length - 1)];
}
Utils.log("🔍", `[PC] 搜索 ${i+1}/${limit}: ${query}`);
try {
const html = await API.getSearchPage(query, false);
if (html) {
await API.reportSearch(html, query, false);
RewardsAuto.state.pcProgress += 3;
}
} catch (e) {
Utils.log("🟡", `搜索失败: ${e.message}`);
}
const span = Number(GM_getValue("Config.span", 30));
const wait = Utils.randomRange((span-15)*1000, (span+15)*1000);
Utils.log("⏳", `等待 ${wait/1000}秒`);
await Utils.delay(wait);
}
const finalInfo = await API.getRewardsInfo();
if (finalInfo) {
const pcDone2 = finalInfo.pc.progress >= finalInfo.pc.max;
if (pcDone2) {
this.searchDate = RewardsAuto.state.dateNowNum;
this.save();
RewardsAuto.state.restrictedTimes = 0;
GM_setValue("Config.restrictedTimes", 0);
GM_setValue("Config.lastSearchProgress", -1);
Utils.log("🔵", `🔍 搜索任务完成!PC: ${finalInfo.pc.progress}/${finalInfo.pc.max}`, true);
} else {
Utils.log("🟡", `搜索已执行,配额未满 PC: ${finalInfo.pc.progress}/${finalInfo.pc.max}`);
}
} else {
Utils.log("🟡", "搜索已执行,但无法获取最终配额状态");
}
},
// 【核心修改】每日活动:纯 XHR,不打开任何页面
async doDailySet() {
const today = Utils.getTodayNum();
const processedKey = "Config.dailySetProcessed";
let processed = GM_getValue(processedKey, []);
if (processed.length > 0 && processed[0]?.date !== today) processed = [];
const processedIds = new Set(processed.map(p => p.offerId));
Utils.log("📅", `开始执行每日活动(已处理 ${processedIds.size} 个)...`);
await Utils.randomDelay(3000, 8000);
let ok = 0, fail = 0;
try {
const userInfo = await Utils.xhr({
url: `https://rewards.bing.com/api/getuserinfo?type=1&X-Requested-With=XMLHttpRequest&_=${Date.now()}`,
headers: {
"user-agent": RewardsAuto.ua.pc,
"referer": "https://rewards.bing.com/",
"x-requested-with": "XMLHttpRequest"
},
anonymous: false
});
if (!Utils.isJSON(userInfo)) {
Utils.log("🟡", "每日活动:getuserinfo 返回非 JSON,跳过");
return;
}
const data = JSON.parse(userInfo);
const dashboard = data.dashboard || data;
const now = new Date();
const todayKeys = [
`${String(now.getMonth() + 1).padStart(2, "0")}/${String(now.getDate()).padStart(2, "0")}/${now.getFullYear()}`,
`${now.getMonth() + 1}/${now.getDate()}/${now.getFullYear()}`
];
let dailyItems = [];
const dailySetPromotions = dashboard.dailySetPromotions || {};
for (const dateKey of todayKeys) {
if (Array.isArray(dailySetPromotions[dateKey])) {
dailyItems = dailySetPromotions[dateKey];
break;
}
}
Utils.log("📅", `发现 ${dailyItems.length} 个每日活动`);
const rewardsToken = await API.getRewardsToken();
for (const item of dailyItems) {
const offerId = item.offerId || item.offerid || item.id || item.name;
const hash = item.hash || item.activityId;
const title = item.title || item.name || "每日活动";
const points = Number(item.points ?? item.pointProgressMax ?? 0);
const doneMax = Number(item.pointProgressMax || 0);
const doneCur = Number(item.pointProgress || 0);
const isCompleted = item.isCompleted || item.complete || item.completed
|| (doneMax > 0 && doneCur >= doneMax);
if (!offerId || !hash || isCompleted) continue;
if (processedIds.has(offerId)) continue;
Utils.log("📅", `领取每日活动: ${title} +${points}p`);
await Utils.randomDelay(3000, 8000);
try {
const params = new URLSearchParams({
id: offerId, hash: hash, timeZone: 480,
activityAmount: 1, dbs: 0, form: "", type: ""
});
if (rewardsToken) params.set("__RequestVerificationToken", rewardsToken);
const headers = {
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
"user-agent": RewardsAuto.ua.pc,
"referer": "https://rewards.bing.com/",
"origin": "https://rewards.bing.com",
"x-requested-with": "XMLHttpRequest"
};
if (rewardsToken) headers["RequestVerificationToken"] = rewardsToken;
await Utils.xhr({
method: "POST",
url: "https://rewards.bing.com/api/reportactivity?X-Requested-With=XMLHttpRequest",
headers, data: params.toString(), anonymous: false
});
ok++;
processedIds.add(offerId);
} catch (e) {
fail++;
Utils.log("🟡", `每日活动领取失败: ${title} - ${e.message}`);
}
}
} catch (e) {
Utils.log("🔴", `每日活动执行异常: ${e.message}`);
}
GM_setValue(processedKey, [...processedIds].map(id => ({ date: today, offerId: id })));
if (ok > 0 || fail > 0) {
Utils.log("🔵", `每日活动完成: ${ok}成功/${fail}失败`, true);
} else {
Utils.log("✅", "无待处理的每日活动");
}
},
// 【核心修改】领取积分:仅检测 + 通知,不打开页面
async doClaimPoints() {
try {
const dashboardHtml = await Utils.xhr({ url: "https://rewards.bing.com/dashboard" });
if (!dashboardHtml) {
Utils.log("✅", "无法获取 dashboard 页面");
return;
}
const claimableMatch = dashboardHtml.match(/alt="可领取"[^>]*>[\s\S]*?(\d[\d,]*)/i);
if (!claimableMatch) {
Utils.log("✅", "无可领取积分");
return;
}
const amount = parseInt(claimableMatch[1].replace(/,/g, '')) || 0;
if (amount > 0) {
Utils.log("🎁", `发现 ${amount} 积分待领取(请手动前往 dashboard 领取)`, true);
} else {
Utils.log("✅", "可领取积分为 0");
}
} catch (e) {
Utils.log("🟡", `检测可领取积分失败: ${e.message}`);
}
},
async doStreak() {
Utils.log("📅", "开始检测连签任务...");
try {
const earnHtml = await Utils.xhr({ url: "https://rewards.bing.com/earn" });
if (!earnHtml) { Utils.log("🟡", "无法获取 earn 页面"); return; }
const text = earnHtml.replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ');
const streakDaysMatch = text.match(/每日连续打卡\s*(\d+)/);
if (streakDaysMatch) {
this.streakDays = parseInt(streakDaysMatch[1]) || 0;
Utils.log("📅", `每日连续打卡:${this.streakDays} 天`);
}
const taskPatterns = [
{ name: "必应搜索连续打卡", pattern: /必应搜索连续打卡[\s\S]*?搜索:\s*(\d+)\/(\d+)/ },
{ name: "每日连续打卡活动", pattern: /每日连续打卡活动[\s\S]*?活动:\s*(\d+)\/(\d+)/ },
{ name: "必应应用连续打卡", pattern: /必应应用连续打卡[\s\S]*?签到:\s*(\d+)\/(\d+)/ },
{ name: "视觉搜索连续打卡", pattern: /视觉搜索连续打卡[\s\S]*?活动:\s*(\d+)\/(\d+)/ },
];
let visualSearchDone = true;
for (const { name, pattern } of taskPatterns) {
const m = text.match(pattern);
if (m) {
const cur = parseInt(m[1]), max = parseInt(m[2]);
const done = cur >= max;
Utils.log(done ? "✅" : "📅", `${name}: ${cur}/${max}${done ? " 已完成" : ""}`);
if (name === "视觉搜索连续打卡" && !done) visualSearchDone = false;
}
}
// 【核心修改】视觉搜索改为后台 XHR
if (!visualSearchDone) {
Utils.log("📅", "视觉搜索未完成,尝试后台打卡...");
try {
await Utils.xhr({
url: "https://www.bing.com/?features=vsstreak,vstooltip&form=ML2XES",
headers: {
"user-agent": RewardsAuto.ua.pc,
"referer": "https://www.bing.com/"
}
});
Utils.log("✅", "视觉搜索打卡请求已发送(后台)");
await Utils.randomDelay(3000, 5000);
} catch (e) {
Utils.log("🟡", `视觉搜索打卡失败: ${e.message}`);
}
}
const stampMatch = text.match(/(\d+)\s*个印章/) || text.match(/印章\s*(\d+)/);
if (stampMatch) {
Utils.log("📅", `连签奖励印章进度: ${stampMatch[1]}/12`);
}
Utils.log("📅", "连签任务检测完成,未完成任务将由活动卡片模块处理");
} catch (e) {
Utils.log("🔴", `连签任务异常: ${e.message}`);
}
},
async runAll() {
if (this.running) {
Utils.log("🟡", "任务正在运行中,请勿重复触发");
return;
}
this.running = true;
try {
RewardsAuto.state.startTime = Utils.getTimestamp();
Utils.log("🚀", "启动全能自动化任务...");
this.init();
const startBalance = await API.getBalance();
Utils.log("📊", `初始积分: ${startBalance}`);
const regionOK = await API.checkRegion();
let isTokenOK = false;
if (regionOK) {
isTokenOK = await API.renewToken();
if (!isTokenOK) Utils.log("🟡", "Token失败,跳过签入/阅读", true);
} else {
Utils.log("🔴", "IP非国内,已暂停全部任务", true);
return;
}
const retryDelay = 60000;
const maxRetries = 2;
const withRetry = async (taskFn, taskName, retries = 0) => {
try {
const result = await taskFn();
if (result === false && retries < maxRetries) {
Utils.log("🟡", `${taskName} 失败,${retryDelay/1000}秒后重试 (${retries + 1}/${maxRetries})`);
await Utils.delay(retryDelay);
return withRetry(taskFn, taskName, retries + 1);
}
return result;
} catch (e) {
if (retries < maxRetries) {
Utils.log("🟡", `${taskName} 异常: ${e.message},${retryDelay/1000}秒后重试`);
await Utils.delay(retryDelay);
return withRetry(taskFn, taskName, retries + 1);
}
Utils.log("🔴", `${taskName} 失败: ${e.message}`);
return false;
}
};
if (regionOK && isTokenOK) {
await withRetry(() => this.doSign(), "签到");
await Utils.randomDelay();
if (!RewardsAuto.state.pc401) {
await withRetry(() => this.doRead(), "阅读");
await Utils.randomDelay();
} else {
Utils.log("🟡", "PC会话已过期,跳过阅读任务");
}
} else if (regionOK) {
await withRetry(() => this.doSign(), "签到");
await Utils.randomDelay();
}
await withRetry(() => this.doPromos(), "活动卡片");
await Utils.randomDelay();
await withRetry(() => this.doSearch(), "搜索");
await withRetry(() => this.doStreak(), "连签检测");
await Utils.randomDelay();
Utils.log("📅", "开始执行每日活动任务...");
await Utils.randomDelay();
await withRetry(() => this.doDailySet(), "每日活动");
try {
await this.doClaimPoints();
} catch (e) {
Utils.log("🟡", `领取积分执行异常: ${e.message}`);
}
Utils.log("🔄", "二次扫描:检查是否有新解锁的卡片...");
await Utils.randomDelay(3000, 8000);
const newCards = await API.discoverCards();
if (newCards.length > 0) {
Utils.log("🧩", `二次扫描发现 ${newCards.length} 个新卡片`);
let ok = 0, fail = 0;
for (const card of newCards) {
Utils.log(" ", `[${card.kind}] ${card.title} +${card.points}p`);
if (card.kind === "quiz" && !GM_getValue("Tasks.quiz", true)) continue;
await Utils.randomDelay(3000, 8000);
const result = await API.claimCard(card);
result ? ok++ : fail++;
}
Utils.log("🔵", `二次扫描完成: ${ok}成功/${fail}失败`);
} else {
Utils.log("✅", "二次扫描:无新卡片");
}
const endTime = Utils.getTimestamp();
const totalTime = ((endTime - RewardsAuto.state.startTime) / 1000).toFixed(1);
const endBalance = await API.getBalance();
const earned = (startBalance > 0 && endBalance > 0) ? (endBalance - startBalance) : 0;
const info = await API.getRewardsInfo();
if (info) {
const signOk = this.signDate === RewardsAuto.state.dateNowNum;
const readOk = this.readDate === RewardsAuto.state.dateNowNum;
const searchOk = info.pc.progress >= info.pc.max;
const promosOk = this.promosDate === RewardsAuto.state.dateNowNum;
let logMsg = `签到\t\t${signOk ? '✅' : '❌'}\n`;
logMsg += `阅读\t\t${readOk ? '✅' : '❌'} ${info.readProgress || 0}/${info.readMax || 30}\n`;
logMsg += `PC 搜索\t${searchOk ? '✅' : '⏳'} ${info.pc.progress}/${info.pc.max}\n`;
logMsg += `活动卡片\t${promosOk ? '✅' : '❌'}\n`;
logMsg += `连签\t\t${this.streakDays || 0} 天\n`;
logMsg += `今日获取\t+${earned}\n`;
logMsg += `总积分\t\t${info.balance || endBalance}`;
RewardsAuto.state.sendMSG = logMsg;
Utils.log("📊", logMsg, true);
} else {
Utils.log("🎉", `任务执行完成!用时 ${totalTime} 秒`, true);
}
} finally {
this.running = false;
}
}
};
GM_registerMenuCommand("🔑 手动授权", () => {
GM_openInTab("https://login.live.com/oauth20_authorize.srf?client_id=0000000040170455&response_type=code&scope=service::prod.rewardsplatform.microsoft.com::MBI_SSL&redirect_uri=https://login.live.com/oauth20_desktop.srf", { active: true });
});
GM_registerMenuCommand("📋 粘贴授权码", () => {
const code = prompt("粘贴授权页面跳转后的完整URL:");
if (code?.trim()) {
GM_setValue("Config.code", code.trim());
alert("已保存!");
}
});
GM_registerMenuCommand("📊 Token状态", () => {
const token = GM_getValue("Config.token", false);
const time = GM_getValue("Config.tokenTime", 0);
let ageStr = "未知";
if (time > 0) {
const diff = Utils.getTimestamp() - time;
const days = Math.floor(diff / 86400000);
const hours = Math.floor((diff % 86400000) / 3600000);
const minutes = Math.floor((diff % 3600000) / 60000);
const parts = [];
if (days > 0) parts.push(`${days}天`);
if (hours > 0) parts.push(`${hours}小时`);
parts.push(`${minutes}分钟`);
ageStr = parts.join("");
}
const tokenDate = time > 0 ? new Date(time).toLocaleString("zh-CN") : "未知";
alert(`Token: ${token ? "已保存" : "无"}\n获取时间: ${tokenDate}\n已过: ${ageStr}\n授权码: ${GM_getValue("Config.code", "") ? "有" : "无"}`);
});
GM_registerMenuCommand("🚀 立即运行", () => TaskManager.runAll());
GM_registerMenuCommand("🔔 配置通知接口", () => {
const configNames = [
{ key: "Notice.wework", name: "企业微信 Webhook", hint: "群机器人webhook key" },
{ key: "Notice.dingding", name: "钉钉机器人 Access Token", hint: "不加签,关键词需包含 #" },
{ key: "Notice.feishu", name: "飞书机器人 Webhook", hint: "不加签,关键词需包含 #" },
{ key: "Notice.pushme", name: "PushMe Key", hint: "push.i-i.me 推送key" },
{ key: "Notice.bark", name: "Bark Key", hint: "bark.day.app 推送key" }
];
let configStr = "🔔 通知接口配置\n==================\n\n";
configNames.forEach((item, index) => {
const saved = GM_getValue(item.key, "");
configStr += `${index + 1}. ${item.name}\n 状态: ${saved ? "✅ 已配置" : "❌ 未配置"}\n 说明: ${item.hint}\n\n`;
});
configStr += "请输入要配置的编号 (1-5),或输入 0 清除所有配置:";
const choice = prompt(configStr);
if (!choice) return;
const num = parseInt(choice);
if (num === 0) {
if (confirm("确定要清除所有通知接口配置吗?")) {
configNames.forEach(item => GM_setValue(item.key, ""));
alert("所有通知接口配置已清除!");
}
return;
}
if (num >= 1 && num <= 5) {
const selected = configNames[num - 1];
const current = GM_getValue(selected.key, "");
const newValue = prompt(`配置 ${selected.name}\n\n当前值: ${current || "(空)"}\n\n请输入新的值:`, current);
if (newValue !== null) {
GM_setValue(selected.key, newValue.trim());
alert(`${selected.name} 已${newValue.trim() ? "配置" : "清除"}!`);
}
} else {
alert("无效的编号!");
}
});
GM_registerMenuCommand("📢 测试通知", () => {
RewardsAuto.state.sendMSG = "🧪 这是一条测试消息\n如果你看到这条消息,说明通知接口配置成功!";
Utils.log("📢", "测试通知已发送", true);
alert("测试消息已发送,请检查各通知渠道!");
});
const updateBroMenu = () => {
const enabled = GM_getValue("Notice.bro", true);
return enabled ? "🔕 关闭浏览器通知" : "🔔 开启浏览器通知";
};
GM_registerMenuCommand(updateBroMenu(), () => {
const current = GM_getValue("Notice.bro", true);
GM_setValue("Notice.bro", !current);
alert(`浏览器通知已${!current ? "开启" : "关闭"}`);
location.reload();
});
GM_registerMenuCommand("📋 查看通知状态", () => {
const wework = GM_getValue("Notice.wework", "");
const dingding = GM_getValue("Notice.dingding", "");
const feishu = GM_getValue("Notice.feishu", "");
const pushme = GM_getValue("Notice.pushme", "");
const bark = GM_getValue("Notice.bark", "");
let status = "📊 通知接口配置状态:\n\n";
status += `企业微信: ${wework ? "✅ 已配置" : "❌ 未配置"}\n`;
status += `钉钉: ${dingding ? "✅ 已配置" : "❌ 未配置"}\n`;
status += `飞书: ${feishu ? "✅ 已配置" : "❌ 未配置"}\n`;
status += `PushMe: ${pushme ? "✅ 已配置" : "❌ 未配置"}\n`;
status += `Bark: ${bark ? "✅ 已配置" : "❌ 未配置"}\n`;
alert(status);
});
const init = () => {
TaskManager.init();
const isKeep = GM_getValue("Config.keep", true);
const checkDone = (enabled, date) => !enabled || date === RewardsAuto.state.dateNowNum;
const isAllDone = checkDone(GM_getValue("Tasks.sign"), TaskManager.signDate) &&
checkDone(GM_getValue("Tasks.read"), TaskManager.readDate) &&
checkDone(GM_getValue("Tasks.promos"), TaskManager.promosDate) &&
checkDone(GM_getValue("Tasks.search"), TaskManager.searchDate);
if (!isKeep && isAllDone) {
Utils.log("💤", "今日任务已全部完成");
return;
}
const delay = Utils.randomRange(5000, 95000);
Utils.log("⏳", `${delay/1000}秒后启动...`);
setTimeout(() => TaskManager.runAll(), delay);
};
GM_cookie("delete", { url: "https://bing.com", name: "_EDGE_S" });
// ====== 后台模式入口 ======
// 【修复核心】只在非搜索页运行,避免搜索时反复触发
const isBingSearchPage = typeof location !== "undefined"
&& location.hostname
&& (location.hostname === "www.bing.com" || location.hostname === "cn.bing.com");
if (!isBingSearchPage) {
init();
} else {
GM_log("⏭️ 当前为 Bing 搜索页,跳过脚本初始化(防止重复执行/打开多余页面)");
}
})();