[油猴脚本] 微软必应奖励每日任务脚本

Microsoft Bing Rewards Daily Task Script

介绍

这是一个自动化脚本,用于自动完成微软 Bing Rewards 的每日搜索任务。脚本支持实时显示任务进度,并根据设备类型(PC 或手机)自动获取热门搜索词。

脚本源码仓库

源码仓库。欢迎提交 Pull Request 来改进此脚本。如果您发现任何问题,请在 Issues 中报告。

功能特点

  • 自动完成每日搜索任务
  • 实时显示任务进度
  • 支持 PC 和手机设备
  • 自动获取热门搜索词
  • 支持随机用户代理和搜索词混淆
  • 开源,不读取Cookie,无隐私安全风险

使用方法

  1. 安装支持用户脚本的浏览器扩展(如 Tampermonkey)。打开开发者模式。
  2. 点击BingRewards脚本连接
  3. 安装脚本。
  4. 新标签页打开Bing网页
  5. 点击Tampermonkey扩展,选择"开始Bing任务",请 耐心 等待脚本执行,您可以在页面上看到任务进度和当前搜索词。

注意事项

  • 请确保您的网络连接正常。
  • 耐心 等待脚本执行。
  • 保持脚本执行标签页始终在 激活 状态。
  • 请勿频繁刷新页面,以免影响任务进度。
  • 如果任务未自动开始,可以手动触发任务启动。
  • 默认每5次搜索后暂停15分钟左右,这是为了防止被微软检测到,可以自调整pauseTime值

V0.0.5

javascript 复制代码
// ==UserScript==
// @name         Microsoft Bing Rewards每日任务脚本
// @version      V0.0.5
// @description  自动完成微软 Rewards 每日搜索任务,实时显示进度
// @author       KEEPA
// @match        https://*.bing.com/*
// @exclude      https://rewards.bing.com/*
// @license      MIT
// @icon         https://www.bing.com/favicon.ico
// @connect      top.baidu.com
// @connect      www.toutiao.com
// @run-at       document-end
// @grant        GM_registerMenuCommand
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_xmlhttpRequest
// @grant        GM_notification
// @grant        GM_log
// @downloadURL  https://gitee.com/idbb98/microsoft-bing-rewards-daily-task-script/raw/master/BingRewards.user.js
// @updateURL    https://gitee.com/idbb98/microsoft-bing-rewards-daily-task-script/raw/master/BingRewards.user.js
// ==/UserScript==

'use strict';

// 配置参数
const CONFIG = {
    maxWebSearches: 40,
    maxMobileSearches: 35,
    pauseInterval: 5,
    pauseTime: 15 * 60 * 1000, // 15分钟
    decimalDelay: 3000,
    minDelay: 8000,
    maxDelay: 15000,
    requestTimeout: 20000
};

// 状态管理
const state = {
    searchWords: [],
    statusPanel: null,
    timers: new Set(),
    isRunning: false,
    searchHistory: [],
    countdownStartTime: 0,
    countdownDuration: 0,
    lastActiveTime: Date.now()
};

// 工具函数
const utils = {
    // 清理所有定时器
    clearAllTimers() {
        state.timers.forEach(timer => {
            clearTimeout(timer);
            clearInterval(timer);
        });
        state.timers.clear();
    },

    // 添加定时器到管理集合
    addTimer(timer) {
        state.timers.add(timer);
        return timer;
    },

    // 生成随机延迟
    getRandomDelay() {
        return Math.random() * (CONFIG.maxDelay - CONFIG.minDelay) + CONFIG.minDelay;
    },

    // 安全JSON解析
    safeJsonParse(str, defaultValue = null) {
        try {
            return JSON.parse(str);
        } catch {
            return defaultValue;
        }
    },

    // 数组洗牌
    shuffleArray(array) {
        return [...array].sort(() => Math.random() - 0.5);
    },

    // 生成随机ID
    generateId() {
        return Date.now().toString(36) + Math.random().toString(36).substr(2, 9);
    },

    // 获取精确的剩余时间(不受标签页激活状态影响)
    getAccurateRemainingTime() {
        if (!state.countdownStartTime || !state.countdownDuration) return 0;

        const elapsed = Date.now() - state.countdownStartTime;
        const remaining = Math.max(0, state.countdownDuration - elapsed);
        return remaining / 1000; // 转换为秒
    },

    // 检查页面是否可见
    isPageVisible() {
        return !document.hidden;
    },

    // 页面可见性变化处理
    handleVisibilityChange(callback) {
        document.addEventListener('visibilitychange', () => {
            state.lastActiveTime = Date.now();
            if (!document.hidden) {
                callback();
            }
        });
    }
};

