自動生成影片,生成結果永遠顯示,進度百分比顯示,低於閾值播放音效,高於閾值自動重試,自動模式控制,生成成功播放 Cmaj7 arpeggio。新增登出按鈕,會檢查側邊欄。
当前为
// ==UserScript==
// @name 自動點擊生成 (Grok Imagine) v1.5
// @namespace http://tampermonkey.net/
// @version 1.5
// @description 自動生成影片,生成結果永遠顯示,進度百分比顯示,低於閾值播放音效,高於閾值自動重試,自動模式控制,生成成功播放 Cmaj7 arpeggio。新增登出按鈕,會檢查側邊欄。
// @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 = 30;
let playBeepOnLow = true;
let playBeepOnLimit = true;
let beepVolume = 0.005;
let limitAlertShown = false;
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 playCmaj7Arpeggio(volume = beepVolume, duration = 0.25) {
const ctx = new (window.AudioContext || window.webkitAudioContext)();
const d = 48;
const notes = [16.35*d, 20.6*d, 24.5*d, 30.87*d]; // C, E, G, B
notes.forEach((freq, i) => {
const osc = ctx.createOscillator();
const gain = ctx.createGain();
osc.connect(gain);
gain.connect(ctx.destination);
osc.type = 'triangle';
osc.frequency.value = freq;
gain.gain.setValueAtTime(volume, ctx.currentTime + i*duration); // 音量隨滑桿設定
osc.start(ctx.currentTime + i*duration);
osc.stop(ctx.currentTime + (i+1)*duration);
});
}
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.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);
// 新增登出按鈕
const logoutBtn = document.createElement('button');
logoutBtn.textContent = '🚪 登出';
logoutBtn.style.marginTop = '6px';
logoutBtn.style.background = 'rgba(255,50,50,0.8)';
logoutBtn.style.border = 'none';
logoutBtn.style.color = '#fff';
logoutBtn.style.padding = '6px 10px';
logoutBtn.style.borderRadius = '4px';
logoutBtn.style.cursor = 'pointer';
logoutBtn.style.fontSize = '14px';
logoutBtn.addEventListener('mouseenter', () => logoutBtn.style.opacity = '0.8');
logoutBtn.addEventListener('mouseleave', () => logoutBtn.style.opacity = '1');
logoutBtn.addEventListener('click', tryLogout);
panel.appendChild(logoutBtn);
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;
}
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('額度') || msg.includes('分頁')) entry.style.color = '#ffd966';
else entry.style.color = '#ccc';
entry.textContent = msg;
consoleBox.appendChild(entry);
consoleBox.scrollTop = consoleBox.scrollHeight;
};
}
function check() {
// 額度用完只提示一次
const upgradeElem = document.querySelector('span.text-secondary.font-medium');
if (upgradeElem && upgradeElem.textContent.includes('Upgrade to unlock more')) {
if (!limitAlertShown) {
limitAlertShown = true;
console.log(`${getTimeString()} 額度已用完!`);
if (playBeepOnLimit) beepTriple(440, 0.15);
}
} else {
limitAlertShown = false;
}
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 && autoMode) {
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 container = document.querySelector('div.relative.mx-auto.rounded-2xl.overflow-hidden');
const progress = lastValue !== null ? `${lastValue}%` : "未知";
let success = false;
if (container) {
const grid = container.querySelector('div.grid');
if (grid && grid.querySelector('video#sd-video')) {
console.log(`${getTimeString()} 生成成功 (進度: ${progress})`);
success = true;
playCmaj7Arpeggio(beepVolume); // 套用滑桿音量
}
}
if (!success) {
console.log(`${getTimeString()} 生成失敗 (進度: ${progress})`);
if (lastValue !== null && lastValue < threshold && playBeepOnLow) {
beepTriple();
}
if (autoMode && lastValue !== null && lastValue >= threshold) {
const button = document.querySelector('button[aria-label="製作影片"]');
if (button) {
button.click();
console.log(`${getTimeString()} 自動模式: 已重新點擊生成按鈕`);
} else {
console.log(`${getTimeString()} 找不到生成按鈕,無法自動重新生成`);
}
}
}
lastValue = null;
}
}
function tryLogout() {
const attemptLogout = () => {
const sidebar = document.querySelector('div[data-variant="sidebar"][data-side="left"]');
if (sidebar) {
const state = sidebar.getAttribute('data-state');
if (state === 'expanded') {
const triggerBtn = document.querySelector('button[data-sidebar="trigger"]');
if (triggerBtn) {
triggerBtn.click();
console.log('Sidebar is OPEN → 關閉側邊欄');
setTimeout(attemptLogout, 300);
return;
}
}
}
const trigger = document.querySelector('button[id^="radix-"][aria-haspopup="menu"]');
if (trigger) {
['pointerdown', 'mousedown', 'mouseup', 'pointerup', 'click'].forEach(type => {
trigger.dispatchEvent(new MouseEvent(type, { bubbles: true, cancelable: true, view: window }));
});
setTimeout(() => {
const logoutBtn = [...document.querySelectorAll('div[role="menuitem"]')]
.find(el => el.textContent.includes('登出'));
if (logoutBtn) {
logoutBtn.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, view: window }));
console.log('✅ 已嘗試點擊登出');
} else {
console.log('⚠️ 找不到登出按鈕,1秒後重試');
setTimeout(attemptLogout, 1000);
}
}, 500);
} else {
console.log('❌ 找不到清單觸發按鈕,1秒後重試');
setTimeout(attemptLogout, 1000);
}
};
attemptLogout();
}
const consoleBox = createControlPanel();
hookConsole(consoleBox);
function startBackgroundChecker() {
const workerCode = `
let interval = ${checkInterval};
setInterval(() => postMessage('tick'), interval);
`;
const blob = new Blob([workerCode], { type: 'application/javascript' });
const worker = new Worker(URL.createObjectURL(blob));
worker.onmessage = () => check();
}
startBackgroundChecker();
document.addEventListener('visibilitychange', () => {
if (!document.hidden) check();
});
})();