微软Bing 必应积分自动脚本(Microsoft Bing Rewards Script)

使用Edge搜索,生成高度随机的1500万+搜索词,循环搜索直到达到指定次数。优化UI,包含倒计时。本脚本修改自 yclown 的原始项目。

// ==UserScript==
// @name         微软Bing 必应积分自动脚本(Microsoft Bing Rewards Script)
// @version      1.0.6
// @description  使用Edge搜索,生成高度随机的1500万+搜索词,循环搜索直到达到指定次数。优化UI,包含倒计时。本脚本修改自 yclown 的原始项目。
// @author       BABAlala (原作者 yclown, 修改自其项目 https://github.com/yclown/myTamperMokey)
// @match        https://cn.bing.com/search?*
// @match        https://www.bing.com/search?*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=bing.com
// @require      https://code.jquery.com/jquery-3.1.1.min.js
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @license      GPL-3.0
// @namespace    https://greasyfork.org/users/1413398
// ==/UserScript==

(function() {
    'use strict';

    // 默认配置
    const DEFAULT_CONFIG = {
        max_pc: 40,
        max_ph: 30,
        min_interval: 50,
        max_interval: 120
    };

    // 添加美化样式和最小化、分割线、拖动样式
    GM_addStyle(`
        #reward_tool {
            position: fixed; right: 30px; bottom: 30px; left:auto; top:auto;
            background: #fff; padding: 0; border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1); width: 220px; font-family: sans-serif; z-index: 9999;
            transition: box-shadow 0.2s, opacity 0.2s;
            cursor: default;
            user-select: none;
        }
        #reward_tool .header {
            position: relative;
            height: 36px;
            border-top-left-radius: 8px;
            border-top-right-radius: 8px;
            background: #f7f7f7;
            border-bottom: 1px solid #eee;
            display: flex;
            align-items: center;
            justify-content: flex-end;
            padding: 0 8px 0 0;
            cursor: move;
        }
        #reward_tool .divider {
            position: absolute;
            left: 0; right: 36px; top: 35px;
            height: 1px;
            background: #eee;
        }
        #reward_tool .minimize-btn {
            position: absolute; top: 6px; right: 8px; width: 24px; height: 24px; border: none; background: none; cursor: pointer;
            font-size: 20px; color: #0078d4; z-index: 10001; line-height: 24px; padding: 0;
        }
        #reward_tool .minimize-btn:hover {
            color: #e67e22;
        }
        #reward_tool .panel-content {
            padding: 12px 15px 15px 15px;
        }
        #reward_tool a {
            display: block; margin: 5px 0; padding: 5px; color: #fff; background: #0078d4; border-radius: 4px; text-align: center;
        }
        #reward_tool a:hover { background: #005bb5; }
        #reward_tool p { margin: 5px 0; color: #333; font-size: 12px; }
        #reward_tool .count { font-weight: bold; color: #e74c3c; }
        #reward_tool #reward_info { color: #27ae60; }
        #reward_tool #countdown { color: #e67e22; font-weight: bold; }
        #reward_tool.minimized {
            width: 48px !important; height: 48px !important; padding: 0 !important; background: transparent !important; box-shadow: none !important;
            display: flex; align-items: center; justify-content: center;
            right: 30px !important; bottom: 30px !important; left:auto !important; top:auto !important;
        }
        #reward_tool .mini-icon {
            width: 40px; height: 40px; border-radius: 50%; background: #0078d4; display: flex; align-items: center; justify-content: center;
            color: #fff; font-size: 24px; cursor: pointer; margin: auto;
            box-shadow: 0 2px 8px rgba(0,0,0,0.15);
        }
    `);

    // 初始化界面
    const countInfo = GetConfig();
    document.body.insertAdjacentHTML('beforeend', `
        <div id="reward_tool" style="right:30px; bottom:30px; left:auto; top:auto;">
            <div class="header">
                <button class="minimize-btn" title="最小化" aria-label="最小化">–</button>
            </div>
            <div class="divider"></div>
            <div class="panel-content">
                <a id="reward_finish">结束脚本</a>
                <a id="reward_clean">重置搜索</a>
                <p>电脑: <span class="count" id="pc_count">${countInfo.pc_count}</span> / ${DEFAULT_CONFIG.max_pc}</p>
                <p>手机: <span class="count" id="ph_count">${countInfo.ph_count}</span> / ${DEFAULT_CONFIG.max_ph}</p>
                <p id="reward_info"></p>
                <p>下次搜索: <span id="countdown">--</span></p>
            </div>
            <div class="mini-icon" style="display:none;">B</div>
        </div>
    `);

    // 事件绑定
    $("body").on("click", "#reward_clean", CleanCount);
    $("body").on("click", "#reward_finish", Finish);

    // 最小化与还原逻辑
    const rewardTool = document.getElementById('reward_tool');
    const minimizeBtn = rewardTool.querySelector('.minimize-btn');
    const panelContent = rewardTool.querySelector('.panel-content');
    const header = rewardTool.querySelector('.header');
    const miniIcon = rewardTool.querySelector('.mini-icon');
    const divider = rewardTool.querySelector('.divider');

    // 记录上次正常位置
    let lastNormalPosition = {
        right: rewardTool.style.right,
        bottom: rewardTool.style.bottom,
        left: rewardTool.style.left,
        top: rewardTool.style.top
    };

    minimizeBtn.onclick = function(e) {
        e.stopPropagation();
        // 记录最小化前的位置
        lastNormalPosition = {
            right: rewardTool.style.right,
            bottom: rewardTool.style.bottom,
            left: rewardTool.style.left,
            top: rewardTool.style.top
        };
        rewardTool.classList.add('minimized');
        panelContent.style.display = 'none';
        header.style.display = 'none';
        divider.style.display = 'none';
        miniIcon.style.display = '';
        // 最小化后固定到右下角
        rewardTool.style.right = '30px';
        rewardTool.style.bottom = '30px';
        rewardTool.style.left = 'auto';
        rewardTool.style.top = 'auto';
    };
    miniIcon.onclick = function(e) {
        e.stopPropagation();
        rewardTool.classList.remove('minimized');
        panelContent.style.display = '';
        header.style.display = '';
        divider.style.display = '';
        miniIcon.style.display = 'none';
        // 还原到右下角
        rewardTool.style.right = '30px';
        rewardTool.style.bottom = '30px';
        rewardTool.style.left = 'auto';
        rewardTool.style.top = 'auto';
    };

    // 拖动功能(悬浮框和最小化图标都可拖动)
    let isDragging = false, dragOffsetX = 0, dragOffsetY = 0;
    function dragStart(e) {
        if (e.target.classList.contains('minimize-btn')) return;
        isDragging = true;
        dragOffsetX = e.clientX - rewardTool.getBoundingClientRect().left;
        dragOffsetY = e.clientY - rewardTool.getBoundingClientRect().top;
        rewardTool.style.transition = 'none';
    }
    function dragMove(e) {
        if (!isDragging) return;
        let x = e.clientX - dragOffsetX;
        let y = e.clientY - dragOffsetY;
        // 限制在窗口内
        x = Math.max(0, Math.min(window.innerWidth - rewardTool.offsetWidth, x));
        y = Math.max(0, Math.min(window.innerHeight - rewardTool.offsetHeight, y));
        rewardTool.style.left = x + 'px';
        rewardTool.style.top = y + 'px';
        rewardTool.style.right = 'auto';
        rewardTool.style.bottom = 'auto';
    }
    function dragEnd() {
        isDragging = false;
        rewardTool.style.transition = '';
    }
    header.addEventListener('mousedown', dragStart);
    miniIcon.addEventListener('mousedown', dragStart);
    document.addEventListener('mousemove', dragMove);
    document.addEventListener('mouseup', dragEnd);

    // 主循环
    let timer = null;
    StartSearch();

    function StartSearch() {
        const config = GetConfig();
        if (config.pc_count >= DEFAULT_CONFIG.max_pc && config.ph_count >= DEFAULT_CONFIG.max_ph) {
            ShowInfo(config);
            return;
        }

        const interval = GetRandomInterval();
        let remainingTime = interval / 1000;
        UpdateCountdown(remainingTime);

        timer = setInterval(() => {
            remainingTime--;
            UpdateCountdown(remainingTime);
            if (remainingTime <= 0) {
                clearInterval(timer);
                PerformSearch();
                StartSearch();
            }
        }, 1000);
    }

    function PerformSearch() {
        const config = GetConfig();
        if ((!IsPhone() && config.pc_count >= DEFAULT_CONFIG.max_pc) || (IsPhone() && config.ph_count >= DEFAULT_CONFIG.max_ph)) return;

        try {
            const searchInput = document.getElementById("sb_form_q") || document.querySelector("input[name='q']");
            const searchButton = document.getElementById("sb_form_go") || document.querySelector("button[type='submit']");
            if (!searchInput) throw new Error("搜索框未找到");
            if (!searchButton) throw new Error("搜索按钮未找到");

            searchInput.value = GetRandomSearchTerm();
            if (IsPhone()) config.ph_count++; else config.pc_count++;
            GM_setValue("bing_reward", JSON.stringify(config));
            ShowInfo(config);
            searchButton.click();
        } catch (error) {
            setTimeout(StartSearch, 5000); // 出错后5秒重试
        }
    }

    // 手机检测
    function IsPhone() {
        const userAgent = navigator.userAgent.toLowerCase();
        const isMobileUA = /mobile|android|iphone|ipad|touch/i.test(userAgent);
        const isSmallScreen = window.innerWidth < 768;
        return isMobileUA || isSmallScreen;
    }

    // 获取配置
    function GetConfig() {
        let config = GM_getValue("bing_reward");
        const today = new Date().toISOString().split("T")[0]; // 简化日期处理
        if (!config || JSON.parse(config).date !== today) {
            config = { date: today, pc_count: 0, ph_count: 0 };
        } else {
            config = JSON.parse(config);
        }
        return config;
    }

    // 显示信息
    function ShowInfo(config) {
        document.getElementById("pc_count").textContent = config.pc_count;
        document.getElementById("ph_count").textContent = config.ph_count;
        document.getElementById("reward_info").textContent = config.pc_count < DEFAULT_CONFIG.max_pc || config.ph_count < DEFAULT_CONFIG.max_ph ? '未完成' : '今日已完成';
    }

    // 更新倒计时
    function UpdateCountdown(seconds) {
        document.getElementById("countdown").textContent = seconds > 0 ? `${Math.floor(seconds)}秒` : '搜索中...';
    }

    // 重置计数
    function CleanCount() {
        const today = new Date().toISOString().split("T")[0];
        GM_setValue("bing_reward", JSON.stringify({ date: today, pc_count: 0, ph_count: 0 }));
        alert("计数已重置");
        location.reload();
    }

    // 结束脚本
    function Finish() {
        const today = new Date().toISOString().split("T")[0];
        GM_setValue("bing_reward", JSON.stringify({ date: today, pc_count: DEFAULT_CONFIG.max_pc, ph_count: DEFAULT_CONFIG.max_ph }));
        clearInterval(timer);
        alert("脚本已结束");
    }

    // 随机搜索词生成
    function GetRandomSearchTerm() {
        const topics = ['AI','区块链','云计算','编程','安全','天气','电影','健康','旅游','美食'];
        const actions = ['学习','制作','找到','优化','下载'];
        const qualifiers = ['最新','免费','简单','高效', new Date().getFullYear() + '年'];
        const useQualifier = Math.random() > 0.5;
        const action = actions[Math.floor(Math.random() * actions.length)];
        const topic = topics[Math.floor(Math.random() * topics.length)];
        const qualifier = qualifiers[Math.floor(Math.random() * qualifiers.length)];
        return useQualifier ? `${action} ${qualifier} ${topic}` : `${action} ${topic}`;
    }

    // 随机时间间隔
    function GetRandomInterval() {
        const min = DEFAULT_CONFIG.min_interval * 1000;
        const max = DEFAULT_CONFIG.max_interval * 1000;
        return Math.floor(Math.random() * (max - min + 1)) + min;
    }
})();