// 搜索词库
const SEARCH_WORDS = [
    "人工智能发展", "量子计算机", "5G技术应用", "区块链", "物联网",
    "自动驾驶技术", "黑洞研究", "基因编辑", "火星探测", "气候变化",
    "传统文化", "世界遗产", "健康饮食", "运动健身", "旅游景点"
];

// 搜索参数配置
const SEARCH_CONFIG = {
    domains: ['https://www.bing.com', 'https://cn.bing.com'],
    pcParams: ['QBLH', 'QBRE', 'QBRP', 'QBRL', 'QBSB', 'QBVA'],
    mobileParams: ['QBNT', 'QBUS', 'QBIN', 'QBEN'],
    markets: ['zh-CN', 'en-US', 'en-GB', 'ja-JP']
};

/**
 * 创建状态面板
 */
function createStatusPanel() {
    if (state.statusPanel) return state.statusPanel;

    const panel = document.createElement('div');
    panel.id = 'bing-rewards-panel';
    panel.innerHTML = `
        <div style="position:absolute;top:8px;right:10px;cursor:pointer;font-size:18px;" onclick="this.parentElement.style.display='none'">×</div>
        <h3 style="margin:0 0 12px 0;color:#0067b8;font-size:16px;">📈 Bing Rewards</h3>
        <div id="panel-content"></div>
        <div style="margin-top:8px;font-size:11px;color:#999;text-align:center;">
            <span id="page-status">🟢 页面活跃</span>
        </div>
    `;

    Object.assign(panel.style, {
        position: 'fixed',
        bottom: '50px',
        right: '20px',
        background: 'rgba(255,255,255,0.98)',
        border: '1px solid #e1e5e9',
        borderRadius: '12px',
        padding: '15px',
        boxShadow: '0 4px 20px rgba(0,0,0,0.15)',
        zIndex: '10000',
        fontFamily: 'Segoe UI, Arial, sans-serif',
        fontSize: '13px',
        minWidth: '280px',
        backdropFilter: 'blur(10px)'
    });

    document.body.appendChild(panel);
    state.statusPanel = panel;

    // 监听页面可见性变化
    utils.handleVisibilityChange(updateStatusPanel);

    updateStatusPanel();
    return panel;
}

/**
 * 更新状态面板
 */
function updateStatusPanel(data = {}) {
    if (!state.statusPanel) return;

    const taskStatus = getTaskStatus();
    const content = document.getElementById('panel-content');
    const pageStatus = document.getElementById('page-status');
    const { currentWord = '', pauseTimeLeft = null } = data;

    // 更新页面状态指示器
    if (utils.isPageVisible()) {
        pageStatus.textContent = '🟢 页面活跃';
        pageStatus.style.color = '#107c10';
    } else {
        pageStatus.textContent = '⚫ 后台运行';
        pageStatus.style.color = '#666';
    }

    const progress = taskStatus.overallProgress;
    const deviceType = taskStatus.currentType === 'web' ? '💻 PC端' : '📱 移动端';

    // 计算剩余时间(使用精确计时)
    const remainingTime = utils.getAccurateRemainingTime();

    content.innerHTML = `
        <div style="display:flex;justify-content:space-between;margin-bottom:8px;">
            <span style="color:#666;">${deviceType}</span>
            <span style="color:#333;font-weight:500;">${taskStatus.currentCount}/${taskStatus.maxCount}</span>
        </div>
        <div style="margin:10px 0;">
            <div style="display:flex;justify-content:space-between;margin-bottom:4px;font-size:12px;color:#666;">
                <span>总体进度</span>
                <span>${progress}%</span>
            </div>
            <div style="height:8px;background:#f0f0f0;border-radius:4px;">
                <div style="width:${progress}%;height:100%;background:linear-gradient(90deg,#0067b8,#00bcf2);border-radius:4px;transition:width 0.5s;"></div>
            </div>
        </div>
        ${taskStatus.isCompleted ? '<div style="color:#107c10;margin-top:8px;padding:6px;background:#f0f9f0;border-radius:4px;text-align:center;">✅ 今日任务已完成</div>' : ''}
        ${pauseTimeLeft !== null ? `
            <div style="margin-top:10px;padding:8px;background:#fff8e6;border-radius:6px;border-left:4px solid #ffb900;">
                <div style="font-size:12px;color:#8a6900;">⏸️ 任务暂停中</div>
                <div style="font-size:14px;color:#8a6900;font-weight:600;">剩余 ${Math.floor(pauseTimeLeft/60)}分${Math.round(pauseTimeLeft%60)}秒</div>
            </div>
        ` : ''}
        ${!pauseTimeLeft && currentWord && remainingTime > 0 ? `
            <div style="margin-top:10px;padding:8px;background:#f0f7ff;border-radius:6px;">
                <div style="font-size:12px;color:#005a9e;margin-bottom:4px;">🔍 下个搜索词(${remainingTime.toFixed(1)}秒后):</div>
                <div style="font-size:13px;word-break:break-all;color:#0067b8;">${currentWord}</div>
            </div>
        ` : ''}
        ${state.isRunning ? '<div style="margin-top:8px;text-align:center;color:#666;font-size:11px;">🔄 任务执行中...</div>' : ''}
    `;
}

