Greasy Fork is available in English.
MIANKRAM
当前为
// ==UserScript==
// @name Microsoft Rewards 自动获取搜索积分
// @namespace http://tampermonkey.net/
// @version 1.4.3
// @description MIANKRAM
// @author 自动完成Microsoft Rewards每日搜索任务,显示今日积分获取进度,自动计算搜索次数,积分等级1和等级2均可使用
// @match *://*/*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_openInTab
// @grant GM_addStyle
// @grant GM_registerMenuCommand
// @grant GM_addValueChangeListener
// @grant GM_xmlhttpRequest
// @run-at document-end
// @license apache 2.0
// ==/UserScript==
(function () {
'use strict';
if (window.self !== window.top) return;
const CONFIG = {
rewardsUrl: "https://rewards.bing.com/redeem/pointsbreakdown",
searchBaseUrl: "https://www.bing.com/search?q=",
weiboApi: "https://weibo.com/ajax/side/hotSearch",
keys: {
l1: 'rewardsPointsLevel1',
l2: 'rewardsPointsLevel2',
status: 'rewardsGlobalStatus',
trigger: 'rewardsStartSignal',
total: 'rewardsTotalPoints'
},
selectors: {
total: "#rh_rwm > div > span.points-container",
l1_pc: "#userPointsBreakdown > div > div:nth-child(2) > div > div > div > div.pointsDetail > mee-rewards-user-points-details > div > div > div > div > p.pointsDetail.c-subheading-3.ng-binding",
l2_pc: "#userPointsBreakdown > div > div:nth-child(2) > div > div:nth-child(1) > div > div.pointsDetail > mee-rewards-user-points-details > div > div > div > div > p.pointsDetail.c-subheading-3.ng-binding",
l2_mobile: "#userPointsBreakdown > div > div:nth-child(2) > div > div:nth-child(2) > div > div.pointsDetail > mee-rewards-user-points-details > div > div > div > div > p.pointsDetail.c-subheading-3.ng-binding"
}
};
const INSTANCE_ID = Math.random().toString(36).substr(2, 9);
let state = {
isRunning: false,
isRemoteRunning: false,
hotWords: [],
timers: {
search: null,
countdown: null,
slaveCountdown: null,
sync: null,
heartbeat: null
}
};
const FALLBACK_KEYWORDS = [
"2025科技趋势", "Surface新款", "Python教程", "天气预报", "健康食谱",
"股市行情", "新能源车", "ChatGPT技巧", "旅游攻略", "电影推荐",
"SpaceX发射", "AI发展", "量子计算", "亚运会", "华为Mate60",
"油价调整", "社保查询", "个税计算", "考研资料", "公务员考试"
];
if (location.href.startsWith(CONFIG.rewardsUrl)) {
const checkExist = setInterval(function () {
const l1El = document.querySelector(CONFIG.selectors.l1_pc);
const l2PcEl = document.querySelector(CONFIG.selectors.l2_pc);
const l2MobEl = document.querySelector(CONFIG.selectors.l2_mobile);
if (l1El || (l2PcEl && l2MobEl)) {
clearInterval(checkExist);
setTimeout(() => {
const finalL1El = document.querySelector(CONFIG.selectors.l1_pc);
const finalL2PcEl = document.querySelector(CONFIG.selectors.l2_pc);
const finalL2MobEl = document.querySelector(CONFIG.selectors.l2_mobile);
let dataToSave = {};
if (finalL2PcEl && finalL2MobEl) {
dataToSave = {
level: 2,
pc: finalL2PcEl.innerText,
mobile: finalL2MobEl.innerText,
timestamp: Date.now()
};
GM_setValue(CONFIG.keys.l2, dataToSave);
GM_setValue(CONFIG.keys.l1, null);
} else if (finalL1El) {
dataToSave = {
level: 1,
pc: finalL1El.innerText,
timestamp: Date.now()
};
GM_setValue(CONFIG.keys.l1, dataToSave);
GM_setValue(CONFIG.keys.l2, null);
}
window.close();
}, 5000);
}
}, 500);
setTimeout(() => clearInterval(checkExist), 20000);
return;
}
function updateTotalPointsFromBing() {
if (!location.hostname.includes('bing.com')) return;
const totalEl = document.querySelector(CONFIG.selectors.total);
if (totalEl) {
const pointsText = totalEl.innerText.trim();
if (pointsText !== "" && pointsText.match(/^\d+$/)) {
GM_setValue(CONFIG.keys.total, {
points: pointsText,
timestamp: Date.now()
});
console.log(`[Rewards] 总积分更新: ${pointsText}`);
updateUI();
}
}
}
function fetchHotSearchKeywords() {
GM_xmlhttpRequest({
method: "GET",
url: CONFIG.weiboApi,
onload: function (response) {
try {
const json = JSON.parse(response.responseText);
if (json?.data?.realtime) {
const words = json.data.realtime.map(item => item.word_scheme || item.word);
state.hotWords = [...new Set([...words, ...FALLBACK_KEYWORDS])].sort(() => Math.random() - 0.5);
} else throw new Error();
} catch (e) { state.hotWords = [...FALLBACK_KEYWORDS].sort(() => Math.random() - 0.5); }
},
onerror: function () { state.hotWords = [...FALLBACK_KEYWORDS].sort(() => Math.random() - 0.5); }
});
}
function getNextKeyword() {
if (!state.hotWords || state.hotWords.length === 0) state.hotWords = [...FALLBACK_KEYWORDS].sort(() => Math.random() - 0.5);
return state.hotWords.shift();
}
function parsePoints(str) {
if (!str) return { current: 0, max: 0, remaining: 0, percent: 0 };
const parts = str.split('/');
if (parts.length !== 2) return { current: 0, max: 0, remaining: 0, percent: 0 };
const current = parseInt(parts[0].replace(/[^0-9]/g, '')) || 0;
const max = parseInt(parts[1].replace(/[^0-9]/g, '')) || 0;
return { current, max, remaining: Math.ceil((max - current) / 3), percent: max > 0 ? (current / max) * 100 : 0 };
}
function getRemainingTasks() {
const l1 = GM_getValue(CONFIG.keys.l1);
const l2 = GM_getValue(CONFIG.keys.l2);
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
if (l2) {
if (isMobile) {
return parsePoints(l2.mobile).remaining;
} else {
return parsePoints(l2.pc).remaining;
}
} else if (l1) {
return parsePoints(l1.pc).remaining;
}
return 0;
}
function performSearch() {
if (!location.hostname.includes('bing.com')) return;
updateTotalPointsFromBing();
const keyword = getNextKeyword();
const sbInput = document.getElementById('sb_form_q');
const sbForm = document.getElementById('sb_form');
const sbGo = document.getElementById('sb_form_go') || document.getElementById('search_icon');
if (sbInput) {
let lastValue = sbInput.value;
sbInput.value = keyword;
const tracker = sbInput._valueTracker;
if (tracker) tracker.setValue(lastValue);
sbInput.dispatchEvent(new Event('input', { bubbles: true }));
sbInput.dispatchEvent(new Event('change', { bubbles: true }));
const titleEl = document.querySelector('.neon-title');
if (titleEl) {
titleEl.innerText = `搜: ${keyword.substring(0, 6)}...`;
setTimeout(() => titleEl.innerText = "Microsoft Rewards", 5000);
}
setTimeout(() => {
if (sbGo) sbGo.click();
else if (sbForm) sbForm.submit();
}, 300 + Math.random() * 200);
}
}
function startLoop() {
if (state.isRunning || state.isRemoteRunning) return;
if (!location.hostname.includes('bing.com')) {
console.log("[Rewards] 启动器模式:发送启动信号并打开新标签页...");
GM_setValue(CONFIG.keys.trigger, Date.now());
if (state.hotWords.length === 0) fetchHotSearchKeywords();
const keyword = getNextKeyword();
const url = CONFIG.searchBaseUrl + encodeURIComponent(keyword);
GM_openInTab(url, { active: true, insert: true });
state.isRemoteRunning = true;
togglePanel(true);
updateUI();
return;
}
console.log("[Rewards] 工作模式:开始执行任务循环");
state.isRunning = true;
updateGlobalStatus(true, Date.now() + 60000);
togglePanel(true);
updateUI();
if (state.hotWords.length === 0) fetchHotSearchKeywords();
performSearch();
updateRemainingDisplay(getRemainingTasks() - 1);
if (state.timers.sync) clearInterval(state.timers.sync);
state.timers.sync = setInterval(() => {
GM_openInTab(CONFIG.rewardsUrl, { active: false, insert: true });
}, 30000);
const schedule = () => {
if (!state.isRunning) return;
const remaining = getRemainingTasks();
if (remaining <= 0) { stopLoop(true); return; }
const delay = Math.floor(Math.random() * (120 - 80 + 1)) + 80;
const targetTime = Date.now() + (delay * 1000);
updateGlobalStatus(true, targetTime);
if (state.timers.heartbeat) clearInterval(state.timers.heartbeat);
state.timers.heartbeat = setInterval(() => { updateGlobalStatus(true, targetTime); }, 5000);
if (state.timers.countdown) clearInterval(state.timers.countdown);
state.timers.countdown = setInterval(() => {
const left = Math.ceil((targetTime - Date.now()) / 1000);
updateTimerDisplay(left);
if (Date.now() >= targetTime) {
clearInterval(state.timers.countdown);
if (state.isRunning) {
performSearch();
schedule();
}
}
}, 1000);
};
schedule();
}
function updateGlobalStatus(isRunning, nextSearchTime = 0) {
GM_setValue(CONFIG.keys.status, {
isRunning: isRunning,
owner: isRunning ? INSTANCE_ID : null,
timestamp: Date.now(),
nextSearchTime: nextSearchTime
});
}
function stopLoop(completed = false) {
console.log("[Rewards] 停止任务");
state.isRunning = false;
const currentStatus = GM_getValue(CONFIG.keys.status);
if (currentStatus && currentStatus.owner === INSTANCE_ID) {
updateGlobalStatus(false);
} else if (!completed) {
updateGlobalStatus(false);
}
if (state.timers.countdown) clearInterval(state.timers.countdown);
if (state.timers.sync) clearInterval(state.timers.sync);
if (state.timers.heartbeat) clearInterval(state.timers.heartbeat);
updateTimerDisplay(completed ? "完成" : "已停止");
updateUI(completed);
}
window.addEventListener('beforeunload', () => { });
GM_addValueChangeListener(CONFIG.keys.status, (name, oldVal, newVal, remote) => {
if (newVal && newVal.isRunning) {
if (newVal.owner !== INSTANCE_ID) {
if (state.isRunning) stopLoop(false);
if (!state.isRemoteRunning) togglePanel(true);
state.isRemoteRunning = true;
if (newVal.nextSearchTime) startSlaveCountdown(newVal.nextSearchTime);
updateUI();
}
} else {
state.isRemoteRunning = false;
if (state.isRunning) stopLoop(false);
stopSlaveCountdown();
updateUI();
}
});
function startSlaveCountdown(targetTime) {
if (state.timers.slaveCountdown) clearInterval(state.timers.slaveCountdown);
const updateSlaveTimer = () => {
const left = Math.ceil((targetTime - Date.now()) / 1000);
updateTimerDisplay(left > 0 ? left : 0);
};
updateSlaveTimer();
state.timers.slaveCountdown = setInterval(updateSlaveTimer, 1000);
}
function stopSlaveCountdown() {
if (state.timers.slaveCountdown) clearInterval(state.timers.slaveCountdown);
updateTimerDisplay("待机");
}
function updateTimerDisplay(val) {
const el = document.getElementById('neon-timer');
if (el) el.innerText = typeof val === 'number' ? `${val}s` : val;
}
function updateRemainingDisplay(val) {
const el = document.getElementById('neon-remaining');
if (el) el.innerText = val > 0 ? val : 0;
}
function togglePanel(show) {
const panel = document.getElementById('neon-panel');
if (panel) {
if (show) panel.classList.add('show');
else panel.classList.remove('show');
}
}
GM_addStyle(`
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600;800&display=swap');
:root { --neon-bg: rgba(20, 20, 25, 0.75); --neon-border: rgba(255, 255, 255, 0.1); --neon-primary: #00f2ea; --neon-secondary: #ff0055; --neon-text: #ffffff; --glass-blur: blur(16px); }
#neon-fab { position: fixed; bottom: 30px; right: 30px; width: 50px; height: 50px; background: var(--neon-bg); backdrop-filter: var(--glass-blur); border: 1px solid var(--neon-border); border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: pointer; z-index: 10000; box-shadow: 0 8px 32px rgba(0,0,0,0.3); transition: all 0.3s; }
#neon-fab:hover { transform: scale(1.1); border-color: var(--neon-primary); box-shadow: 0 0 15px rgba(0, 242, 234, 0.3); }
#neon-fab svg { width: 24px; height: 24px; fill: var(--neon-primary); }
#neon-panel { position: fixed; bottom: 90px; right: 30px; width: 300px; background: var(--neon-bg); backdrop-filter: var(--glass-blur); border: 1px solid var(--neon-border); border-radius: 16px; padding: 20px; z-index: 10000; font-family: 'Inter', sans-serif; color: var(--neon-text); box-shadow: 0 16px 48px rgba(0,0,0,0.4); opacity: 0; visibility: hidden; transform: translateY(20px) scale(0.95); transition: all 0.3s; pointer-events: none; }
#neon-panel.show { opacity: 1; visibility: visible; transform: translateY(0) scale(1); pointer-events: auto; }
.neon-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; border-bottom: 1px solid rgba(255,255,255,0.05); padding-bottom: 10px; }
.neon-title { font-weight: 800; font-size: 14px; background: linear-gradient(90deg, #fff, #bbb); -webkit-background-clip: text; -webkit-text-fill-color: transparent; max-width: 160px; }
.neon-controls { display: flex; align-items: center; gap: 10px; }
.neon-refresh { font-size: 12px; color: #a0a0a0; cursor: pointer; transition: color 0.2s; }
.neon-refresh:hover { color: var(--neon-primary); }
.neon-close { font-size: 20px; color: #a0a0a0; cursor: pointer; line-height: 1; transition: color 0.2s; font-family: sans-serif; }
.neon-close:hover { color: var(--neon-secondary); }
.neon-row { margin-bottom: 12px; }
.neon-label-row { display: flex; justify-content: space-between; font-size: 12px; color: #a0a0a0; margin-bottom: 4px; }
.neon-progress-bg { width: 100%; height: 4px; background: rgba(255,255,255,0.05); border-radius: 2px; overflow: hidden; }
.neon-progress-bar { height: 100%; background: linear-gradient(90deg, var(--neon-primary), #00c2ff); width: 0%; transition: width 0.6s ease; box-shadow: 0 0 8px rgba(0, 242, 234, 0.4); }
.neon-dashboard { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 20px; }
.neon-card { background: rgba(255,255,255,0.03); border-radius: 12px; padding: 10px; text-align: center; position: relative; }
.neon-card.wide { grid-column: span 2; display: flex; align-items: center; justify-content: space-between; padding: 10px 20px; }
.neon-card.wide .neon-card-val { margin-top: 0; font-size: 24px; color: #ffeb3b; text-shadow: 0 0 10px rgba(255, 235, 59, 0.3); }
.neon-card-label { font-size: 11px; color: #a0a0a0; text-transform: uppercase; letter-spacing: 1px; }
.neon-card-val { font-size: 20px; font-weight: 800; margin-top: 5px; color: var(--neon-primary); }
#neon-action-btn { width: 100%; margin-top: 20px; padding: 12px; background: var(--neon-primary); color: #000; font-weight: 800; border: none; border-radius: 8px; cursor: pointer; transition: all 0.2s; text-transform: uppercase; font-size: 12px; letter-spacing: 1px; }
#neon-action-btn:hover { box-shadow: 0 0 20px rgba(0, 242, 234, 0.5); transform: translateY(-1px); }
#neon-action-btn:disabled { background: #333; color: #666; cursor: not-allowed; box-shadow: none; transform: none; }
.neon-pulse { position: absolute; top: 5px; right: 5px; width: 8px; height: 8px; border-radius: 50%; background: var(--neon-secondary); opacity: 0.2; transition: all 0.3s; }
.neon-pulse.active { opacity: 1; box-shadow: 0 0 10px var(--neon-secondary); }
`);
function createUI() {
if (document.getElementById('neon-fab')) return;
const fab = document.createElement('div');
fab.id = 'neon-fab';
fab.innerHTML = `<svg viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/></svg>`;
document.body.appendChild(fab);
const panel = document.createElement('div');
panel.id = 'neon-panel';
panel.innerHTML = `
<div class="neon-header">
<span class="neon-title">Microsoft Rewards</span>
<div class="neon-controls">
<span class="neon-refresh" id="neon-refresh" title="同步数据">手动同步</span>
<span class="neon-close" id="neon-close" title="隐藏面板">×</span>
</div>
</div>
<div id="neon-content"><div style="text-align:center; color:#666; font-size:12px; padding:20px;">正在初始化...</div></div>
<div class="neon-dashboard">
<div class="neon-card wide">
<div class="neon-card-label">总积分 BALANCE</div>
<div class="neon-card-val" id="neon-total-points">--</div>
</div>
<div class="neon-card"><div class="neon-pulse"></div><div class="neon-card-label">本设备剩余</div><div class="neon-card-val" id="neon-remaining">--</div></div>
<div class="neon-card"><div class="neon-card-label">下次搜索</div><div class="neon-card-val" id="neon-timer" style="color:#fff;">--</div></div>
</div>
<button id="neon-action-btn">开始运行</button>
`;
document.body.appendChild(panel);
fab.addEventListener('click', () => panel.classList.toggle('show'));
document.getElementById('neon-refresh').addEventListener('click', () => {
GM_openInTab(CONFIG.rewardsUrl, { active: false, insert: true });
});
document.getElementById('neon-close').addEventListener('click', (e) => {
e.stopPropagation();
panel.classList.remove('show');
});
document.addEventListener('click', (e) => {
if (!panel.contains(e.target) && !fab.contains(e.target) && panel.classList.contains('show')) panel.classList.remove('show');
});
updateUI();
}
function updateUI(isCompleted = false) {
const container = document.getElementById('neon-content');
if (!container) return;
const l1 = GM_getValue(CONFIG.keys.l1);
const l2 = GM_getValue(CONFIG.keys.l2);
const totalPointsData = GM_getValue(CONFIG.keys.total);
const remaining = getRemainingTasks();
const remainingEl = document.getElementById('neon-remaining');
if (remainingEl) remainingEl.innerText = remaining;
const totalEl = document.getElementById('neon-total-points');
if (totalEl) {
let totalTxt = "--";
if (totalPointsData && totalPointsData.points) totalTxt = totalPointsData.points;
totalEl.innerText = totalTxt;
}
let html = '';
if (l2) {
const pc = parsePoints(l2.pc);
const mob = parsePoints(l2.mobile);
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
const pcStyle = !isMobile ? 'color:#fff; font-weight:bold;' : 'color:#a0a0a0;';
const mobStyle = isMobile ? 'color:#fff; font-weight:bold;' : 'color:#a0a0a0;';
html += `
<div class="neon-row"><div class="neon-label-row"><span style="${pcStyle}">PC 端 ${!isMobile ? '(当前)' : ''}</span> <span>${l2.pc}</span></div><div class="neon-progress-bg"><div class="neon-progress-bar" style="width:${pc.percent}%"></div></div></div>
<div class="neon-row"><div class="neon-label-row"><span style="${mobStyle}">移动端 ${isMobile ? '(当前)' : ''}</span> <span>${l2.mobile}</span></div><div class="neon-progress-bg"><div class="neon-progress-bar" style="width:${mob.percent}%; background: linear-gradient(90deg, #ff0055, #ff5e00);"></div></div></div>
`;
} else if (l1) {
const pc = parsePoints(l1.pc);
html += `<div class="neon-row"><div class="neon-label-row"><span>积分进度</span> <span>${l1.pc}</span></div><div class="neon-progress-bg"><div class="neon-progress-bar" style="width:${pc.percent}%"></div></div></div>`;
} else {
html = `<div style="text-align:center; color:#aaa; font-size:12px; padding:10px;">暂无 PC/移动端积分数据 (点击手动同步)</div>`;
}
container.innerHTML = html;
const btn = document.getElementById('neon-action-btn');
const newBtn = btn.cloneNode(true);
btn.parentNode.replaceChild(newBtn, btn);
if (state.isRunning) {
newBtn.innerText = "停止运行";
newBtn.disabled = false;
newBtn.style.opacity = "1";
newBtn.style.background = "var(--neon-secondary)";
newBtn.style.color = "#fff";
newBtn.onclick = () => stopLoop(false);
} else if (state.isRemoteRunning) {
newBtn.innerText = "停止 (后台运行中)";
newBtn.disabled = false;
newBtn.style.opacity = "1";
newBtn.style.background = "#555";
newBtn.onclick = () => stopLoop(false);
} else if (isCompleted || (remaining <= 0 && (l1 || l2))) {
newBtn.innerText = "当前设备任务完成";
newBtn.disabled = true;
newBtn.style.background = "rgba(255,255,255,0.1)";
newBtn.style.color = "#aaa";
} else {
newBtn.innerText = "开始运行";
newBtn.disabled = false;
newBtn.style.opacity = "1";
newBtn.style.background = "var(--neon-primary)";
newBtn.style.color = "#000";
newBtn.onclick = startLoop;
}
}
GM_addValueChangeListener(CONFIG.keys.l1, () => updateUI());
GM_addValueChangeListener(CONFIG.keys.l2, () => updateUI());
GM_addValueChangeListener(CONFIG.keys.total, () => updateUI());
window.addEventListener('load', () => {
createUI();
fetchHotSearchKeywords();
if (location.hostname.includes('bing.com')) {
setTimeout(updateTotalPointsFromBing, 2000);
}
const triggerTime = GM_getValue(CONFIG.keys.trigger);
if (triggerTime && (Date.now() - triggerTime < 15000)) {
if (location.hostname.includes("bing.com")) {
console.log("[Rewards] 检测到启动信号,本页面接管任务...");
GM_setValue(CONFIG.keys.trigger, 0);
setTimeout(startLoop, 1000);
return;
}
}
const currentStatus = GM_getValue(CONFIG.keys.status);
if (currentStatus && currentStatus.isRunning && currentStatus.owner !== INSTANCE_ID) {
if (Date.now() - currentStatus.timestamp < 180000) {
state.isRemoteRunning = true;
if (currentStatus.nextSearchTime) startSlaveCountdown(currentStatus.nextSearchTime);
updateUI();
} else {
updateGlobalStatus(false);
}
}
setTimeout(() => {
if (!GM_getValue(CONFIG.keys.l1) && !GM_getValue(CONFIG.keys.l2)) {
GM_openInTab(CONFIG.rewardsUrl, { active: false, insert: true });
}
}, 1500);
});
})();