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

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();
}
相关推荐
学传打活6 天前
【边打字.边学昆仑正义文化】_进阶篇5 宇宙星系的运转(续3)
微信公众平台·1024程序员节·汉字·昆仑正义文化
学传打活11 天前
【边打字.边学昆仑正义文化】_进阶篇5 宇宙星系的运转(续1)
微信公众平台·1024程序员节·汉字·昆仑正义文化
fangzhanpeng16819 天前
快速掌握C#语言基础知识点(1.基础数据类型)
开发语言·c#·1024程序员节
无己心21 天前
面向多平台 AI 搜索引擎的适配层架构设计
前端·人工智能·搜索引擎·ai·1024程序员节
u0103055271 个月前
AI驱动的安卓UI自动生成功能实现
人工智能·1024程序员节
u0103055271 个月前
长株潭智能体赋能腾讯元器链接共享
1024程序员节
云贝贝贝1 个月前
OceanBase认证体系介绍与备考指南
运维·服务器·oceanbase·1024程序员节
u0103055271 个月前
Java图像处理实战指南
人工智能·1024程序员节
u0103055271 个月前
Java AWT鼠标事件全解析
人工智能·1024程序员节
u0103055271 个月前
Java实用类应用精讲
人工智能·1024程序员节