/**
 * 获取热门搜索词
 */
async function fetchSearchKeywords() {
    const isMobile = /Mobile|Android|iPhone/i.test(navigator.userAgent);
    const cacheKey = `cache_${isMobile ? 'mobile' : 'pc'}`;
    const cached = GM_getValue(cacheKey);

    if (cached && Date.now() - cached.time < 3600000) {
        return cached.words;
    }

    const sources = isMobile ? [
        {
            url: "https://www.toutiao.com/hot-event/hot-board/?origin=toutiao_pc",
            parser: data => data.data?.map(item => item.Title?.trim()).filter(Boolean) || []
        }
    ] : [
        {
            url: "https://top.baidu.com/api/board?tab=realtime",
            parser: data => data.data?.cards?.[0]?.content?.map(item => item.word) || []
        }
    ];

    for (const source of sources) {
        try {
            const response = await new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: "GET",
                    url: source.url,
                    timeout: CONFIG.requestTimeout,
                    onload: res => res.status === 200 ? resolve(res.responseText) : reject(new Error(`HTTP ${res.status}`)),
                    onerror: reject,
                    ontimeout: () => reject(new Error("请求超时"))
                });
            });

            const data = utils.safeJsonParse(response, {});
            const words = source.parser(data).filter(word => word && word.length >= 2 && word.length <= 20);

            if (words.length >= 5) {
                GM_setValue(cacheKey, { words, time: Date.now() });
                return words;
            }
        } catch (error) {
            GM_log(`获取热词失败: ${error.message}`);
        }
    }

    return utils.shuffleArray(SEARCH_WORDS);
}

/**
 * 构建搜索URL
 */
function buildSearchUrl(searchWord) {
    const isMobile = /Mobile|Android|iPhone/i.test(navigator.userAgent);
    const params = isMobile ? SEARCH_CONFIG.mobileParams : SEARCH_CONFIG.pcParams;
    const domain = SEARCH_CONFIG.domains[Math.floor(Math.random() * SEARCH_CONFIG.domains.length)];
    const form = params[Math.floor(Math.random() * params.length)];
    const mkt = SEARCH_CONFIG.markets[Math.floor(Math.random() * SEARCH_CONFIG.markets.length)];

    const urlParams = new URLSearchParams({
        q: searchWord,
        form,
        cvid: utils.generateId(),
        pc: isMobile ? 'U03' : 'U01',
        mkt
    });

    return `${domain}/search?${urlParams.toString()}&startTask=1`;
}

/**
 * 获取任务状态
 */
function getTaskStatus() {
    const webCount = GM_getValue('webSearchCount', 0);
    const mobileCount = GM_getValue('mobileSearchCount', 0);
    const isMobile = /Mobile|Android|iPhone/i.test(navigator.userAgent);

    const currentType = isMobile ? 'mobile' : 'web';
    const maxCount = isMobile ? CONFIG.maxMobileSearches : CONFIG.maxWebSearches;
    const currentCount = isMobile ? mobileCount : webCount;
    const totalProgress = ((webCount + mobileCount) / (CONFIG.maxWebSearches + CONFIG.maxMobileSearches)) * 100;

    return {
        currentType,
        currentCount,
        maxCount,
        isCompleted: currentCount >= maxCount,
        overallProgress: Math.round(totalProgress),
        overallProgressRaw: totalProgress
    };
}

/**
 * 执行搜索任务
 */
async function executeSearch() {
    if (state.isRunning) return;
    state.isRunning = true;

    createStatusPanel();
    const taskStatus = getTaskStatus();

    if (taskStatus.isCompleted) {
        updateStatusPanel();
        GM_notification({ text: "Bing Rewards 任务已完成", title: "任务完成", timeout: 3000 });
        state.isRunning = false;
        return;
    }

    // 更新标题
    const title = document.querySelector('title');
    if (title) title.textContent = `[${taskStatus.currentCount}/${taskStatus.maxCount}] Bing任务...`;

    // 滚动页面
    window.scrollTo({ top: document.documentElement.scrollHeight, behavior: 'smooth' });

    // 获取搜索词
    if (state.searchWords.length === 0) {
        try {
            state.searchWords = await fetchSearchKeywords();
        } catch {
            state.searchWords = utils.shuffleArray(SEARCH_WORDS);
        }
    }

    const searchIndex = taskStatus.currentCount % state.searchWords.length;
    const searchWord = state.searchWords[searchIndex];
    const delay = utils.getRandomDelay();

    // 设置精确倒计时
    state.countdownStartTime = Date.now();
    state.countdownDuration = delay;

    // 更新面板
    updateStatusPanel({ currentWord: searchWord });

    // 使用精确计时器,不受页面可见性影响
    const searchTimer = utils.addTimer(setTimeout(() => {
        utils.clearAllTimers();
        performSearch(searchWord, taskStatus);
    }, delay));

    // 添加一个定期更新面板的定时器(每秒更新一次)
    const updateTimer = utils.addTimer(setInterval(() => {
        updateStatusPanel({ currentWord: searchWord });
    }, 1000));
}

