自動點擊生成 (Grok Imagine) v1.3

自動生成影片,閾值警示、音量調整、0%重試、額度用完提醒。

目前為 2025-10-29 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         自動點擊生成 (Grok Imagine) v1.3
// @namespace    http://tampermonkey.net/
// @version      1.3
// @description  自動生成影片,閾值警示、音量調整、0%重試、額度用完提醒。
// @match        https://grok.com/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    const targetClassString = 'text-xs font-semibold w-[4ch] mb-[1px]';
    const selector = `div[class="${targetClassString}"]`;

    let lastValue = null;
    let wasPresent = false;
    let autoMode = true;
    let threshold = 50;
    let playBeepOnLow = true;
    let playBeepOnLimit = true; // 🔹 額度用完提醒開關
    let beepVolume = 0.005;

    // 0% 檢測
    let zeroCount = 0;
    const zeroThreshold = 20;
    const checkInterval = 500;
    const zeroMaxCount = zeroThreshold * 1000 / checkInterval;

    function getTimeString() {
        const now = new Date();
        const pad = n => n.toString().padStart(2, '0');
        return `[${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}]`;
    }

    function parseNumber(text) {
        if (!text) return null;
        const match = text.match(/-?\d+(\.\d+)?/);
        return match ? parseFloat(match[0]) : null;
    }

    // 三段短促蜂鳴音效
    function beepTriple(frequency = 880, duration = 0.1, volume = beepVolume) {
        const ctx = new (window.AudioContext || window.webkitAudioContext)();
        const playBeep = () => {
            const oscillator = ctx.createOscillator();
            const gain = ctx.createGain();
            oscillator.connect(gain);
            gain.connect(ctx.destination);
            oscillator.type = 'square';
            oscillator.frequency.value = frequency;
            gain.gain.value = volume;
            oscillator.start();
            oscillator.stop(ctx.currentTime + duration);
        };
        playBeep();
        setTimeout(playBeep, duration * 1000 + 100);
        setTimeout(playBeep, 2 * (duration * 1000 + 100));
    }

    // 建立控制面板
    function createControlPanel() {
        const panel = document.createElement('div');
        panel.style.position = 'fixed';
        panel.style.top = '80px';
        panel.style.right = '10px';
        panel.style.zIndex = '99999';
        panel.style.background = 'rgba(30,30,30,0.9)';
        panel.style.color = '#fff';
        panel.style.padding = '10px 15px';
        panel.style.borderRadius = '10px';
        panel.style.fontSize = '14px';
        panel.style.fontFamily = 'monospace';
        panel.style.boxShadow = '0 4px 12px rgba(0,0,0,0.6)';
        panel.style.backdropFilter = 'blur(5px)';
        panel.style.width = '320px';
        panel.style.maxHeight = '480px';
        panel.style.overflow = 'hidden';
        panel.style.display = 'flex';
        panel.style.flexDirection = 'column';
        panel.style.pointerEvents = 'auto';

        const controlsHTML = `
            <div style="margin-bottom:6px; font-weight:bold;">Grok 檢查控制</div>
            <label style="display:flex; align-items:center; gap:6px; margin-bottom:4px;">
                <input type="checkbox" id="autoModeToggle" checked />
                自動模式
            </label>
            <label style="display:flex; align-items:center; gap:6px; margin-bottom:4px;">
                閥值(%):
                <input type="number" id="thresholdInput" value="${threshold}" min="0" max="100" step="1"
                       style="width:60px; padding:2px; border-radius:4px; border:none; text-align:center;">
            </label>
            <label style="display:flex; align-items:center; gap:6px; margin-bottom:4px;">
                <input type="checkbox" id="beepToggle" checked />
                低於閾值播放音效
            </label>
            <label style="display:flex; align-items:center; gap:6px; margin-bottom:4px;">
                <input type="checkbox" id="limitToggle" checked />
                額度用完提醒音效
            </label>
            <label style="display:flex; align-items:center; gap:6px; margin-bottom:6px;">
                音量:
                <input type="range" id="volumeSlider" min="0" max="0.05" step="0.005" value="${beepVolume}" style="flex:1;">
                <span id="volumeDisplay">${beepVolume.toFixed(3)}</span>
            </label>
        `;

        const consoleBox = document.createElement('div');
        consoleBox.id = 'grok-console';
        consoleBox.style.flex = '1';
        consoleBox.style.background = 'rgba(0,0,0,0.3)';
        consoleBox.style.padding = '6px';
        consoleBox.style.borderRadius = '6px';
        consoleBox.style.overflowY = 'auto';
        consoleBox.style.fontSize = '12px';
        consoleBox.style.lineHeight = '1.4';
        consoleBox.style.whiteSpace = 'pre-wrap';

        const clearBtn = document.createElement('button');
        clearBtn.textContent = '🧹 清空紀錄';
        clearBtn.style.marginTop = '6px';
        clearBtn.style.background = 'rgba(255,255,255,0.1)';
        clearBtn.style.border = 'none';
        clearBtn.style.color = '#ccc';
        clearBtn.style.padding = '4px 8px';
        clearBtn.style.borderRadius = '4px';
        clearBtn.style.cursor = 'pointer';
        clearBtn.style.fontSize = '12px';
        clearBtn.addEventListener('mouseenter', () => clearBtn.style.color = '#fff');
        clearBtn.addEventListener('mouseleave', () => clearBtn.style.color = '#ccc');
        clearBtn.addEventListener('click', () => (consoleBox.innerHTML = ''));

        panel.innerHTML = controlsHTML;
        panel.appendChild(consoleBox);
        panel.appendChild(clearBtn);
        document.body.appendChild(panel);

        const autoModeToggle = panel.querySelector('#autoModeToggle');
        const thresholdInput = panel.querySelector('#thresholdInput');
        const beepToggle = panel.querySelector('#beepToggle');
        const limitToggle = panel.querySelector('#limitToggle'); // 🔹 額度提醒勾選盒
        const volumeSlider = panel.querySelector('#volumeSlider');
        const volumeDisplay = panel.querySelector('#volumeDisplay');

        autoModeToggle.addEventListener('change', () => {
            autoMode = autoModeToggle.checked;
            console.log(`${getTimeString()} 自動模式: ${autoMode ? '啟用' : '停用'}`);
        });
        thresholdInput.addEventListener('change', () => {
            threshold = parseFloat(thresholdInput.value);
            console.log(`${getTimeString()} 閥值更新為 ${threshold}`);
        });
        beepToggle.addEventListener('change', () => {
            playBeepOnLow = beepToggle.checked;
            console.log(`${getTimeString()} 低於閾值播放音效: ${playBeepOnLow ? '啟用' : '停用'}`);
        });
        limitToggle.addEventListener('change', () => { // 🔹 額度提醒
            playBeepOnLimit = limitToggle.checked;
            console.log(`${getTimeString()} 額度用完提醒音效: ${playBeepOnLimit ? '啟用' : '停用'}`);
        });
        volumeSlider.addEventListener('input', () => {
            beepVolume = parseFloat(volumeSlider.value);
            volumeDisplay.textContent = beepVolume.toFixed(3);
        });

        return consoleBox;
    }

    // 攔截 console.log
    function hookConsole(consoleBox) {
        const originalLog = console.log;
        console.log = (...args) => {
            originalLog.apply(console, args);
            const msg = args.join(' ');
            const entry = document.createElement('div');
            if (msg.includes('成功')) entry.style.color = '#6eff9f';
            else if (msg.includes('失敗')) entry.style.color = '#ff7b7b';
            else if (msg.includes('閥值') || msg.includes('模式') || msg.includes('額度')) entry.style.color = '#ffd966';
            else entry.style.color = '#ccc';
            entry.textContent = msg;
            consoleBox.appendChild(entry);
            consoleBox.scrollTop = consoleBox.scrollHeight;
        };
    }

    // === 主邏輯 ===
    function check() {
        if (!autoMode) return;

        // 🔹 額度用完檢測
        const upgradeElem = document.querySelector('span.text-secondary.font-medium');
        if (upgradeElem && upgradeElem.textContent.includes('Upgrade to unlock more')) {
            console.log(`${getTimeString()} 額度已用完!`);
            if (playBeepOnLimit) beepTriple(440, 0.15); // 低頻蜂鳴提示
        }

        const elem = document.querySelector(selector);
        if (elem) {
            wasPresent = true;
            const val = parseNumber(elem.textContent.trim());
            if (!isNaN(val)) lastValue = val;

            if (val === 0) {
                zeroCount++;
                if (zeroCount >= zeroMaxCount) {
                    const button = document.querySelector('button[aria-label="製作影片"]');
                    if (button) {
                        button.click();
                        console.log(`${getTimeString()} 進度長時間為0,已再次點擊生成按鈕`);
                    } else {
                        console.log(`${getTimeString()} 找不到生成按鈕,無法重點擊`);
                    }
                    zeroCount = 0;
                }
            } else zeroCount = 0;

        } else if (wasPresent) {
            wasPresent = false;
            const progress = lastValue !== null ? `${lastValue}%` : "未知";
            const container = document.querySelector('div.relative.mx-auto.rounded-2xl.overflow-hidden');
            let success = false;

            if (container) {
                const grid = container.querySelector('div.grid');
                if (grid && grid.querySelector('video#sd-video')) {
                    console.log(`${getTimeString()} 生成成功 (進度:${progress})`);
                    success = true;
                }
            }

            if (!success) {
                if (lastValue !== null && lastValue >= threshold) {
                    console.log(`${getTimeString()} 生成失敗 (進度:${progress})`);
                    const button = document.querySelector('button[aria-label="製作影片"]');
                    if (button) {
                        const section = document.querySelector('section[aria-label="Notifications alt+T"]');
                        let attempts = 0;
                        const maxAttempts = 40;
                        const intervalId = setInterval(() => {
                            const exists = section && section.querySelector('span')?.textContent.trim() === "Content Moderated. Try a different idea.";
                            attempts++;
                            if (exists) {
                                button.click();
                                console.log(`${getTimeString()} 已點擊製作影片按鈕`);
                                clearInterval(intervalId);
                            } else if (attempts >= maxAttempts) {
                                console.log(`${getTimeString()} 失敗訊息超時仍未出現`);
                                clearInterval(intervalId);
                                button.click();
                            }
                        }, 500);
                    } else {
                        console.log(`${getTimeString()} 找不到製作影片按鈕`);
                    }
                } else {
                    console.log(`${getTimeString()} 生成失敗且低於閾值 (進度:${progress})`);
                    if (playBeepOnLow) beepTriple();
                }
            }
            lastValue = null;
        }
    }

    const consoleBox = createControlPanel();
    hookConsole(consoleBox);
    setInterval(check, checkInterval);
})();