// ==UserScript==
// @name Grok Imagine Auto-Advance (Final V3.2)
// @namespace http://tampermonkey.net/
// @version 3.2
// @description FINAL FIX: Corrected logic to ensure the 'Video READY' message remains in the title after successful generation (i.e., when moderation check times out without finding 'Content Moderated').
// @author You
// @match https://grok.com/*
// @grant GM_addStyle
// @run-at document-idle
// ==/UserScript==
(function() {
'use strict';
// --- Configuration & State ---
const CHECK_INTERVAL_MS = 1000;
const MODERATION_CHECK_INTERVAL_MS = 500;
const MODERATION_CHECK_DURATION = 5000;
const FAB_BUTTON_ID = 'grok-fab-toggle';
const NEXT_SVG_PATH = 'M5 11L12 4M12 4L19 11M12 4V21';
// State Variables
let isEnabled = false;
let initialActionTaken = false;
let checkInterval = null;
let moderationInterval = null;
let retryCount = 0;
let moderationCheckStartTime = 0;
let percentIndicatorWasVisible = false;
const originalTitle = document.title;
// --- QoL: Title Management ---
function updateTitle(status) {
if (!isEnabled && status !== 'READY') { // Allow 'READY' state even if isEnabled is false (for success display)
document.title = originalTitle;
return;
}
switch (status) {
case 'LOADING':
document.title = `[🔄 Auto-Retry #${retryCount}] Generation in progress...`;
break;
case 'MODERATION_CHECK':
document.title = `[🔎 Moderation Check #${retryCount}] Waiting...`;
break;
case 'READY':
document.title = `[✅ VIDEO READY] - Click to continue`;
break;
default:
document.title = originalTitle;
}
}
// --- Core Logic Helpers (Unchanged) ---
function findNextButton() {
const pathElement = document.querySelector(`path[d="${NEXT_SVG_PATH}"]`);
if (!pathElement) return null;
let currentElement = pathElement;
while (currentElement && currentElement.tagName !== 'BUTTON') {
currentElement = currentElement.parentElement;
}
return (currentElement && currentElement.tagName === 'BUTTON') ? currentElement : null;
}
function checkIsLoading(nextButton) {
const previousElement = nextButton.previousElementSibling;
if (previousElement && previousElement.tagName === 'BUTTON') {
return previousElement.textContent.includes('%');
}
return false;
}
/** Runs the continuous check for "Content Moderated" text. */
function checkModerationStatus() {
const elapsedTime = Date.now() - moderationCheckStartTime;
const nextButton = findNextButton();
const moderatedElement = Array.from(document.body.querySelectorAll('body *')).find(el => {
return el.textContent.includes('Content Moderated') && el.tagName !== 'SCRIPT' && el.tagName !== 'STYLE';
});
if (moderatedElement && nextButton) {
// CASE 1: MODERATED -> Retry (Success for the automation, failure for the content)
console.log("%c[ACTION] SUCCESS: 'Content Moderated' found. Clicking NEXT button.", 'background: #007700; color: white; padding: 2px;');
updateTitle('READY');
clearInterval(moderationInterval);
moderationInterval = null;
nextButton.click();
startRapidCheck();
return;
}
if (elapsedTime >= MODERATION_CHECK_DURATION) {
// CASE 2: TIMEOUT -> Video generated successfully (Absence of 'Content Moderated' after 5s check)
console.log("%c[ACTION] FINAL SUCCESS: 5 seconds elapsed. 'Content Moderated' NOT found. Video generated successfully.", 'background: #00AA00; color: white; padding: 2px;');
clearInterval(moderationInterval);
moderationInterval = null;
// Stop the feature, but preserve the success status in the title
stopFeature(true);
} else {
updateTitle('MODERATION_CHECK');
}
}
function startModerationCheck() {
console.log(`%c[PHASE 2/3] Loading complete. Starting ${MODERATION_CHECK_DURATION/1000}s active moderation search.`, 'color: orange;');
moderationCheckStartTime = Date.now();
moderationInterval = setInterval(checkModerationStatus, MODERATION_CHECK_INTERVAL_MS);
checkModerationStatus();
}
/** The function run by the 1-second interval (Phase 1: Loading Check). */
function initialLoadingCheck() {
if (!isEnabled) return;
const nextButton = findNextButton();
if (!nextButton) return;
const isLoading = checkIsLoading(nextButton);
if (isLoading) {
percentIndicatorWasVisible = true;
updateTitle('LOADING');
} else {
if (!percentIndicatorWasVisible) {
// Initial wait for '%' to appear after click.
updateTitle('LOADING');
return;
}
// Loading is gone, and we know it was there. -> Generation completed.
if (checkInterval) {
clearInterval(checkInterval);
checkInterval = null;
percentIndicatorWasVisible = false;
startModerationCheck();
}
}
}
function startRapidCheck() {
if (!checkInterval) {
if (initialActionTaken) {
retryCount++;
}
percentIndicatorWasVisible = false;
checkInterval = setInterval(initialLoadingCheck, CHECK_INTERVAL_MS);
initialLoadingCheck();
}
}
// --- Feature Toggling & Action Execution ---
/** Stops the feature. If 'isSuccess' is true, it preserves the 'READY' title. */
function stopFeature(isSuccess) {
isEnabled = false;
const fabButton = document.getElementById(FAB_BUTTON_ID);
if (fabButton) {
fabButton.classList.remove('active');
fabButton.title = 'Click to enable Auto-Advance';
}
// Clear all states and timers
if (checkInterval) {
clearInterval(checkInterval);
checkInterval = null;
}
if (moderationInterval) {
clearInterval(moderationInterval);
moderationInterval = null;
console.log('Moderation check stopped.');
}
// Reset counters and flags
initialActionTaken = false;
percentIndicatorWasVisible = false;
retryCount = 0;
// QoL: Only reset title if it wasn't a success event.
if (isSuccess) {
updateTitle('READY');
} else {
updateTitle(null);
}
}
/** Main function to start/stop the feature. */
function toggleFeature(status) {
if (status) {
// START
isEnabled = true;
const fabButton = document.getElementById(FAB_BUTTON_ID);
if (!fabButton) return;
fabButton.classList.add('active');
fabButton.title = 'Click to disable Auto-Advance';
if (!initialActionTaken) {
const nextButton = findNextButton();
if (nextButton) {
console.log('%c[INITIAL ACTION] First click detected. Starting generation immediately.', 'background: #333; color: yellow;');
nextButton.click();
initialActionTaken = true;
retryCount = 1;
}
}
startRapidCheck();
} else {
// STOP
stopFeature(false);
}
}
// ------------------------------------------------------------------
// --- UI Setup and Initialization (Stability Logic) ---
// ------------------------------------------------------------------
function setupUIAndListeners() {
// --- UI (FAB & Style) ---
GM_addStyle(`
#grok-fab-container { position: fixed; bottom: 20px; right: 20px; z-index: 10000; }
#${FAB_BUTTON_ID} {
width: 56px; height: 56px; border-radius: 50%;
color: white; border: none; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.4);
font-size: 24px; cursor: pointer; display: flex; align-items: center; justify-content: center;
transition: background-color 0.3s, transform 0.2s; outline: none;
background-color: #2ecc71; line-height: 1;
}
#${FAB_BUTTON_ID}:hover { transform: scale(1.05); }
#${FAB_BUTTON_ID}.active { background-color: #e74c3c; }
#${FAB_BUTTON_ID}::before { content: ''; position: absolute; transition: all 0.2s; }
#${FAB_BUTTON_ID}:not(.active)::before {
width: 0; height: 0; border-top: 10px solid transparent; border-bottom: 10px solid transparent;
border-left: 15px solid white; transform: translateX(2px);
}
#${FAB_BUTTON_ID}.active::before { width: 16px; height: 16px; background-color: white; transform: none; }
`);
// Create FAB button
const fabContainer = document.createElement('div');
fabContainer.id = 'grok-fab-container';
const fabButton = document.createElement('button');
fabButton.id = FAB_BUTTON_ID;
fabButton.innerHTML = '';
fabButton.title = 'Click to enable Auto-Advance';
fabContainer.appendChild(fabButton);
if (document.body) {
document.body.appendChild(fabContainer);
} else {
console.error("❌ [INIT V3.2] ERROR: document.body not available.");
return;
}
// --- Event Listener ---
fabButton.addEventListener('click', (e) => {
e.preventDefault();
toggleFeature(!isEnabled);
});
updateTitle(null);
}
// Guaranteed Initialization Logic
if (document.body) {
setupUIAndListeners();
} else {
document.addEventListener('DOMContentLoaded', setupUIAndListeners);
window.addEventListener('load', () => setTimeout(setupUIAndListeners, 500));
}
})();