/**
 * 执行搜索
 */
function performSearch(searchWord, taskStatus) {
    const nextCount = taskStatus.currentCount + 1;
    const counterKey = taskStatus.currentType === 'web' ? 'webSearchCount' : 'mobileSearchCount';

    GM_setValue(counterKey, nextCount);
    GM_log(`搜索: ${searchWord} (${nextCount}/${taskStatus.maxCount})`);

    // 重置倒计时状态
    state.countdownStartTime = 0;
    state.countdownDuration = 0;

    // 暂停检查
    if (nextCount % CONFIG.pauseInterval === 0) {
        let pauseTimeLeft = CONFIG.pauseTime / 1000;
        updateStatusPanel({ pauseTimeLeft });

        // 使用精确的暂停计时
        const pauseStartTime = Date.now();
        const pauseTimer = utils.addTimer(setInterval(() => {
            const elapsed = Date.now() - pauseStartTime;
            pauseTimeLeft = Math.max(0, (CONFIG.pauseTime - elapsed) / 1000);
            updateStatusPanel({ pauseTimeLeft });

            if (pauseTimeLeft <= 0) {
                utils.clearAllTimers();
                window.location.href = buildSearchUrl(searchWord);
            }
        }, 1000));
    } else {
        window.location.href = buildSearchUrl(searchWord);
    }
}

/**
 * 检查并启动任务
 */
function checkAndStartTask() {
    if (new URLSearchParams(window.location.search).has('startTask')) {
        setTimeout(executeSearch, 2000);
    } else {
        // createStatusPanel();
    }
}

// 注册菜单命令
GM_registerMenuCommand('🚀 开始任务', () => {
    GM_setValue('webSearchCount', 0);
    GM_setValue('mobileSearchCount', 0);
    window.location.href = 'https://www.bing.com/?startTask=1';
});

GM_registerMenuCommand('⏹️ 停止任务', () => {
    const taskStatus = getTaskStatus();
    const counterKey = taskStatus.currentType === 'web' ? 'webSearchCount' : 'mobileSearchCount';
    GM_setValue(counterKey, taskStatus.maxCount);
    utils.clearAllTimers();
    state.isRunning = false;
    state.countdownStartTime = 0;
    state.countdownDuration = 0;
    updateStatusPanel();
});

GM_registerMenuCommand('📊 查看进度', createStatusPanel);

// 启动脚本
if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', checkAndStartTask);
} else {
    checkAndStartTask();
}
相关推荐
金融小师妹1 小时前
AI视角下黄金避风港属性的量化验证:基于2000-2025年历史数据的时序分析
大数据·人工智能·深度学习·1024程序员节
君以思为故3 小时前
认识linux -- 进程控制
linux·运维·1024程序员节
CoderYanger5 小时前
递归、搜索与回溯-记忆化搜索:40.矩阵中的最长递增路径
java·线性代数·算法·leetcode·矩阵·1024程序员节
xcLeigh7 小时前
KingbaseES数据库:首个多院区异构多活容灾架构,浙人医创新开新篇
国产数据库·1024程序员节·kingbasees·金仓数据库
熊文豪7 小时前
首个多院区异构多活容灾架构,浙人医创新开新篇
1024程序员节·kingbase·电科金仓
tg-zm88999615 小时前
2025返利商城源码/挂机自动收益可二开多语言/自定义返利比例/三级分销理财商城
java·mysql·php·laravel·1024程序员节
CoderYanger20 小时前
递归、搜索与回溯-综合练习:19.目标和
java·算法·leetcode·1024程序员节
CoderYanger1 天前
递归、搜索与回溯-综合练习:27.黄金矿工
java·算法·leetcode·深度优先·1024程序员节
CoderYanger1 天前
递归、搜索与回溯-穷举vs暴搜vs深搜vs回溯vs剪枝:13.子集
java·算法·leetcode·机器学习·剪枝·1024程序员节