自動生成影片,閾值警示、音量調整、0%重試、額度用完提醒。
目前為
// ==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);
})();