您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Simple polling-based bot
// ==UserScript== // @name Ovipets Credit Bot (with many other ovipets hack/cheat options) // @namespace http://tampermonkey.net/ // @version 0.1 // @description Simple polling-based bot // @match *://*.ovipets.com/* // @grant GM_xmlhttpRequest // @connect 127.0.0.1 // @connect localhost // @connect im*.ovipets.com // @connect ovipets.com // @license MIT // ==/UserScript== (function () { "use strict"; const API_BASE = "http://127.0.0.1:8000"; const RUN_KEY = "ovipets_bot_running"; const CLASS_NAMES_EVIL = [ "Canis", "Draconis", "Equus", "Feline", "Gekko", "Lupus", "Mantis", "Raptor", "Slime", "Vulpes", ]; const CLASS_NAMES = ["Turn Egg"]; let pollInterval = null; let eggQueue = []; let isProcessing = false; let isPuzzleSolving = false; let isEvil = false; let isPeaceful = false; let accum = 0; function saveState() { // Save important state before reload sessionStorage.setItem("ovipets_bot_evil_mode", JSON.stringify(isEvil)); sessionStorage.setItem( "ovipets_bot_peaceful_mode", JSON.stringify(isPeaceful) ); sessionStorage.setItem("ovipets_bot_queue", JSON.stringify(eggQueue)); sessionStorage.setItem("ovipets_bot_accumulator", JSON.stringify(accum)); sessionStorage.setItem( "ovipets_bot_is_processing", JSON.stringify(isProcessing) ); sessionStorage.setItem( "ovipets_bot_is_puzzle_solving", JSON.stringify(isPuzzleSolving) ); } function restoreState() { // Restore state after page load try { const savedEvil = sessionStorage.getItem("ovipets_bot_evil_mode"); const savedPeaceful = sessionStorage.getItem("ovipets_bot_peaceful_mode"); const savedQueue = sessionStorage.getItem("ovipets_bot_queue"); if (savedEvil !== null) { isEvil = JSON.parse(savedEvil); log(`Restored evil mode: ${isEvil}`); // Update UI toggle if it exists const evilToggle = document.getElementById("evil-mode-toggle"); while (!evilToggle) { if (isEvil) { evilToggle.classList.add("active"); } else { evilToggle.classList.remove("active"); } // Update panel background color const panel = document.getElementById("ovipets-bot-panel"); if (panel) { panel.style.background = isEvil ? "rgba(139, 0, 0, 0.8)" // Dark red with transparency : "rgba(0, 0, 0, 0.8)"; // Default black with transparency } } } if (savedPeaceful !== null) { isPeaceful = JSON.parse(savedPeaceful); log(`Restored peaceful mode: ${isPeaceful}`); // Update UI toggle if it exists const peacefulToggle = document.getElementById("peaceful-mode-toggle"); while (!peacefulToggle) { if (isPeaceful) { peacefulToggle.classList.add("active"); } else { peacefulToggle.classList.remove("active"); } } } if (savedQueue !== null) { eggQueue = JSON.parse(savedQueue); log(`Restored queue with ${eggQueue.length} eggs`); updateQueueVisualizer(); if (eggQueue.length > 0 && !isProcessing && !isPuzzleSolving) { log("Starting egg processing from restored queue"); processEggQueue(); } } } catch (error) { log("Error restoring state:", error); } } function log(...args) { console.log("%c[OVIPETS-BOT]", "background:#ff6b35;color:#fff;", ...args); } function isRunning() { return sessionStorage.getItem(RUN_KEY) === "true"; } function extractUserIdFromPage() { // First try to get from the user avatar image const avatarImg = document.querySelector("#self > a > img"); if (avatarImg && avatarImg.src) { const match = avatarImg.src.match(/[?&]usr=(\d+)/); if (match) { log(`Found user ID from avatar: ${match[1]}`); return match[1]; } } // Fallback: check URL hash const hashMatch = location.hash.match(/usr=(\d+)/); if (hashMatch) { log(`Found user ID from URL hash: ${hashMatch[1]}`); return hashMatch[1]; } // Fallback: check page scripts const scripts = document.querySelectorAll("script"); for (let script of scripts) { const match = script.textContent.match(/usr[_\s]*[:=]\s*['"]*(\d+)/); if (match) { log(`Found user ID from script: ${match[1]}`); return match[1]; } } log("Could not find user ID automatically"); return null; } async function solvePuzzleWithAPI(imageBlob) { return new Promise((resolve, reject) => { const formData = new FormData(); formData.append("file", imageBlob, "puzzle.png"); GM_xmlhttpRequest({ method: "POST", url: `${API_BASE}/predict`, data: formData, onload: function (response) { try { const data = JSON.parse(response.responseText); resolve(data.predicted_class); } catch (error) { reject(error); } }, onerror: reject, }); }); } async function solveAndSubmit() { isPuzzleSolving = true; log("🧩 PUZZLE SOLVING - Bot paused"); const dlgSel = 'div.ui-dialog[role="dialog"]'; const turnSel = 'button[onclick*="pet_turn_egg"]'; const maxRetries = 1; for (let attempt = 1; attempt <= maxRetries; attempt++) { log(`Solve attempt ${attempt}/${maxRetries}`); const dlg = document.querySelector(dlgSel); if (!dlg) { log("No dialog found"); await new Promise((r) => setTimeout(r, 300)); continue; } // Check if this is actually a puzzle dialog if (!dlg.innerHTML.includes("Name the Species")) { log("Not a puzzle dialog"); isPuzzleSolving = false; return false; } const img = dlg.querySelector("fieldset img"); if (!img) { log("No puzzle image found"); await new Promise((r) => setTimeout(r, 300)); continue; } try { // Get the image URL and convert to proper format const url = img.src.replace(/^\/\//, "https://"); log(`Fetching puzzle image: ${url}`); // Fetch image as blob using GM_xmlhttpRequest const blob = await new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", url: url, responseType: "blob", onload: (response) => resolve(response.response), onerror: (error) => reject(error), }); }); log("Sending to AI for prediction..."); const prediction = await solvePuzzleWithAPI(blob); // Handle retry logic - use fallback prediction let predictedClass = prediction; if (attempt > 1) { predictedClass = "Raptor"; // Fallback for retries log(`Retry attempt, using fallback: ${predictedClass}`); } else { log(`AI predicted: ${predictedClass}`); } // Find and click the matching label let clicked = false; const labels = dlg.querySelectorAll("label"); for (let label of labels) { if (label.textContent.trim() === predictedClass) { log(`Clicking label: ${predictedClass}`); label.click(); clicked = true; break; } } if (!clicked) { log(`No label found for prediction: ${predictedClass}`); continue; } // Submit the answer const submitBtn = dlg.querySelector(".ui-dialog-buttonpane button"); if (submitBtn) { log("Submitting answer..."); submitBtn.click(); await new Promise((r) => setTimeout(r, 259)); // Wait for response } // Check for error dialog const errorDialog = Array.from(document.querySelectorAll(dlgSel)).find( (d) => d.querySelector(".ui-dialog-title")?.innerText === "Error" ); if (errorDialog) { log("Error dialog detected, wrong answer - retrying"); // Close error dialog const errorBtn = errorDialog.querySelector( ".ui-dialog-buttonpane button" ); if (errorBtn) errorBtn.click(); await new Promise((r) => setTimeout(r, 300)); // Click turn button again for retry const turnBtn = document.querySelector(turnSel); if (turnBtn) { turnBtn.click(); await new Promise((r) => setTimeout(r, 300)); } continue; // Retry } // Success - puzzle solved correctly log("✅ Puzzle solved successfully! Bot resuming..."); // click any button matching the close dialog button until no more are found // <button type="button" class="ui-button ui-corner-all ui-widget ui-button-icon-only ui-dialog-titlebar-close" title="Close"><span class="ui-button-icon ui-icon ui-icon-closethick"></span><span class="ui-button-icon-space"> </span>Close</button> // after it has clicked all close buttons, it should look to see if more close buttons are available, repeating until no more are found const closeButtons = document.querySelectorAll( ".ui-dialog-titlebar-close" ); while (closeButtons.length > 0) { closeButtons.forEach((btn) => { log("Clicking close button"); btn.click(); }); await new Promise((r) => setTimeout(r, 300)); // Wait for dialog to close // Re-fetch close buttons closeButtons = document.querySelectorAll(".ui-dialog-titlebar-close"); } log("All close buttons clicked, puzzle dialog should be closed now"); // Wait a bit to ensure dialog closes isPuzzleSolving = false; return true; } catch (error) { log(`Error in attempt ${attempt}:`, error); } } log("❌ Failed to solve puzzle after all attempts. Bot resuming..."); isPuzzleSolving = false; return false; } async function processEggQueue() { if (isProcessing || !isRunning()) return; isProcessing = true; while (eggQueue.length > 0 && isRunning()) { // Wait if puzzle is being solved while (isPuzzleSolving && isRunning()) { log("⏸️ Waiting for puzzle to be solved..."); await new Promise((r) => setTimeout(r, 50)); } if (!isRunning()) break; const egg = eggQueue.shift(); log(`Processing egg ${egg.egg_id} (${eggQueue.length} remaining)`); // Update queue visualizer after removing egg updateQueueVisualizer(); try { location.href = egg.page_url; accum += 1; if (accum >= 500) { log("🐢 Slow down! Waiting 5 seconds to avoid rate limiting..."); await new Promise((r) => setTimeout(r, 5000)); accum = 0; saveState(); // hit the browser refresh button to avoid rate limiting location.reload(); } // just wait for contentloaded await new Promise((r) => setTimeout(r, 100)); // wait until <div class="loading"></div> is not present let attempts = 0; while (document.querySelector("div.loading") && attempts < 100) { log("⏳ Waiting for page to load... (processEggQueue)"); attempts += 1; await new Promise((r) => setTimeout(r, 100)); } log(`Page loaded for egg ${egg.egg_id}`); // look for a single instance of each CLASS_NAME from CLASS_NAMES on the page, anywhere // not just as a class name but as any string of text anywhere in the entire HTML/JS/plaintext of the page var classesOnPage = true; var classes_to_use = isEvil ? CLASS_NAMES_EVIL : CLASS_NAMES; for (const className of classes_to_use) { if (!document.body.innerHTML.includes(className)) { log( `❌ Egg ${egg.egg_id} does not have required class: ${className}` ); classesOnPage = false; break; // No need to check further if one is missing } } if (!classesOnPage) { log( `❌ Egg ${egg.egg_id} does not have all required classes on the page, skipping` ); continue; } // Try to click turn button with retries let turnAttempts = 0; const maxTurnAttempts = 0; const tryTurn = async () => { const turnBtn = document.querySelector( 'button[onclick*="pet_turn_egg"]' ); if (turnBtn) { log("Found turn button, clicking..."); turnBtn.click(); await new Promise((r) => setTimeout(r, 300)); // Check if puzzle dialog appeared const dlg = document.querySelector('div.ui-dialog[role="dialog"]'); if (dlg && dlg.innerHTML.includes("Name the Species")) { log("Puzzle dialog detected, solving..."); await solveAndSubmit(); } else { const closeButtons = document.querySelectorAll( ".ui-dialog-titlebar-close" ); while (closeButtons.length > 0) { closeButtons.forEach((btn) => { log("Clicking close button"); btn.click(); }); await new Promise((r) => setTimeout(r, 300)); // Wait for dialog to close // Re-fetch close buttons closeButtons = document.querySelectorAll( ".ui-dialog-titlebar-close" ); } log("No puzzle dialog, egg turned successfully"); } return true; } else if (turnAttempts++ < maxTurnAttempts) { log( `Turn button not found, attempt ${turnAttempts}/${maxTurnAttempts}` ); await new Promise((r) => setTimeout(r, 300)); return await tryTurn(); } else { log("Turn button not found after all attempts"); return false; } }; await tryTurn(); // Wait before processing next egg await new Promise((r) => setTimeout(r, 50)); while (document.querySelector("div.loading")) { log("⏳ Waiting for page to load... (after egg turn)"); await new Promise((r) => setTimeout(r, 100)); } } catch (error) { log(`Error processing egg ${egg.egg_id}:`, error); } } isProcessing = false; // Update visualizer when queue is empty updateQueueVisualizer(); } function pollForEggs() { if (isPuzzleSolving) { log("Skipping poll - puzzle in progress"); return; } GM_xmlhttpRequest({ method: "GET", url: `${API_BASE}/get_eggs`, onload: function (response) { try { const data = JSON.parse(response.responseText); if (data.eggs && data.eggs.length > 0) { log(`Found ${data.eggs.length} new eggs!`); const oldQueueLength = eggQueue.length; eggQueue.push(...data.eggs); // Update queue visualizer after adding eggs setTimeout(() => { updateQueueVisualizer(); }, 100); if (!isProcessing && !isPuzzleSolving) { processEggQueue(); } } // If server is idle, restart collection if (data.status === "idle" && isRunning() && !isPuzzleSolving) { log("Server idle, restarting collection..."); startCollection(); } } catch (error) { log("Error parsing eggs:", error); } }, onerror: function (error) { log("Error fetching eggs:", error); }, }); } function updateProgress() { if (!isRunning()) return; GM_xmlhttpRequest({ method: "GET", url: `${API_BASE}/progress`, onload: function (response) { try { const data = JSON.parse(response.responseText); log( `Progress update: ${data.friends_completed}/${data.friends_total}, running: ${data.is_running}` ); // Debug log const progressElement = document.getElementById("friend-progress"); if (progressElement) { if (data.is_running && data.friends_total > 0) { progressElement.textContent = `👥 ${data.friends_completed}/${data.friends_total}`; progressElement.style.display = "block"; log( `Updated progress display to: ${progressElement.textContent}` ); // Debug log } else { log( `Hiding progress - running: ${data.is_running}, total: ${data.friends_total}` ); // Debug log } } // Update status indicator updateStatusIndicator(); } catch (error) { log("Error updating progress:", error); } }, onerror: function (error) { log("Error fetching progress:", error); }, }); // Replace the problematic code block with this: if (isEvil) { const evilToggle = document.getElementById("evil-mode-toggle"); if (evilToggle) { // The toggle is a checkbox input, not a div with classes evilToggle.checked = true; // The visual slider is the span with class "slider" const slider = evilToggle.nextElementSibling; // This gets the .slider span if (slider && slider.classList.contains("slider")) { // Trigger the CSS :checked state by ensuring the input is checked slider.style.backgroundColor = "#e74c3c"; // Red background for evil mode slider.style.transition = "background-color 0.3s ease, box-shadow 0.3s ease"; } } // Update panel background color (correct ID) const panel = document.getElementById("bot-controls"); if (panel) { panel.style.background = "rgba(139, 0, 0, 0.8)"; // Dark red with transparency } } } function updateStatusIndicator() { const statusDot = document.getElementById("status-dot"); if (statusDot) { if (isRunning()) { statusDot.style.backgroundColor = "#00ff00"; // Green when running statusDot.title = "Bot is running"; } else { statusDot.style.backgroundColor = "#ff0000"; // Red when stopped statusDot.title = "Bot is stopped"; } } } function updateQueueVisualizer() { const queueContainer = document.getElementById("queue-visualizer"); if (!queueContainer) return; const currentQueueLength = eggQueue.length; const maxBarWidth = 220; // Maximum width of the queue bar const segmentWidth = 0.22; // Width of each egg segment const maxSegments = Math.floor(maxBarWidth / segmentWidth); // Get or create the background bar (gray bar) let backgroundBar = queueContainer.querySelector(".queue-background-bar"); if (!backgroundBar) { backgroundBar = document.createElement("div"); backgroundBar.className = "queue-background-bar"; backgroundBar.style.cssText = ` height: 14px; width: ${maxBarWidth}px; background: rgba(255,255,255,0.1); border-radius: 7px; position: relative; overflow: hidden; `; // Insert the background bar into the existing container const existingBarContainer = queueContainer.querySelector( 'div[style*="height: 14px"]' ); if (existingBarContainer) { existingBarContainer.replaceWith(backgroundBar); } else { queueContainer.appendChild(backgroundBar); } } // Get or create the queue bar (orange bar) - make it a child of background bar let queueBar = backgroundBar.querySelector(".queue-bar"); if (!queueBar) { queueBar = document.createElement("div"); queueBar.className = "queue-bar"; queueBar.style.cssText = ` height: 100%; background: linear-gradient(90deg, #ff6b35, #f7931e); border-radius: 6px; transition: width 0.3s ease; position: absolute; top: 0; left: 0; overflow: hidden; border: 1px solid rgba(255,255,255,0.3); box-sizing: border-box; `; backgroundBar.appendChild(queueBar); // also make sure the ui changes (switch and background color) are applied // if evil mode is enabled if (isEvil) { queueBar.style.background = "linear-gradient(90deg, #ff0000, #aa0000)"; queueBar.style.boxShadow = "0 2px 8px rgba(255, 0, 0, 0.3)"; // make sure the switch is actually on const evilToggle = document.getElementById("evil-mode-toggle"); if (evilToggle) { evilToggle.classList.add("active"); } // Update panel background color const panel = document.getElementById("ovipets-bot-panel"); if (panel) { panel.style.background = "rgba(139, 0, 0, 0.8)"; // Dark red with transparency } } else { queueBar.style.background = "linear-gradient(90deg, #ff6b35, #f7931e)"; queueBar.style.boxShadow = "0 2px 8px rgba(255, 107, 53, 0.3)"; } } // Calculate target width - constrain to max bar width const visibleSegments = Math.min(currentQueueLength, maxSegments); const targetWidth = Math.min(visibleSegments * segmentWidth, maxBarWidth); // Get current width const currentWidth = parseInt(queueBar.style.width) || 0; // Update width with animation queueBar.style.width = `${targetWidth}px`; // Add segments effect if (targetWidth > currentWidth) { // Queue growing - animate segments flying in from right animateSegmentsIn( queueBar, Math.ceil((targetWidth - currentWidth) / segmentWidth) ); } else if (targetWidth < currentWidth) { // Queue shrinking - animate segments leaving from left animateSegmentsOut( queueBar, Math.ceil((currentWidth - targetWidth) / segmentWidth) ); } // Update queue counter const queueCounter = queueContainer.querySelector(".queue-counter"); if (queueCounter) { queueCounter.textContent = `🥚 ${currentQueueLength}`; // Pulse effect when queue changes queueCounter.style.transform = "scale(1.1)"; setTimeout(() => { queueCounter.style.transform = "scale(1)"; }, 150); } // if isEvil is true just make sure the switch is on and the background color is dark red console.log(`isEvil: ${isEvil}`); if (isEvil) { const evilToggle = document.getElementById("evil-mode-toggle"); while (!evilToggle) { console.log("Waiting for evil toggle to be available..."); evilToggle = document.getElementById("evil-mode-toggle"); } if (evilToggle) { evilToggle.classList.add("active"); } // Update panel background color const panel = document.getElementById("ovipets-bot-panel"); if (panel) { panel.style.background = "rgba(139, 0, 0, 0.8)"; // Dark red with transparency } } } function animateSegmentsIn(queueBar, segmentCount) { for (let i = 0; i < segmentCount; i++) { setTimeout(() => { const segment = document.createElement("div"); segment.style.cssText = ` position: absolute; right: -10px; top: 0; width: 6px; height: 100%; background: linear-gradient(90deg, #ffaa35, #ff8535); border-radius: 3px; animation: flyInFromRight 0.4s ease-out forwards; `; // Add keyframes if not already added if (!document.getElementById("queue-animations")) { const style = document.createElement("style"); style.id = "queue-animations"; style.textContent = ` @keyframes flyInFromRight { 0% { right: -10px; opacity: 0; transform: scale(0.5) rotate(45deg); } 50% { opacity: 1; transform: scale(1.1) rotate(10deg); } 100% { right: 2px; opacity: 0.8; transform: scale(1) rotate(0deg); } } @keyframes slideOutLeft { 0% { left: 0; opacity: 0.8; transform: scale(1); } 50% { opacity: 0.5; transform: scale(0.8); } 100% { left: -10px; opacity: 0; transform: scale(0.3); } } .queue-bar { box-shadow: 0 2px 8px rgba(255, 107, 53, 0.3); } `; document.head.appendChild(style); } queueBar.appendChild(segment); // Remove segment after animation setTimeout(() => { if (segment.parentNode) { segment.remove(); } }, 500); }, i * 100); // Stagger the animations } } function animateSegmentsOut(queueBar, segmentCount) { for (let i = 0; i < segmentCount; i++) { setTimeout(() => { const segment = document.createElement("div"); segment.style.cssText = ` position: absolute; left: 2px; top: 0; width: 6px; height: 100%; background: linear-gradient(90deg, #ff6b35, #f7931e); border-radius: 3px; animation: slideOutLeft 0.3s ease-in forwards; `; queueBar.appendChild(segment); // Remove segment after animation setTimeout(() => { if (segment.parentNode) { segment.remove(); } }, 350); }, i * 50); // Faster staggering for removal } } function startCollection() { const userId = extractUserIdFromPage() || prompt("Enter your user ID:"); if (!userId) return; GM_xmlhttpRequest({ method: "POST", url: `${API_BASE}/start_collection/${userId}${ isPeaceful ? "?peaceful=true" : "" }`, onload: function (response) { try { const data = JSON.parse(response.responseText); log("Collection started:", data); } catch (error) { log("Error parsing start collection response:", error); } }, onerror: function (error) { log("Error starting collection:", error); }, }); } function startBot() { log("Starting bot..."); sessionStorage.setItem(RUN_KEY, "true"); startCollection(); // Poll for eggs every 3 seconds pollInterval = setInterval(() => { pollForEggs(); updateProgress(); }, 3000); // Update progress immediately updateProgress(); // Update status indicator updateStatusIndicator(); } function stopBot() { log("Stopping bot..."); sessionStorage.removeItem(RUN_KEY); if (pollInterval) { clearInterval(pollInterval); pollInterval = null; } // Clean up saved state when manually stopping sessionStorage.removeItem("ovipets_bot_evil_mode"); sessionStorage.removeItem("ovipets_bot_queue"); sessionStorage.removeItem("ovipets_bot_accumulator"); sessionStorage.removeItem("ovipets_bot_is_processing"); sessionStorage.removeItem("ovipets_bot_is_puzzle_solving"); // Hide progress display const progressElement = document.getElementById("friend-progress"); if (progressElement) { // set to 0/0 progressElement.textContent = "👥 0/0"; } // Update status indicator updateStatusIndicator(); // Notify server to stop collection GM_xmlhttpRequest({ method: "POST", url: `${API_BASE}/stop_collection`, onload: function (response) { log("Collection stopped on server"); }, onerror: function (error) { log("Error stopping collection:", error); }, }); eggQueue = []; isProcessing = false; isPuzzleSolving = false; accum = 0; } // --- Befriender Module --- const Befriender = (function () { const RUN_KEY = "befriender_running"; const LINKS_KEY = "befriender_links"; const IDX_KEY = "befriender_index"; const BASE_KEY = "befriender_base_href"; const UL_KEY = "befriender_selected_ul"; function log(...args) { console.log("%c[BEFRIENDER]", "background:#0055aa;color:#fff;", ...args); } function startBot() { log( "Starting Befriender - Please click on the UL element containing the user avatars" ); // Store the current location as base sessionStorage.setItem(BASE_KEY, location.href); sessionStorage.setItem(RUN_KEY, "true"); sessionStorage.setItem(IDX_KEY, "0"); sessionStorage.removeItem(LINKS_KEY); sessionStorage.removeItem(UL_KEY); // Enable UL selection mode enableULSelection(); } function stopBot() { sessionStorage.removeItem(RUN_KEY); sessionStorage.removeItem(UL_KEY); disableULSelection(); log("Stopped Befriender"); } function isRunning() { return sessionStorage.getItem(RUN_KEY) === "true"; } function getBase() { return sessionStorage.getItem(BASE_KEY); } function getLinks() { try { return JSON.parse(sessionStorage.getItem(LINKS_KEY)) || []; } catch { return []; } } function saveLinks(a) { sessionStorage.setItem(LINKS_KEY, JSON.stringify(a)); } function getIndex() { return parseInt(sessionStorage.getItem(IDX_KEY) || "0", 10); } function setIndex(i) { sessionStorage.setItem(IDX_KEY, String(i)); } function getSelectedUL() { return sessionStorage.getItem(UL_KEY); } function setSelectedUL(selector) { sessionStorage.setItem(UL_KEY, selector); } function enableULSelection() { // Create visual indicators const style = document.createElement("style"); style.id = "befriender-ul-selection-style"; style.textContent = ` .befriender-ul-highlight { outline: 3px solid #0055aa !important; outline-offset: 2px !important; cursor: pointer !important; position: relative !important; } .befriender-ul-highlight::before { content: "Click to select this list for befriending"; position: absolute; top: -25px; left: 0; background: #0055aa; color: white; padding: 2px 8px; font-size: 12px; border-radius: 3px; white-space: nowrap; z-index: 10000; } .befriender-selection-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.3); z-index: 9999; pointer-events: none; } .befriender-instruction { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: #0055aa; color: white; padding: 20px; border-radius: 8px; font-family: monospace; font-size: 16px; z-index: 10001; text-align: center; box-shadow: 0 4px 20px rgba(0,0,0,0.5); } `; document.head.appendChild(style); // Create overlay const overlay = document.createElement("div"); overlay.className = "befriender-selection-overlay"; overlay.id = "befriender-overlay"; document.body.appendChild(overlay); // Create instruction const instruction = document.createElement("div"); instruction.className = "befriender-instruction"; instruction.id = "befriender-instruction"; instruction.innerHTML = ` <div style="margin-bottom: 10px;"><strong>📨 Befriender Setup</strong></div> <div>Hover over UL elements and click the one containing the user avatars you want to befriend</div> <div style="margin-top: 10px; font-size: 12px;">Press ESC to cancel</div> `; document.body.appendChild(instruction); // Find all UL elements and add hover effects const uls = document.querySelectorAll("ul"); uls.forEach((ul) => { ul.addEventListener("mouseenter", handleULHover); ul.addEventListener("mouseleave", handleULLeave); ul.addEventListener("click", handleULClick); }); // ESC to cancel document.addEventListener("keydown", handleEscapeKey); log( `Found ${uls.length} UL elements - hover to highlight, click to select` ); } function disableULSelection() { // Remove style const style = document.getElementById("befriender-ul-selection-style"); if (style) style.remove(); // Remove overlay and instruction const overlay = document.getElementById("befriender-overlay"); if (overlay) overlay.remove(); const instruction = document.getElementById("befriender-instruction"); if (instruction) instruction.remove(); // Remove event listeners from all ULs const uls = document.querySelectorAll("ul"); uls.forEach((ul) => { ul.removeEventListener("mouseenter", handleULHover); ul.removeEventListener("mouseleave", handleULLeave); ul.removeEventListener("click", handleULClick); ul.classList.remove("befriender-ul-highlight"); }); document.removeEventListener("keydown", handleEscapeKey); } // Replace the handleULHover function in the Befriender module: function handleULHover(event) { const ul = event.target; ul.classList.add("befriender-ul-highlight"); // Add debug info overlay const debugInfo = document.createElement("div"); debugInfo.id = "befriender-debug-info"; debugInfo.style.cssText = ` position: fixed; top: 10px; right: 10px; background: #0055aa; color: white; padding: 10px; border-radius: 5px; font-family: monospace; font-size: 12px; z-index: 10002; max-width: 300px; `; const userAvatars = ul.querySelectorAll("a.user.avatar").length; const totalLis = ul.querySelectorAll("li").length; const selector = generateUniqueSelector(ul); debugInfo.innerHTML = ` <strong>🔍 UL Preview</strong><br> Selector: ${selector}<br> Total LIs: ${totalLis}<br> User Avatars: ${userAvatars}<br> ${ userAvatars > 0 ? "✅ Contains user avatars!" : "❌ No user avatars found" } `; document.body.appendChild(debugInfo); } function handleULLeave(event) { event.target.classList.remove("befriender-ul-highlight"); // Remove debug info const debugInfo = document.getElementById("befriender-debug-info"); if (debugInfo) debugInfo.remove(); } // Replace the handleULClick function in the Befriender module: // Replace the handleULClick and collectLinks functions in the Befriender module: function handleULClick(event) { event.preventDefault(); event.stopPropagation(); const ul = event.target; // Remove highlight class ul.classList.remove("befriender-ul-highlight"); log(`Selected UL with ${ul.children.length} children`); // Clean up selection mode first disableULSelection(); // Collect links immediately from the selected UL - no timeout needed collectLinksFromElement(ul); } // New function to collect links directly from the element function collectLinksFromElement(ul) { if (!isRunning()) return; log("Collecting avatar links from selected UL"); if (!ul) { log("No UL element provided"); stopBot(); return; } log(`Found UL element with ${ul.children.length} child elements`); // Extract avatar links specifically const avatarLinks = []; const lis = ul.querySelectorAll("li"); log(`Scanning ${lis.length} li elements for avatar links...`); lis.forEach((li, index) => { // Look for user avatar links const avatarLink = li.querySelector("a.user.avatar") || li.querySelector("a.avatar") || li.querySelector('a[href*="usr="]') || li.querySelector('a[href*="#!/"]'); if (avatarLink && avatarLink.href) { const href = avatarLink.href; // Check if it's a user profile link (not a pet link) const isUserLink = (href.includes("usr=") && !href.includes("pet=")) || (href.includes("#!/") && !href.includes("pet=") && href !== "https://ovipets.com/#"); if (isUserLink) { avatarLinks.push(href); log(`Found avatar link ${index + 1}: ${href}`); } } }); if (!avatarLinks.length) { log("No avatar links found in selected UL"); log("UL HTML preview:", ul.innerHTML.substring(0, 500)); // Debug what we actually found log("Debug: Found links in UL:"); const allLinks = ul.querySelectorAll("a"); allLinks.forEach((link, i) => { log(` Link ${i + 1}: href="${link.href}" class="${link.className}"`); }); stopBot(); return; } log(`Found ${avatarLinks.length} avatars to befriend`); saveLinks(avatarLinks); goToProfile(); } // Keep the old collectLinks function for when we're at the base page function collectLinks() { if (!isRunning()) return; log("Looking for previously selected UL..."); // This function is only called when we're back at the base page // and need to re-find the UL. Since we don't persist the selector, // we'll just let the user re-select if needed. log("Please re-select the UL containing user avatars"); enableULSelection(); } function handleEscapeKey(event) { if (event.key === "Escape") { log("Selection cancelled"); stopBot(); } } function generateUniqueSelector(element) { // Remove the highlight class before generating selector element.classList.remove("befriender-ul-highlight"); // Try ID first if (element.id) { return `#${element.id}`; } // Try class combination (excluding our highlight class) if (element.className) { const classes = element.className .split(" ") .filter((c) => c.trim() && c !== "befriender-ul-highlight"); if (classes.length > 0) { let selector = `ul.${classes.join(".")}`; if (document.querySelectorAll(selector).length === 1) { return selector; } } } // Try parent-child relationship let parent = element.parentElement; if (parent) { if (parent.id) { return `#${parent.id} > ul`; } if (parent.className) { const classes = parent.className.split(" ").filter((c) => c.trim()); if (classes.length > 0) { let selector = `.${classes.join(".")} > ul`; if (document.querySelectorAll(selector).length === 1) { return selector; } } } } // Try to find position among siblings const siblings = Array.from(element.parentElement?.children || []); const ulSiblings = siblings.filter((el) => el.tagName === "UL"); if (ulSiblings.length === 1) { // Only UL child if (parent && parent.id) { return `#${parent.id} ul`; } if (parent && parent.className) { const classes = parent.className.split(" ").filter((c) => c.trim()); if (classes.length > 0) { return `.${classes.join(".")} ul`; } } } else { // Multiple UL siblings, use nth-of-type const index = ulSiblings.indexOf(element) + 1; if (parent && parent.id) { return `#${parent.id} ul:nth-of-type(${index})`; } if (parent && parent.className) { const classes = parent.className.split(" ").filter((c) => c.trim()); if (classes.length > 0) { return `.${classes.join(".")} ul:nth-of-type(${index})`; } } } // Final fallback: use XPath-like approach function getElementPath(el) { if (el.id) return `#${el.id}`; if (el === document.body) return "body"; const parent = el.parentElement; if (!parent) return el.tagName.toLowerCase(); const siblings = Array.from(parent.children); const sameTagSiblings = siblings.filter( (sibling) => sibling.tagName === el.tagName ); if (sameTagSiblings.length === 1) { return `${getElementPath(parent)} > ${el.tagName.toLowerCase()}`; } else { const index = sameTagSiblings.indexOf(el) + 1; return `${getElementPath( parent )} > ${el.tagName.toLowerCase()}:nth-of-type(${index})`; } } return getElementPath(element); } // Replace the collectLinks function in the Befriender module: // Replace the collectLinks function in the Befriender module: function collectLinks() { if (!isRunning()) return; log("Collecting avatar links from selected UL"); const ulSelector = getSelectedUL(); if (!ulSelector) { log("No UL selected"); stopBot(); return; } let ul; // Handle special fallback selector if (ulSelector.startsWith("ul-signature:")) { ul = window.befrienderSelectedUL; if (!ul) { log("Element reference lost"); stopBot(); return; } } else { ul = document.querySelector(ulSelector); } if (!ul) { log(`Selected UL not found: ${ulSelector}`); stopBot(); return; } log(`Found UL element with ${ul.children.length} child elements`); // Extract avatar links specifically (improved detection) const avatarLinks = []; const lis = ul.querySelectorAll("li"); log(`Scanning ${lis.length} li elements for avatar links...`); lis.forEach((li, index) => { // Look for user avatar links with improved selectors const avatarLink = li.querySelector("a.user.avatar") || li.querySelector("a.avatar") || li.querySelector('a[href*="usr="]') || li.querySelector('a[href*="#!/"]'); if (avatarLink && avatarLink.href) { // More flexible user link detection const href = avatarLink.href; // Check if it's a user profile link (not a pet link) const isUserLink = (href.includes("usr=") && !href.includes("pet=")) || (href.includes("#!/") && !href.includes("pet=") && !href === "https://ovipets.com/#"); if (isUserLink) { avatarLinks.push(href); log(`Found avatar link ${index + 1}: ${href}`); } } }); if (!avatarLinks.length) { log("No avatar links found in selected UL"); log("UL HTML preview:", ul.innerHTML.substring(0, 500)); // Let's also log what we actually found for debugging log("Debug: Found links in UL:"); const allLinks = ul.querySelectorAll("a"); allLinks.forEach((link, i) => { log(` Link ${i + 1}: href="${link.href}" class="${link.className}"`); }); stopBot(); return; } log(`Found ${avatarLinks.length} avatars to befriend`); saveLinks(avatarLinks); goToProfile(); } function goToProfile() { if (!isRunning()) return; const links = getLinks(); const idx = getIndex(); if (idx >= links.length) { log("All friend requests sent! Befriending complete!"); stopBot(); location.href = getBase(); return; } log(`📨 Visiting profile ${idx + 1}/${links.length}...`); location.href = links[idx]; } function handleProfile() { if (!isRunning()) return; log("🔍 Looking for friend request button..."); let tries = 0; const maxTries = 1; const tryFriendButton = () => { const btn = document.querySelector('button[onclick*="friend_request"]'); if (btn) { log("✅ Found friend request button, clicking..."); btn.click(); log("📨 Friend request sent!"); setIndex(getIndex() + 1); setTimeout(() => { if (isRunning()) { location.href = getBase(); } }, 300); } else if (tries++ < maxTries) { log( `⏳ Friend request button not found, retrying... (${tries}/${maxTries})` ); setTimeout(tryFriendButton, 300); } else { log("❌ No friend request button found, skipping this user"); setIndex(getIndex() + 1); setTimeout(() => { if (isRunning()) { goToProfile(); } }, 300); } }; setTimeout(tryFriendButton, 300); } function main() { if (!isRunning()) return; const href = location.href; const base = getBase(); log("🚀 Befriender main executing...", href); // If we haven't collected links yet and we're at the base if (!sessionStorage.getItem(LINKS_KEY) && href === base) { log("📍 At base, need to collect avatar links"); setTimeout(() => { if (isRunning()) { collectLinks(); } }, 300); } // If we're back at the base and have collected links else if (href === base) { log("📍 At base, have links, continuing to next profile..."); setTimeout(() => { if (isRunning()) { goToProfile(); } }, 300); } // If we're on a user profile page else { log("📍 On user profile, sending friend request..."); setTimeout(() => { if (isRunning()) { handleProfile(); } }, 300); } } return { startBot, stopBot, main }; })(); // --- Old Ovipets Auto‐Turn Eggs Across Friends --- const OvipetsOldAcross = (function () { const RUN_KEY = "ovipets_old_across_running"; const FR_KEY = "ovipets_old_across_friends"; const FI_KEY = "ovipets_old_across_friend_index"; const EG_KEY = "ovipets_old_across_eggs"; const EI_KEY = "ovipets_old_across_egg_index"; function log(...args) { console.log( "%c[OVIPETS-OLD-ACROSS]", "background:#444;color:#bada55;", ...args ); } function startBot() { log("Starting Old Across bot"); sessionStorage.setItem(RUN_KEY, "true"); sessionStorage.removeItem(FR_KEY); sessionStorage.setItem(FI_KEY, "0"); sessionStorage.removeItem(EG_KEY); sessionStorage.setItem(EI_KEY, "0"); hideResume(); main(); } function stopBot() { sessionStorage.removeItem(RUN_KEY); log("Stopped Old Across bot"); } function resumeBot() { sessionStorage.setItem(RUN_KEY, "true"); hideResume(); stepEggIndex(); navigateEggProfile(); } function showResume() { const btn = document.getElementById("ovipets-old-across-resume"); if (btn) btn.style.display = "inline-block"; } function hideResume() { const btn = document.getElementById("ovipets-old-across-resume"); if (btn) btn.style.display = "none"; } function isRunning() { return sessionStorage.getItem(RUN_KEY) === "true"; } function getFriends() { try { return JSON.parse(sessionStorage.getItem(FR_KEY)) || []; } catch { return []; } } function setFriends(a) { sessionStorage.setItem(FR_KEY, JSON.stringify(a)); } function getFI() { return parseInt(sessionStorage.getItem(FI_KEY) || "0", 10); } function setFI(i) { sessionStorage.setItem(FI_KEY, String(i)); log(`Friend index set to ${i}`); } function stepFI() { setFI(getFI() + 1); } function getEggs() { try { return JSON.parse(sessionStorage.getItem(EG_KEY)) || []; } catch { return []; } } function setEggs(a) { sessionStorage.setItem(EG_KEY, JSON.stringify(a)); } function getEI() { return parseInt(sessionStorage.getItem(EI_KEY) || "0", 10); } function setEI(i) { sessionStorage.setItem(EI_KEY, String(i)); } function stepEggIndex() { setEI(getEI() + 1); } function collectFriends() { if (!isRunning()) return; log("Collecting friends"); const ul = document.querySelector("body div#friends-list-modal ul") || document.querySelector("body div.friends-list ul") || document.querySelector("body ul"); if (!ul) { log("Friends list not found"); stopBot(); return; } const friends = Array.from(ul.querySelectorAll("a.user.avatar")) .map((a) => a.href) .filter(Boolean) .filter((h) => h !== window.location.origin + window.location.hash); log(`Found ${friends.length} friends`); setFriends(friends); navigateToNextFriend(); } function navigateToNextFriend() { if (!isRunning()) return; const friends = getFriends(); let idx = getFI(); if (idx >= friends.length) { log("Restarting friends at 0"); idx = 0; setFI(0); } const friendUrl = friends[idx]; if (!friendUrl || typeof friendUrl !== "string") { log(`Invalid URL at ${idx}, skipping`); stepFI(); setTimeout(navigateToNextFriend, 300); return; } let url = friendUrl.replace("#!/", "#!/?src=pets&sub=hatchery&usr="); if (url.includes("&usr=?usr=")) url = url.replace("&usr=?usr=", "&usr="); log(`Go to friend ${idx + 1}/${friends.length}`); location.href = url; } function hideAnnoyingDiv() { const annoyingSection = document.querySelector("#unnamed"); if (annoyingSection) { annoyingSection.style.display = "none"; log("Hiding annoying section"); } } function collectEggs(retries = 3) { if (!isRunning()) return; setTimeout(function () { hideAnnoyingDiv(); log("Collecting eggs (enhanced scan)"); const progressiveScan = async () => { const found = new Set(); for (let i = 0; i < retries; i++) { log(`Scan attempt ${i + 1}/${retries}`); var usr = document.querySelector("#src_pets")?.getAttribute("usr"); while (!usr) { await new Promise((r) => setTimeout(r, 100)); usr = document.querySelector("#src_pets")?.getAttribute("usr"); } const response = await fetch( `https://ovipets.com/?src=pets&sub=hatchery&usr=${usr}&!=jQuery36001195479668276287_1748584681454&_=1748584681455` ); if (!response.ok) { log("Failed to fetch hatchery page"); continue; } log("Response received, parsing HTML"); var text = await response.text(); text = text.replace(/^[^{]*\(/, "").replace(/\);?$/, ""); text = JSON.parse(text); text = text["output"]; const parser = new DOMParser(); const doc = parser.parseFromString(text, "text/html"); const lis = doc.querySelectorAll('img[title="Turn Egg"]'); lis.forEach((li) => { const a = li.parentElement?.parentElement?.parentElement?.querySelector( "a.pet" ); if (a) found.add(a.href); }); } log(`Found ${found.size} eggs`); return Array.from(found); }; (async () => { let looseDone = false; async function attempt(n) { log(`Attempt ${n}/${retries}`); var lis = await progressiveScan(); if (!looseDone) { looseDone = true; document .querySelectorAll('img[title="Turn Egg"]') .forEach((img) => { log("checking img", img); const li = img.parentElement?.parentElement?.parentElement; const a = li?.querySelector("a.pet"); if (a) { lis.push(a); log("Found!"); } }); log("Loose fallback"); if (lis.length) { log(`Loose found ${lis.length}`); setEggs(lis.map((a) => a.href).filter(Boolean)); return navigateEggProfile(); } } if (n < retries) return attempt(n + 1); log("No eggs → next friend"); stepFI(); navigateToNextFriend(); } attempt(1); })(); }, 1500); } function navigateEggProfile() { if (!isRunning()) return; const eggs = getEggs(), idx = getEI(); if (idx >= eggs.length) { log("Eggs done for this friend"); stepFI(); navigateToNextFriend(); return; } log(`Go to egg page ${idx + 1}/${eggs.length}`); location.href = eggs[idx]; } async function solveAndSubmitAll() { const dlgSel = 'div.ui-dialog[role="dialog"]', turnSel = 'button[onclick*="pet_turn_egg"]', maxR = 2; for (let a = 1; a <= maxR; a++) { const dlg = document.querySelector(dlgSel); if (!dlg) continue; const img = dlg.querySelector("fieldset img"); if (!img) { log("No modal image"); break; } const url = img.src.replace(/^\/\//, "https://"); log(`Solve attempt ${a}: fetch ${url}`); const blob = await new Promise((res, rej) => GM_xmlhttpRequest({ method: "GET", url, responseType: "blob", onload: (r) => res(r.response), onerror: (e) => rej(e), }) ); const form = new FormData(); form.append("file", blob, "egg.jpg"); log("Sending to AI"); const resp = await fetch("http://127.0.0.1:8000/predict", { method: "POST", body: form, }); const { predicted_class } = await resp.json(); log("Predicted", predicted_class); Array.from(dlg.querySelectorAll("label")).forEach((lbl) => { if (lbl.textContent.trim() === predicted_class) lbl.click(); }); dlg.querySelector(".ui-dialog-buttonpane button").click(); await new Promise((r) => setTimeout(r, 1000)); const err = Array.from(document.querySelectorAll(dlgSel)).find( (d) => d.querySelector(".ui-dialog-title")?.innerText === "Error" ); if (err) { log("Error modal, retry"); err.querySelector(".ui-dialog-buttonpane button").click(); document.querySelector(turnSel).click(); await new Promise((r) => setTimeout(r, 1000)); continue; } break; } log("All solved, moving on"); stepEggIndex(); setTimeout(() => { const prev = location.href; navigateEggProfile(); setTimeout(() => { if (location.href === prev) { log("Stuck, retry nav"); navigateEggProfile(); } }, 1500); }, 500); } function handleProfile() { if (!isRunning()) return; log("On egg profile"); let tries = 0, max = 6; (function clickTry() { const btn = document.querySelector('button[onclick*="pet_turn_egg"]'); if (btn) { log("Click turn"); btn.click(); setTimeout(async () => { const dlg = document.querySelector('div.ui-dialog[role="dialog"]'); if (dlg && /Name the Species/.test(dlg.innerHTML)) { log("Puzzle"); await solveAndSubmitAll(); } else { log("No puzzle"); stepEggIndex(); navigateEggProfile(); } }, 800); } else if (tries++ < max) { setTimeout(clickTry, 200); } else { log("No button"); stepEggIndex(); navigateEggProfile(); } })(); } function main() { if (!isRunning()) return; const h = location.hash || ""; log("Old Across main", h); if (h.includes("sub=profile") && h.includes("pet=")) { handleProfile(); } else if (h.includes("sub=hatchery")) { collectEggs(); } else { collectFriends(); } } return { startBot, stopBot, resumeBot, main }; })(); // --- Old Ovipets Thorough --- const OvipetsOldThorough = (function () { const RUN_KEY = "ovipets_old_thorough_running"; const FR_KEY = "ovipets_old_thorough_friends"; const FI_KEY = "ovipets_old_thorough_friend_index"; const EG_KEY = "ovipets_old_thorough_eggs"; const EI_KEY = "ovipets_old_thorough_egg_index"; function log(...args) { console.log( "%c[OVIPETS-OLD-THOROUGH]", "background:#666;color:#bada55;", ...args ); } function startBot() { log("Starting Old Thorough bot"); sessionStorage.setItem(RUN_KEY, "true"); sessionStorage.removeItem(FR_KEY); sessionStorage.setItem(FI_KEY, "0"); sessionStorage.removeItem(EG_KEY); sessionStorage.setItem(EI_KEY, "0"); hideResume(); main(); } function stopBot() { sessionStorage.removeItem(RUN_KEY); log("Stopped Old Thorough bot"); } function resumeBot() { sessionStorage.setItem(RUN_KEY, "true"); hideResume(); stepEggIndex(); navigateEggProfile(); } function showResume() { const btn = document.getElementById("ovipets-old-thorough-resume"); if (btn) btn.style.display = "inline-block"; } function hideResume() { const btn = document.getElementById("ovipets-old-thorough-resume"); if (btn) btn.style.display = "none"; } function isRunning() { return sessionStorage.getItem(RUN_KEY) === "true"; } function getFriends() { try { return JSON.parse(sessionStorage.getItem(FR_KEY)) || []; } catch { return []; } } function setFriends(a) { sessionStorage.setItem(FR_KEY, JSON.stringify(a)); } function getFI() { return parseInt(sessionStorage.getItem(FI_KEY) || "0", 10); } function setFI(i) { sessionStorage.setItem(FI_KEY, String(i)); log(`Friend index set to ${i}`); } function stepFI() { setFI(getFI() + 1); } function getEggs() { try { return JSON.parse(sessionStorage.getItem(EG_KEY)) || []; } catch { return []; } } function setEggs(a) { sessionStorage.setItem(EG_KEY, JSON.stringify(a)); } function getEI() { return parseInt(sessionStorage.getItem(EI_KEY) || "0", 10); } function setEI(i) { sessionStorage.setItem(EI_KEY, String(i)); } function stepEggIndex() { setEI(getEI() + 1); } function collectFriends() { if (!isRunning()) return; log("Collecting friends"); const ul = document.querySelector("body div#friends-list-modal ul") || document.querySelector("body div.friends-list ul") || document.querySelector("body ul"); if (!ul) { log("Friends list not found"); stopBot(); return; } const friends = Array.from(ul.querySelectorAll("a.user.avatar")) .map((a) => a.href) .filter(Boolean) .filter((h) => h !== window.location.origin + window.location.hash); log(`Found ${friends.length} friends`); setFriends(friends); navigateToNextFriend(); } function navigateToNextFriend() { if (!isRunning()) return; const friends = getFriends(); let idx = getFI(); if (idx >= friends.length) { log("Restarting friends at 0"); idx = 0; setFI(0); } const friendUrl = friends[idx]; if (!friendUrl || typeof friendUrl !== "string") { log(`Invalid URL at ${idx}, skipping`); stepFI(); setTimeout(navigateToNextFriend, 500); return; } let url = friendUrl.replace("#!/", "#!/?src=pets&sub=hatchery&usr="); if (url.includes("&usr=?usr=")) url = url.replace("&usr=?usr=", "&usr="); log(`Go to friend ${idx + 1}/${friends.length}`); location.href = url; } function hideAnnoyingDiv() { const annoyingSection = document.querySelector("#unnamed"); if (annoyingSection) { annoyingSection.style.display = "none"; log("Hiding annoying section"); } } function collectEggs(retries = 3) { if (!isRunning()) return; setTimeout(function () { hideAnnoyingDiv(); log("Collecting eggs (thorough scan)"); const progressiveScan = async () => { const found = new Set(); const stepY = window.innerHeight * 0.1; log("--- SCAN DOWN ---"); for (let y = 0; y <= document.body.scrollHeight; y += stepY) { window.scrollTo(0, y); } hideAnnoyingDiv(); window.scrollTo(0, 0); log(`Scan complete, found ${found.size} li`); return Array.from( document.querySelectorAll( "#hatchery > div > form > ul > li > div > a" ) ); }; (async () => { let looseDone = false; async function attempt(n) { log(`Attempt ${n}/${retries}`); var lis = await progressiveScan(); looseDone = true; log("Thorough fallback"); if (lis.length) { log(`Thorough found ${lis.length}`); setEggs(lis.map((a) => a.href).filter(Boolean)); return navigateEggProfile(); } if (n < retries) return attempt(n + 1); log("No eggs → next friend"); stepFI(); navigateToNextFriend(); } attempt(1); })(); }, 1500); } function navigateEggProfile() { if (!isRunning()) return; const eggs = getEggs(), idx = getEI(); if (idx >= eggs.length) { log("Eggs done for this friend"); stepFI(); navigateToNextFriend(); return; } log(`Go to egg page ${idx + 1}/${eggs.length}`); location.href = eggs[idx]; } async function solveAndSubmitAll() { const dlgSel = 'div.ui-dialog[role="dialog"]', turnSel = 'button[onclick*="pet_turn_egg"]', maxR = 2; for (let a = 1; a <= maxR; a++) { const dlg = document.querySelector(dlgSel); if (!dlg) continue; const img = dlg.querySelector("fieldset img"); if (!img) { log("No modal image"); break; } const url = img.src.replace(/^\/\//, "https://"); log(`Solve attempt ${a}: fetch ${url}`); const blob = await new Promise((res, rej) => GM_xmlhttpRequest({ method: "GET", url, responseType: "blob", onload: (r) => res(r.response), onerror: (e) => rej(e), }) ); const form = new FormData(); form.append("file", blob, "egg.jpg"); log("Sending to AI"); const resp = await fetch("http://127.0.0.1:8000/predict", { method: "POST", body: form, }); const { predicted_class } = await resp.json(); log("Predicted", predicted_class); Array.from(dlg.querySelectorAll("label")).forEach((lbl) => { if (lbl.textContent.trim() === predicted_class) lbl.click(); }); dlg.querySelector(".ui-dialog-buttonpane button").click(); await new Promise((r) => setTimeout(r, 1000)); const err = Array.from(document.querySelectorAll(dlgSel)).find( (d) => d.querySelector(".ui-dialog-title")?.innerText === "Error" ); if (err) { log("Error modal, retry"); err.querySelector(".ui-dialog-buttonpane button").click(); document.querySelector(turnSel).click(); await new Promise((r) => setTimeout(r, 1000)); continue; } break; } log("All solved, moving on"); stepEggIndex(); setTimeout(() => { const prev = location.href; navigateEggProfile(); setTimeout(() => { if (location.href === prev) { log("Stuck, retry nav"); navigateEggProfile(); } }, 1500); }, 500); } function handleProfile() { if (!isRunning()) return; log("On egg profile"); let tries = 0, max = 2; (function clickTry() { const btn = document.querySelector('button[onclick*="pet_turn_egg"]'); if (btn) { log("Click turn"); btn.click(); setTimeout(async () => { const dlg = document.querySelector('div.ui-dialog[role="dialog"]'); if (dlg && /Name the Species/.test(dlg.innerHTML)) { log("Puzzle"); await solveAndSubmitAll(); } else { log("No puzzle"); stepEggIndex(); navigateEggProfile(); } }, 800); } else if (tries++ < max) { setTimeout(clickTry, 100); } else { log("No button"); stepEggIndex(); navigateEggProfile(); } })(); } function main() { if (!isRunning()) return; const h = location.hash || ""; log("Old Thorough main", h); if (h.includes("sub=profile") && h.includes("pet=")) { handleProfile(); } else if (h.includes("sub=hatchery")) { collectEggs(); } else { collectFriends(); } } return { startBot, stopBot, resumeBot, main }; })(); // --- Adopter Module --- const Adopter = (function () { const RUN_KEY = "adopter_running"; const PETS_KEY = "adopter_pets"; const IDX_KEY = "adopter_index"; const BASE_KEY = "adopter_base_href"; const UL_KEY = "adopter_selected_ul"; function log(...args) { console.log("%c[ADOPTER]", "background:#ff6600;color:#fff;", ...args); } function startBot() { log( "Starting Adopter - Please click on the UL element containing the pets" ); // Store the current location as base sessionStorage.setItem(BASE_KEY, location.href); sessionStorage.setItem(RUN_KEY, "true"); sessionStorage.setItem(IDX_KEY, "0"); sessionStorage.removeItem(PETS_KEY); sessionStorage.removeItem(UL_KEY); // Enable UL selection mode enableULSelection(); } function stopBot() { sessionStorage.removeItem(RUN_KEY); sessionStorage.removeItem(UL_KEY); disableULSelection(); log("Stopped Adopter"); } function isRunning() { return sessionStorage.getItem(RUN_KEY) === "true"; } function getBase() { return sessionStorage.getItem(BASE_KEY); } function getPets() { try { return JSON.parse(sessionStorage.getItem(PETS_KEY)) || []; } catch { return []; } } function savePets(pets) { sessionStorage.setItem(PETS_KEY, JSON.stringify(pets)); } function getIndex() { return parseInt(sessionStorage.getItem(IDX_KEY) || "0", 10); } function setIndex(i) { sessionStorage.setItem(IDX_KEY, String(i)); } function getSelectedUL() { return sessionStorage.getItem(UL_KEY); } function setSelectedUL(selector) { sessionStorage.setItem(UL_KEY, selector); } function enableULSelection() { // Create visual indicators const style = document.createElement("style"); style.id = "adopter-ul-selection-style"; style.textContent = ` .adopter-ul-highlight { outline: 3px solid #ff6600 !important; outline-offset: 2px !important; cursor: pointer !important; position: relative !important; } .adopter-ul-highlight::before { content: "Click to select this list for adoption"; position: absolute; top: -25px; left: 0; background: #ff6600; color: white; padding: 2px 8px; font-size: 12px; border-radius: 3px; white-space: nowrap; z-index: 10000; } .adopter-selection-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.3); z-index: 9999; pointer-events: none; } .adopter-instruction { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: #ff6600; color: white; padding: 20px; border-radius: 8px; font-family: monospace; font-size: 16px; z-index: 10001; text-align: center; box-shadow: 0 4px 20px rgba(0,0,0,0.5); } `; document.head.appendChild(style); // Create overlay const overlay = document.createElement("div"); overlay.className = "adopter-selection-overlay"; overlay.id = "adopter-overlay"; document.body.appendChild(overlay); // Create instruction const instruction = document.createElement("div"); instruction.className = "adopter-instruction"; instruction.id = "adopter-instruction"; instruction.innerHTML = ` <div style="margin-bottom: 10px;"><strong>🏠 Adopter Setup</strong></div> <div>Hover over UL elements and click the one containing the pets you want to adopt</div> <div style="margin-top: 10px; font-size: 12px;">Press ESC to cancel</div> `; document.body.appendChild(instruction); // Find all UL elements and add hover effects const uls = document.querySelectorAll("ul"); uls.forEach((ul) => { ul.addEventListener("mouseenter", handleULHover); ul.addEventListener("mouseleave", handleULLeave); ul.addEventListener("click", handleULClick); }); // ESC to cancel document.addEventListener("keydown", handleEscapeKey); log( `Found ${uls.length} UL elements - hover to highlight, click to select` ); } function disableULSelection() { // Remove style const style = document.getElementById("adopter-ul-selection-style"); if (style) style.remove(); // Remove overlay and instruction const overlay = document.getElementById("adopter-overlay"); if (overlay) overlay.remove(); const instruction = document.getElementById("adopter-instruction"); if (instruction) instruction.remove(); // Remove event listeners from all ULs const uls = document.querySelectorAll("ul"); uls.forEach((ul) => { ul.removeEventListener("mouseenter", handleULHover); ul.removeEventListener("mouseleave", handleULLeave); ul.removeEventListener("click", handleULClick); ul.classList.remove("adopter-ul-highlight"); }); document.removeEventListener("keydown", handleEscapeKey); } function handleULHover(event) { event.target.classList.add("adopter-ul-highlight"); } function handleULLeave(event) { event.target.classList.remove("adopter-ul-highlight"); } function handleULClick(event) { event.preventDefault(); event.stopPropagation(); const ul = event.target; // Remove highlight class before generating selector ul.classList.remove("adopter-ul-highlight"); // Generate a unique selector for this UL let selector = generateUniqueSelector(ul); log(`Selected UL with selector: ${selector}`); // Test the selector immediately const testElement = document.querySelector(selector); if (testElement === ul) { log("✅ Selector test passed"); } else { log("❌ Selector test failed, trying alternative..."); // Try a more specific approach if (ul.parentElement) { const parent = ul.parentElement; if (parent.id) { selector = `#${parent.id} ul:nth-child(${ Array.from(parent.children).indexOf(ul) + 1 })`; } else if (parent.className) { const classes = parent.className.split(" ").filter((c) => c.trim()); selector = `.${classes.join(".")} ul:nth-child(${ Array.from(parent.children).indexOf(ul) + 1 })`; } else { // Ultimate fallback - store the innerHTML signature const signature = ul.innerHTML.substring(0, 100); selector = `ul-signature:${btoa(signature)}`; // Store the actual element reference temporarily window.adopterSelectedUL = ul; log("Using element reference fallback"); } } log(`Alternative selector: ${selector}`); } setSelectedUL(selector); // Clean up selection mode disableULSelection(); // Start collecting pets from the selected UL setTimeout(() => { collectPets(); }, 300); } function handleEscapeKey(event) { if (event.key === "Escape") { log("Selection cancelled"); stopBot(); } } function generateUniqueSelector(element) { // Remove the highlight class before generating selector element.classList.remove("adopter-ul-highlight"); // Try ID first if (element.id) { return `#${element.id}`; } // Try class combination (excluding our highlight class) if (element.className) { const classes = element.className .split(" ") .filter((c) => c.trim() && c !== "adopter-ul-highlight"); if (classes.length > 0) { let selector = `ul.${classes.join(".")}`; if (document.querySelectorAll(selector).length === 1) { return selector; } } } // Try parent-child relationship let parent = element.parentElement; if (parent) { if (parent.id) { return `#${parent.id} > ul`; } if (parent.className) { const classes = parent.className.split(" ").filter((c) => c.trim()); if (classes.length > 0) { let selector = `.${classes.join(".")} > ul`; if (document.querySelectorAll(selector).length === 1) { return selector; } } } } // Try to find position among siblings const siblings = Array.from(element.parentElement?.children || []); const ulSiblings = siblings.filter((el) => el.tagName === "UL"); if (ulSiblings.length === 1) { // Only UL child if (parent && parent.id) { return `#${parent.id} ul`; } if (parent && parent.className) { const classes = parent.className.split(" ").filter((c) => c.trim()); if (classes.length > 0) { return `.${classes.join(".")} ul`; } } } else { // Multiple UL siblings, use nth-of-type const index = ulSiblings.indexOf(element) + 1; if (parent && parent.id) { return `#${parent.id} ul:nth-of-type(${index})`; } if (parent && parent.className) { const classes = parent.className.split(" ").filter((c) => c.trim()); if (classes.length > 0) { return `.${classes.join(".")} ul:nth-of-type(${index})`; } } } // Final fallback: use XPath-like approach function getElementPath(el) { if (el.id) return `#${el.id}`; if (el === document.body) return "body"; const parent = el.parentElement; if (!parent) return el.tagName.toLowerCase(); const siblings = Array.from(parent.children); const sameTagSiblings = siblings.filter( (sibling) => sibling.tagName === el.tagName ); if (sameTagSiblings.length === 1) { return `${getElementPath(parent)} > ${el.tagName.toLowerCase()}`; } else { const index = sameTagSiblings.indexOf(el) + 1; return `${getElementPath( parent )} > ${el.tagName.toLowerCase()}:nth-of-type(${index})`; } } return getElementPath(element); } function collectPets() { if (!isRunning()) return; log("Collecting adoption pets from selected UL"); const ulSelector = getSelectedUL(); if (!ulSelector) { log("No UL selected"); stopBot(); return; } let ul; // Handle special fallback selector if (ulSelector.startsWith("ul-signature:")) { ul = window.adopterSelectedUL; if (!ul) { log("Element reference lost"); stopBot(); return; } } else { ul = document.querySelector(ulSelector); } if (!ul) { log(`Selected UL not found: ${ulSelector}`); stopBot(); return; } log(`Found UL element with ${ul.children.length} child elements`); // Extract all pet links from li elements const petLinks = []; const lis = ul.querySelectorAll("li"); log(`Scanning ${lis.length} li elements for pet links...`); lis.forEach((li, index) => { const petLink = li.querySelector('a.pet[href*="pet="]') || li.querySelector('a[href*="pet="]') || li.querySelector('a[href*="sub=profile"]'); if (petLink && petLink.href) { petLinks.push(petLink.href); log(`Found pet link ${index + 1}: ${petLink.href}`); } }); if (!petLinks.length) { log("No adoption pets found in selected UL"); log("UL HTML preview:", ul.innerHTML.substring(0, 500)); stopBot(); return; } log(`Found ${petLinks.length} adoption pets`); savePets(petLinks); goToPet(); } function goToPet() { if (!isRunning()) return; const pets = getPets(); const idx = getIndex(); if (idx >= pets.length) { log("All pets processed - adoption complete!"); stopBot(); location.href = getBase(); return; } log(`🏠 Going to adopt pet ${idx + 1}/${pets.length}...`); location.href = pets[idx]; } function handlePetProfile() { if (!isRunning()) return; log("🔍 Looking for adoption button on pet profile..."); let tries = 0; const maxTries = 10; const tryAdoptButton = () => { // Look for adoption button let adoptBtn = null; // Method 1: Look for button with "adopt" text const buttons = document.querySelectorAll( 'button, input[type="button"], input[type="submit"]' ); for (let btn of buttons) { if ( btn.textContent.toLowerCase().includes("adopt") || btn.value?.toLowerCase().includes("adopt") || btn.onclick?.toString().includes("adopt") ) { adoptBtn = btn; break; } } // Method 2: Look for links with adopt in onclick if (!adoptBtn) { const links = document.querySelectorAll( 'a[onclick*="adopt"], a[href*="adopt"]' ); if (links.length > 0) { adoptBtn = links[0]; } } if (adoptBtn) { log("✅ Found adoption button, clicking..."); adoptBtn.click(); // Wait for adoption modal or confirmation setTimeout(() => { if (isRunning()) { handleAdoptionModal(); } }, 300); } else if (tries++ < maxTries) { log( `⏳ Adoption button not found, retrying... (${tries}/${maxTries})` ); setTimeout(tryAdoptButton, 300); } else { log("❌ No adoption button found, moving to next pet"); setIndex(getIndex() + 1); setTimeout(() => { if (isRunning()) { goToPet(); } }, 300); } }; // Initial delay to let page load setTimeout(tryAdoptButton, 300); } function handleAdoptionModal() { if (!isRunning()) return; log("🔍 Looking for adoption confirmation modal..."); let modalTries = 0; const maxModalTries = 10; const tryModal = () => { let confirmBtn = null; // Method 1: Look for visible dialog with adoption confirmation const modals = document.querySelectorAll( 'div.ui-dialog[role="dialog"]' ); for (let modal of modals) { if (modal.style.display !== "none" && modal.offsetParent !== null) { // Check if this modal contains adoption-related content if ( modal.innerHTML.includes("adopt") || modal.innerHTML.includes("Adopt") ) { const buttonPane = modal.querySelector(".ui-dialog-buttonpane"); if (buttonPane) { confirmBtn = buttonPane.querySelector("button:first-child"); if (confirmBtn) { log("Found adoption modal confirmation button"); break; } } } } } // Method 2: Look for any visible dialog button as fallback if (!confirmBtn) { const dialogButtons = document.querySelectorAll( ".ui-dialog-buttonpane button" ); for (let btn of dialogButtons) { if (btn.offsetParent !== null) { // visible confirmBtn = btn; log("Found dialog button via visibility fallback"); break; } } } if (confirmBtn) { log("✅ Found adoption confirmation button, clicking..."); confirmBtn.click(); log("🏠 Adoption confirmed! Moving to next pet..."); // Move to next pet setTimeout(() => { if (isRunning()) { setIndex(getIndex() + 1); goToPet(); } }, 300); } else if (modalTries++ < maxModalTries) { log( `⏳ Modal button not found, retrying... (${modalTries}/${maxModalTries})` ); setTimeout(tryModal, 300); } else { log("❌ No modal confirmation button found, moving to next pet"); setIndex(getIndex() + 1); setTimeout(() => { if (isRunning()) { goToPet(); } }, 300); } }; tryModal(); } function main() { if (!isRunning()) return; const href = location.href; const base = getBase(); log("🚀 Adopter main executing...", href); // If we're on a pet profile page if (href.includes("sub=profile") && href.includes("pet=")) { log("📍 On pet profile, looking for adoption button"); setTimeout(() => { if (isRunning()) { handlePetProfile(); } }, 300); } // If we're back at the base page and haven't collected pets yet else if (href === base && !sessionStorage.getItem(PETS_KEY)) { log("📍 At base, need to collect pets"); setTimeout(() => { if (isRunning()) { collectPets(); } }, 300); } // If we're back at the base page and have pets collected else if (href === base) { log("📍 At base, have pets, continuing..."); setTimeout(() => { if (isRunning()) { goToPet(); } }, 300); } // If we're on a different page else { const pets = getPets(); if (pets.length > 0) { log("📍 On different page, going to next pet"); setTimeout(() => { if (isRunning()) { goToPet(); } }, 300); } else { log("📍 No pets collected, returning to base"); location.href = base; } } } return { startBot, stopBot, main }; })(); // --- Breeder Module --- const Breeder = (function () { const RUN_KEY = "breeder_running"; const PETS_KEY = "breeder_pets"; const IDX_KEY = "breeder_index"; const BASE_KEY = "breeder_base_href"; const UL_KEY = "breeder_selected_ul"; const JUST_BREAD = "breeder_just_bred"; function log(...args) { console.log("%c[BREEDER]", "background:#ff1493;color:#fff;", ...args); } function startBot() { log( "Starting Breeder - Please click on the UL element containing the pets" ); // Store the current location as base sessionStorage.setItem(BASE_KEY, location.href); sessionStorage.setItem(RUN_KEY, "true"); sessionStorage.setItem(IDX_KEY, "0"); sessionStorage.setItem(JUST_BREAD, "false"); sessionStorage.removeItem(PETS_KEY); sessionStorage.removeItem(UL_KEY); // Enable UL selection mode enableULSelection(); } function stopBot() { sessionStorage.removeItem(RUN_KEY); sessionStorage.removeItem(UL_KEY); sessionStorage.removeItem(JUST_BREAD); disableULSelection(); log("Stopped Breeder"); } function isRunning() { return sessionStorage.getItem(RUN_KEY) === "true"; } function getBase() { return sessionStorage.getItem(BASE_KEY); } function getPets() { try { return JSON.parse(sessionStorage.getItem(PETS_KEY)) || []; } catch { return []; } } function savePets(pets) { sessionStorage.setItem(PETS_KEY, JSON.stringify(pets)); } function getIndex() { return parseInt(sessionStorage.getItem(IDX_KEY) || "0", 10); } function setIndex(i) { sessionStorage.setItem(IDX_KEY, String(i)); } function getSelectedUL() { return sessionStorage.getItem(UL_KEY); } function setSelectedUL(selector) { sessionStorage.setItem(UL_KEY, selector); } function enableULSelection() { // Create visual indicators const style = document.createElement("style"); style.id = "breeder-ul-selection-style"; style.textContent = ` .breeder-ul-highlight { outline: 3px solid #ff1493 !important; outline-offset: 2px !important; cursor: pointer !important; position: relative !important; } .breeder-ul-highlight::before { content: "Click to select this list for breeding"; position: absolute; top: -25px; left: 0; background: #ff1493; color: white; padding: 2px 8px; font-size: 12px; border-radius: 3px; white-space: nowrap; z-index: 10000; } .breeder-selection-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.3); z-index: 9999; pointer-events: none; } .breeder-instruction { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: #ff1493; color: white; padding: 20px; border-radius: 8px; font-family: monospace; font-size: 16px; z-index: 10001; text-align: center; box-shadow: 0 4px 20px rgba(0,0,0,0.5); } `; document.head.appendChild(style); // Create overlay const overlay = document.createElement("div"); overlay.className = "breeder-selection-overlay"; overlay.id = "breeder-overlay"; document.body.appendChild(overlay); // Create instruction const instruction = document.createElement("div"); instruction.className = "breeder-instruction"; instruction.id = "breeder-instruction"; instruction.innerHTML = ` <div style="margin-bottom: 10px;"><strong>💕 Breeder Setup</strong></div> <div>Hover over UL elements and click the one containing the pets you want to breed</div> <div style="margin-top: 10px; font-size: 12px;">Press ESC to cancel</div> `; document.body.appendChild(instruction); // Find all UL elements and add hover effects const uls = document.querySelectorAll("ul"); uls.forEach((ul) => { ul.addEventListener("mouseenter", handleULHover); ul.addEventListener("mouseleave", handleULLeave); ul.addEventListener("click", handleULClick); }); // ESC to cancel document.addEventListener("keydown", handleEscapeKey); log( `Found ${uls.length} UL elements - hover to highlight, click to select` ); } function disableULSelection() { // Remove style const style = document.getElementById("breeder-ul-selection-style"); if (style) style.remove(); // Remove overlay and instruction const overlay = document.getElementById("breeder-overlay"); if (overlay) overlay.remove(); const instruction = document.getElementById("breeder-instruction"); if (instruction) instruction.remove(); // Remove event listeners from all ULs const uls = document.querySelectorAll("ul"); uls.forEach((ul) => { ul.removeEventListener("mouseenter", handleULHover); ul.removeEventListener("mouseleave", handleULLeave); ul.removeEventListener("click", handleULClick); ul.classList.remove("breeder-ul-highlight"); }); document.removeEventListener("keydown", handleEscapeKey); } function handleULHover(event) { event.target.classList.add("breeder-ul-highlight"); } function handleULLeave(event) { event.target.classList.remove("breeder-ul-highlight"); } function handleULClick(event) { event.preventDefault(); event.stopPropagation(); const ul = event.target; // Remove highlight class before generating selector ul.classList.remove("breeder-ul-highlight"); // Generate a unique selector for this UL let selector = generateUniqueSelector(ul); log(`Selected UL with selector: ${selector}`); // Test the selector immediately const testElement = document.querySelector(selector); if (testElement === ul) { log("✅ Selector test passed"); } else { log("❌ Selector test failed, trying alternative..."); // Try a more specific approach if (ul.parentElement) { const parent = ul.parentElement; if (parent.id) { selector = `#${parent.id} ul:nth-child(${ Array.from(parent.children).indexOf(ul) + 1 })`; } else if (parent.className) { const classes = parent.className.split(" ").filter((c) => c.trim()); selector = `.${classes.join(".")} ul:nth-child(${ Array.from(parent.children).indexOf(ul) + 1 })`; } else { // Ultimate fallback - store the innerHTML signature const signature = ul.innerHTML.substring(0, 100); selector = `ul-signature:${btoa(signature)}`; // Store the actual element reference temporarily window.breederSelectedUL = ul; log("Using element reference fallback"); } } log(`Alternative selector: ${selector}`); } setSelectedUL(selector); // Clean up selection mode disableULSelection(); // Start collecting pets from the selected UL setTimeout(() => { collectPets(); }, 300); } function handleEscapeKey(event) { if (event.key === "Escape") { log("Selection cancelled"); stopBot(); } } function generateUniqueSelector(element) { // Remove the highlight class before generating selector element.classList.remove("breeder-ul-highlight"); // Try ID first if (element.id) { return `#${element.id}`; } // Try class combination (excluding our highlight class) if (element.className) { const classes = element.className .split(" ") .filter((c) => c.trim() && c !== "breeder-ul-highlight"); if (classes.length > 0) { let selector = `ul.${classes.join(".")}`; if (document.querySelectorAll(selector).length === 1) { return selector; } } } // Try parent-child relationship let parent = element.parentElement; if (parent) { if (parent.id) { return `#${parent.id} > ul`; } if (parent.className) { const classes = parent.className.split(" ").filter((c) => c.trim()); if (classes.length > 0) { let selector = `.${classes.join(".")} > ul`; if (document.querySelectorAll(selector).length === 1) { return selector; } } } } // Try to find position among siblings const siblings = Array.from(element.parentElement?.children || []); const ulSiblings = siblings.filter((el) => el.tagName === "UL"); if (ulSiblings.length === 1) { // Only UL child if (parent && parent.id) { return `#${parent.id} ul`; } if (parent && parent.className) { const classes = parent.className.split(" ").filter((c) => c.trim()); if (classes.length > 0) { return `.${classes.join(".")} ul`; } } } else { // Multiple UL siblings, use nth-of-type const index = ulSiblings.indexOf(element) + 1; if (parent && parent.id) { return `#${parent.id} ul:nth-of-type(${index})`; } if (parent && parent.className) { const classes = parent.className.split(" ").filter((c) => c.trim()); if (classes.length > 0) { return `.${classes.join(".")} ul:nth-of-type(${index})`; } } } // Final fallback: use XPath-like approach function getElementPath(el) { if (el.id) return `#${el.id}`; if (el === document.body) return "body"; const parent = el.parentElement; if (!parent) return el.tagName.toLowerCase(); const siblings = Array.from(parent.children); const sameTagSiblings = siblings.filter( (sibling) => sibling.tagName === el.tagName ); if (sameTagSiblings.length === 1) { return `${getElementPath(parent)} > ${el.tagName.toLowerCase()}`; } else { const index = sameTagSiblings.indexOf(el) + 1; return `${getElementPath( parent )} > ${el.tagName.toLowerCase()}:nth-of-type(${index})`; } } return getElementPath(element); } function collectPets() { if (!isRunning()) return; log("Collecting breeding pets from selected UL"); const ulSelector = getSelectedUL(); if (!ulSelector) { log("No UL selected"); stopBot(); return; } let ul; // Handle special fallback selector if (ulSelector.startsWith("ul-signature:")) { ul = window.breederSelectedUL; if (!ul) { log("Element reference lost"); stopBot(); return; } } else { ul = document.querySelector(ulSelector); } if (!ul) { log(`Selected UL not found: ${ulSelector}`); stopBot(); return; } log(`Found UL element with ${ul.children.length} child elements`); // Extract all pet links from li elements const petLinks = []; const lis = ul.querySelectorAll("li"); log(`Scanning ${lis.length} li elements for pet links...`); lis.forEach((li, index) => { const petLink = li.querySelector('a.pet[href*="pet="]') || li.querySelector('a[href*="pet="]') || li.querySelector('a[href*="sub=profile"]'); if (petLink && petLink.href) { petLinks.push(petLink.href); log(`Found pet link ${index + 1}: ${petLink.href}`); } }); if (!petLinks.length) { log("No breeding pets found in selected UL"); log("UL HTML preview:", ul.innerHTML.substring(0, 500)); stopBot(); return; } log(`Found ${petLinks.length} breeding pets`); savePets(petLinks); goToPet(); } function goToPet() { if (!isRunning()) return; const pets = getPets(); const idx = getIndex(); if (idx >= pets.length) { log("All pets processed - breeding complete!"); stopBot(); location.href = getBase(); return; } log(`🐾 Going to pet ${idx + 1}/${pets.length}...`); location.href = pets[idx]; } function handlePetProfile() { if (!isRunning()) return; log("🔍 Looking for breeding tab on pet profile..."); let tries = 0; const maxTries = 1; // Increased wait time const tryBreedingTab = () => { if (!isRunning()) return; // Try multiple selectors for the breeding tab let breedingTab = null; // Method 1: Look for the specific li with breeding panel attribute const breedingLi = document.querySelector( '#tabs > div > form > div > ul > li[panel*="sec=breeding"]' ); if (breedingLi) { breedingTab = breedingLi.querySelector("a.ui-tabs-anchor"); } // Method 2: Look for tab with "Breeding" text if (!breedingTab) { const tabs = document.querySelectorAll("#tabs a.ui-tabs-anchor"); for (let tab of tabs) { if (tab.textContent.trim().toLowerCase().includes("breeding")) { breedingTab = tab; break; } } } // Method 3: Try the specific selector you provided if (!breedingTab) { breedingTab = document.querySelector("#ui-id-35"); } // Method 4: Look for any tab with breeding in the href if (!breedingTab) { const allTabs = document.querySelectorAll( '#tabs a[href*="breeding"], #tabs a[href*="Breeding"]' ); if (allTabs.length > 0) { breedingTab = allTabs[0]; } } if (breedingTab) { log("✅ Found breeding tab, clicking..."); breedingTab.click(); // Wait longer for breeding section to load setTimeout(() => { if (isRunning()) { handleBreedingSection(); } }, 300); // Increased wait time } else if (tries++ < maxTries) { log(`⏳ Breeding tab not found, retrying... (${tries}/${maxTries})`); setTimeout(tryBreedingTab, 300); // Wait longer between retries } else { log("❌ No breeding tab found after all attempts, skipping this pet"); setIndex(getIndex() + 1); setTimeout(() => { if (isRunning()) { goToPet(); } }, 300); } }; // Initial delay to let page load setTimeout(tryBreedingTab, 300); } // Replace the handleBreedingModal function in the Breeder module: function handleBreedingModal() { if (!isRunning()) return; log("🔍 Looking for breeding confirmation modal..."); let modalTries = 0; const maxModalTries = 15; const tryModal = () => { if (!isRunning()) return; let confirmBtn = null; // Method 1: Look for the specific breeding modal button confirmBtn = document.querySelector( "body > div.ui-dialog.ui-corner-all.ui-widget.ui-widget-content.ui-front.ui-dialog-buttons.ui-draggable.ui-resizable > div.ui-dialog-buttonpane.ui-widget-content.ui-helper-clearfix > div > button:nth-child(1)" ); // Method 2: Look for any visible dialog with breeding confirmation if (!confirmBtn) { const modals = document.querySelectorAll( 'div.ui-dialog[role="dialog"]' ); for (let modal of modals) { if (modal.style.display !== "none" && modal.offsetParent !== null) { // Check if this modal contains breeding-related content if ( modal.innerHTML.includes("breed") || modal.innerHTML.includes("Breed") ) { const buttonPane = modal.querySelector(".ui-dialog-buttonpane"); if (buttonPane) { confirmBtn = buttonPane.querySelector("button:first-child"); if (confirmBtn) { log( "Found breeding modal confirmation button via content search" ); break; } } } } } } // Method 3: Look for any visible dialog button as fallback if (!confirmBtn) { const dialogButtons = document.querySelectorAll( ".ui-dialog-buttonpane button" ); for (let btn of dialogButtons) { if (btn.offsetParent !== null) { // visible confirmBtn = btn; log("Found dialog button via visibility fallback"); break; } } } if (confirmBtn) { log("✅ Found breeding confirmation button, clicking..."); // set just bred flag sessionStorage.setItem(JUST_BREAD, "true"); confirmBtn.click(); log( "💕 Breeding confirmed! Staying on same pet to find more partners..." ); } else if (modalTries++ < maxModalTries) { log( `⏳ Modal button not found, retrying... (${modalTries}/${maxModalTries})` ); setTimeout(tryModal, 300); } else { log("❌ No modal confirmation button found, moving to next pet"); setIndex(getIndex() + 1); setTimeout(() => { if (isRunning()) { goToPet(); } }, 300); } }; tryModal(); } // Also update the handleBreedingSection function to handle "no more partners" case: function handleBreedingSection() { if (!isRunning()) return; log("💕 Looking for breeding partners..."); let attempts = 0; const maxAttempts = 1; const findBreedingPartners = () => { if (!isRunning()) return; // look for a feeding link /* <button type="button" onclick="var self = this; ui_action_cmdExec('pet_feed','PetID=469843813',self.form,function(){ui_action_secLoad('pets','profile','profile','pet=469843813&prio=1','',self);});" class="ui-button ui-corner-all ui-widget"><span>Feed</span></button> */ // wait 100 ms setTimeout(() => { if (!isRunning()) return; }, 100); let feedingButton = document.querySelector( 'button[onclick*="pet_feed"]' ); if (feedingButton) { log("✅ Found feeding button, clicking..."); feedingButton.click(); } // find this selector // <select name="enclosure" onchange="var self = this; ui_action_secLoad('pets','profile','breeding','usr=5262473&pet=476473623','',self);"><option value="0">hate these thing </option><option value="1">aye </option></select> // then for each item in the select, select the item, then check the breeding section log("🔍 looking for breeding tabs"); var breedingSelector = document.querySelector( 'select[name="enclosure"]' ); var breedingLinks = []; if (breedingSelector) { log("✅ Found breeding selector, iterating options..."); const options = breedingSelector.querySelectorAll("option"); options.forEach((option) => { option.selected = true; log(`🔄 Selected breeding option: ${option.textContent}`); }); // Try multiple selectors for the breeding UL let breedingUL = document.querySelector( "#breeding > div > form > ul" ); if (!breedingUL) { // Fallback: look for any UL with breeding-related links const allULs = document.querySelectorAll("ul"); for (let ul of allULs) { if (ul.querySelector('a[onclick*="pet_breed"]')) { breedingUL = ul; log("Found breeding UL via fallback search"); break; } } } if (!breedingUL && attempts++ < maxAttempts) { log( `⏳ Breeding section not loaded yet, retrying... (${attempts}/${maxAttempts})` ); setTimeout(findBreedingPartners, 300); return; } if (!breedingUL) { log("❌ No breeding UL found, moving to next pet"); setIndex(getIndex() + 1); setTimeout(() => { if (isRunning()) { goToPet(); } }, 300); return; } // Look for available breeding partners breedingLinks = breedingUL.querySelectorAll( 'a[onclick*="pet_breed"]' ); if (!breedingLinks.length) { log( "✅ No more breeding partners available for this pet, moving to next pet" ); setIndex(getIndex() + 1); setTimeout(() => { if (isRunning()) { goToPet(); } }, 300); return; } } else { // Try multiple selectors for the breeding UL let breedingUL = document.querySelector( "#breeding > div > form > ul" ); if (!breedingUL) { // Fallback: look for any UL with breeding-related links const allULs = document.querySelectorAll("ul"); for (let ul of allULs) { if (ul.querySelector('a[onclick*="pet_breed"]')) { breedingUL = ul; log("Found breeding UL via fallback search"); break; } } } if (!breedingUL && attempts++ < maxAttempts) { log( `⏳ Breeding section not loaded yet, retrying... (${attempts}/${maxAttempts})` ); setTimeout(findBreedingPartners, 300); return; } if (!breedingUL) { log("❌ No breeding UL found, moving to next pet"); setIndex(getIndex() + 1); setTimeout(() => { if (isRunning()) { goToPet(); } }, 300); return; } // Look for available breeding partners const breedingLinks = breedingUL.querySelectorAll( 'a[onclick*="pet_breed"]' ); if (!breedingLinks.length) { log( "✅ No more breeding partners available for this pet, moving to next pet" ); setIndex(getIndex() + 1); setTimeout(() => { if (isRunning()) { goToPet(); } }, 300); return; } } // Process the first available breeding partner const firstBreedingLink = breedingLinks[0]; log( `🎯 Found ${breedingLinks.length} breeding partners, clicking first one...` ); firstBreedingLink.click(); // Wait for modal to appear setTimeout(() => { if (isRunning()) { handleBreedingModal(); } }, 300); if (breedingLinks.length > 1) { log( `🔄 Found ${ breedingLinks.length - 1 } more breeding partners, will continue after this one` ); // to repeat this pet after breeding, set the index back once setIndex(getIndex() - 1); } }; findBreedingPartners(); } function main() { if (!isRunning()) return; const href = location.href; const base = getBase(); var counter = 0; let feedingButton = document.querySelector('button[onclick*="pet_feed"]'); if (feedingButton) { log("✅ Found feeding button, clicking..."); feedingButton.click(); } log("🚀 Breeder main executing...", href); // if just bred, go back to breeding section if (sessionStorage.getItem(JUST_BREAD) === "true") { log("🔙 Just bred, going back to breeding section..."); // set just bred flag to false if (counter > 0) { counter = 0; sessionStorage.setItem(JUST_BREAD, "false"); setTimeout(() => { if (isRunning()) { history.back(); } }, 300); setTimeout(() => { if (isRunning()) { handlePetProfile(); } }, 400); } else { counter++; } } // If we're on a pet profile page if (href.includes("sub=profile") && href.includes("pet=")) { // Check if we're already on the breeding tab let feedingButton = document.querySelector( 'button[onclick*="pet_feed"]' ); if (feedingButton) { log("✅ Found feeding button, clicking..."); feedingButton.click(); } const breedingSection = document.querySelector("#breeding"); if (breedingSection) { log("📍 Already on breeding section, looking for partners..."); setTimeout(() => { if (isRunning()) { handleBreedingSection(); } }, 300); } else { let feedingButton = document.querySelector( 'button[onclick*="pet_feed"]' ); if (feedingButton) { log("✅ Found feeding button, clicking..."); feedingButton.click(); } log("📍 On pet profile, need to click breeding tab"); setTimeout(() => { if (isRunning()) { handlePetProfile(); } }, 300); } } // If we're back at the base page and haven't collected pets yet else if (href === base && !sessionStorage.getItem(PETS_KEY)) { log("📍 At base, need to collect pets"); setTimeout(() => { if (isRunning()) { collectPets(); } }, 300); } // If we're back at the base page and have pets collected else if (href === base) { log("📍 At base, have pets, continuing..."); setTimeout(() => { if (isRunning()) { goToPet(); } }, 300); } // If we're on a different page, go to current pet (not next!) else { const pets = getPets(); if (pets.length > 0) { log("📍 On different page, going to current pet"); setTimeout(() => { if (isRunning()) { // Don't increment index - go to current pet const currentPetUrl = pets[getIndex()]; if (currentPetUrl) { log(`🔄 Returning to current pet: ${currentPetUrl}`); location.href = currentPetUrl; } else { goToPet(); } } }, 300); } else { log("📍 No pets collected, returning to base"); location.href = base; } } } return { startBot, stopBot, main }; })(); // --- Namer Module --- const Namer = (function () { const RUN_KEY = "namer_running"; const PETS_KEY = "namer_pets"; const IDX_KEY = "namer_index"; const BASE_KEY = "namer_base_href"; const UL_KEY = "namer_selected_ul"; const NAME = "namer_name"; function log(...args) { console.log("%c[NAMER]", "background:#9932cc;color:#fff;", ...args); } function startBot() { log( "Starting Namer - Please click on the UL element containing the pets to name" ); // Store the current location as base sessionStorage.setItem(BASE_KEY, location.href); sessionStorage.setItem(RUN_KEY, "true"); sessionStorage.setItem(IDX_KEY, "0"); sessionStorage.removeItem(PETS_KEY); sessionStorage.removeItem(UL_KEY); sessionStorage.removeItem(NAME); // Enable UL selection mode enableULSelection(); } function stopBot() { sessionStorage.removeItem(RUN_KEY); sessionStorage.removeItem(UL_KEY); disableULSelection(); log("Stopped Namer"); } function isRunning() { return sessionStorage.getItem(RUN_KEY) === "true"; } function getBase() { return sessionStorage.getItem(BASE_KEY); } function getPets() { try { return JSON.parse(sessionStorage.getItem(PETS_KEY)) || []; } catch { return []; } } function savePets(pets) { sessionStorage.setItem(PETS_KEY, JSON.stringify(pets)); } function getIndex() { return parseInt(sessionStorage.getItem(IDX_KEY) || "0", 10); } function setIndex(i) { sessionStorage.setItem(IDX_KEY, String(i)); } function getSelectedUL() { return sessionStorage.getItem(UL_KEY); } function setSelectedUL(selector) { sessionStorage.setItem(UL_KEY, selector); } function enableULSelection() { // Create visual indicators const style = document.createElement("style"); style.id = "namer-ul-selection-style"; style.textContent = ` .namer-ul-highlight { outline: 3px solid #9932cc !important; outline-offset: 2px !important; cursor: pointer !important; position: relative !important; } .namer-ul-highlight::before { content: "Click to select this list for naming"; position: absolute; top: -25px; left: 0; background: #9932cc; color: white; padding: 2px 8px; font-size: 12px; border-radius: 3px; white-space: nowrap; z-index: 10000; } .namer-selection-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.3); z-index: 9999; pointer-events: none; } .namer-instruction { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: #9932cc; color: white; padding: 20px; border-radius: 8px; font-family: monospace; font-size: 16px; z-index: 10001; text-align: center; box-shadow: 0 4px 20px rgba(0,0,0,0.5); } `; document.head.appendChild(style); // Create overlay const overlay = document.createElement("div"); overlay.className = "namer-selection-overlay"; overlay.id = "namer-overlay"; document.body.appendChild(overlay); // Create instruction const instruction = document.createElement("div"); instruction.className = "namer-instruction"; instruction.id = "namer-instruction"; instruction.innerHTML = ` <div style="margin-bottom: 10px;"><strong>📝 Namer Setup</strong></div> <div>Hover over UL elements and click the one containing the pets you want to name</div> <div style="margin-top: 10px; font-size: 12px;">Press ESC to cancel</div> `; document.body.appendChild(instruction); // Find all UL elements and add hover effects const uls = document.querySelectorAll("ul"); uls.forEach((ul) => { ul.addEventListener("mouseenter", handleULHover); ul.addEventListener("mouseleave", handleULLeave); ul.addEventListener("click", handleULClick); }); // ESC to cancel document.addEventListener("keydown", handleEscapeKey); log( `Found ${uls.length} UL elements - hover to highlight, click to select` ); } function disableULSelection() { // Remove style const style = document.getElementById("namer-ul-selection-style"); if (style) style.remove(); // Remove overlay and instruction const overlay = document.getElementById("namer-overlay"); if (overlay) overlay.remove(); const instruction = document.getElementById("namer-instruction"); if (instruction) instruction.remove(); // Remove event listeners from all ULs const uls = document.querySelectorAll("ul"); uls.forEach((ul) => { ul.removeEventListener("mouseenter", handleULHover); ul.removeEventListener("mouseleave", handleULLeave); ul.removeEventListener("click", handleULClick); ul.classList.remove("namer-ul-highlight"); }); document.removeEventListener("keydown", handleEscapeKey); } function handleULHover(event) { const ul = event.target; ul.classList.add("namer-ul-highlight"); // Add debug info overlay const debugInfo = document.createElement("div"); debugInfo.id = "namer-debug-info"; debugInfo.style.cssText = ` position: fixed; top: 10px; right: 10px; background: #9932cc; color: white; padding: 10px; border-radius: 5px; font-family: monospace; font-size: 12px; z-index: 10002; max-width: 300px; `; const petLinks = ul.querySelectorAll('a.pet[href*="pet="]').length; const totalLis = ul.querySelectorAll("li").length; debugInfo.innerHTML = ` <strong>🔍 UL Preview</strong><br> Total LIs: ${totalLis}<br> Pet Links: ${petLinks}<br> ${ petLinks > 0 ? "✅ Contains pet links!" : "❌ No pet links found" } `; document.body.appendChild(debugInfo); } function handleULLeave(event) { event.target.classList.remove("namer-ul-highlight"); // Remove debug info const debugInfo = document.getElementById("namer-debug-info"); if (debugInfo) debugInfo.remove(); } function handleULClick(event) { event.preventDefault(); event.stopPropagation(); const ul = event.target; // Remove highlight class ul.classList.remove("namer-ul-highlight"); log(`Selected UL with ${ul.children.length} children`); // Clean up selection mode first disableULSelection(); // ask what the name should be with an alert prompt const name = prompt("Enter the name to use for all pets:", "cute guy"); if (name) { sessionStorage.setItem(NAME, name); log(`Using name: ${name}`); } else { log('No name provided, using default "cute guy"'); sessionStorage.setItem(NAME, "cute guy"); } // Collect pets immediately from the selected UL collectPetsFromElement(ul); } function collectPetsFromElement(ul) { if (!isRunning()) return; log("Collecting pet links from selected UL"); if (!ul) { log("No UL element provided"); stopBot(); return; } log(`Found UL element with ${ul.children.length} child elements`); // Extract pet links specifically const petLinks = []; const lis = ul.querySelectorAll("li"); log(`Scanning ${lis.length} li elements for pet links...`); lis.forEach((li, index) => { // Look for pet links - using the pattern from your example const petLink = li.querySelector('a.pet[href*="pet="]') || li.querySelector('a[href*="pet="]') || li.querySelector('a[href*="sub=profile"]'); if (petLink && petLink.href) { const href = petLink.href; // Check if it's a pet profile link (not a user link) const isPetLink = href.includes("pet=") && href.includes("sub=profile"); if (isPetLink) { petLinks.push(href); log(`Found pet link ${index + 1}: ${href}`); } } }); if (!petLinks.length) { log("No pet links found in selected UL"); log("UL HTML preview:", ul.innerHTML.substring(0, 500)); // Debug what we actually found log("Debug: Found links in UL:"); const allLinks = ul.querySelectorAll("a"); allLinks.forEach((link, i) => { log(` Link ${i + 1}: href="${link.href}" class="${link.className}"`); }); stopBot(); return; } log(`Found ${petLinks.length} pets to name`); savePets(petLinks); goToPet(); } function handleEscapeKey(event) { if (event.key === "Escape") { log("Selection cancelled"); stopBot(); } } function goToPet() { if (!isRunning()) return; const pets = getPets(); const idx = getIndex(); if (idx >= pets.length) { log("All pets named! Naming complete!"); stopBot(); location.href = getBase(); return; } log(`📝 Going to name pet ${idx + 1}/${pets.length}...`); location.href = pets[idx]; } function handlePetProfile() { if (!isRunning()) return; // while a close button is present, click it // <span class="ui-button-icon ui-icon ui-icon-closethick"></span> while (document.querySelector(".ui-button-icon.ui-icon-closethick")) { const closeBtn = document.querySelector( ".ui-button-icon.ui-icon-closethick" ); log("🔄 Found close button, clicking to close any open modals..."); closeBtn.click(); } log("🔍 Looking for naming button on pet profile..."); // Single attempt with minimal wait - no retries setTimeout(() => { const namingBtn = document.querySelector( "#profile > div > form > fieldset.ui-fieldset.ui-widget-content.ui-corner-all.actions > div > div > button" ); if (namingBtn) { log("✅ Found naming button, clicking..."); namingBtn.click(); // Wait for modal to appear with fixed delay setTimeout(() => { if (isRunning()) { handleNamingModal(); } }, 250); } else { log("❌ No naming button found, moving to next pet"); setIndex(getIndex() + 1); setTimeout(() => { if (isRunning()) { goToPet(); } }, 50); } }, 100); } function handleNamingModal() { if (!isRunning()) return; log("🔍 Looking for naming modal..."); // Single attempt - modal should be ready by now const nameInput = document.querySelector( "#dialog > fieldset > div > div > input[type=text]" ); if (nameInput) { log("✅ Found name input field, entering name..."); // Clear existing text and type new name nameInput.value = ""; nameInput.focus(); nameInput.value = sessionStorage.getItem(NAME) || "cute guy"; // Trigger input events to ensure the change is registered nameInput.dispatchEvent(new Event("input", { bubbles: true })); nameInput.dispatchEvent(new Event("change", { bubbles: true })); // click this body > div.ui-dialog.ui-corner-all.ui-widget.ui-widget-content.ui-front.ui-dialog-buttons.ui-draggable.ui-resizable > div.ui-dialog-buttonpane.ui-widget-content.ui-helper-clearfix // just to be sure lol document .querySelector( "body > div.ui-dialog.ui-corner-all.ui-widget.ui-widget-content.ui-front.ui-dialog-buttons.ui-draggable.ui-resizable > div.ui-dialog-buttonpane.ui-widget-content.ui-helper-clearfix" ) .click(); log("📝 Name entered, looking for confirm button..."); // Minimal wait then look for confirm button setTimeout(() => { const confirmBtn = document.querySelector( "body > div.ui-dialog.ui-corner-all.ui-widget.ui-widget-content.ui-front.ui-dialog-buttons.ui-draggable.ui-resizable > div.ui-dialog-buttonpane.ui-widget-content.ui-helper-clearfix > div > button:nth-child(1)" ); if (confirmBtn) { log("✅ Found confirm button, clicking..."); confirmBtn.click(); log("🎉 Pet named successfully! Moving to next pet..."); // Move to next pet with minimal delay setTimeout(() => { if (isRunning()) { setIndex(getIndex() + 1); goToPet(); } }, 25); } else { log("❌ No confirm button found, moving to next pet"); setIndex(getIndex() + 1); setTimeout(() => { if (isRunning()) { goToPet(); } }, 50); } }, 100); } else { log("❌ No modal input found, moving to next pet"); setIndex(getIndex() + 1); setTimeout(() => { if (isRunning()) { goToPet(); } }, 50); } } function main() { if (!isRunning()) return; const href = location.href; const base = getBase(); log("🚀 Namer main executing...", href); // If we're on a pet profile page if (href.includes("sub=profile") && href.includes("pet=")) { log("📍 On pet profile, looking for naming button"); handlePetProfile(); } // If we're back at the base page and haven't collected pets yet else if (href === base && !sessionStorage.getItem(PETS_KEY)) { log("📍 At base, need to collect pets"); setTimeout(() => { if (isRunning()) { // Re-enable UL selection enableULSelection(); } }, 50); } // If we're back at the base page and have pets collected else if (href === base) { log("📍 At base, have pets, continuing..."); setTimeout(() => { if (isRunning()) { goToPet(); } }, 50); } // If we're on a different page else { const pets = getPets(); if (pets.length > 0) { log("📍 On different page, going to next pet"); setTimeout(() => { if (isRunning()) { goToPet(); } }, 50); } else { log("📍 No pets collected, returning to base"); location.href = base; } } } return { startBot, stopBot, main }; })(); // --- Image Collector Module --- const ImageCollector = (function () { const RUN_KEY = "image_collector_running"; const SPECIES_KEY = "image_collector_species"; const COUNT_KEY = "image_collector_count"; // List of species that the puzzle solver can recognize const SPECIES_OPTIONS = [ "Canis", "Draconis", "Equus", "Feline", "Gekko", "Lupus", "Mantis", "Raptor", "Vulpes", ]; function log(...args) { console.log( "%c[IMAGE-COLLECTOR]", "background:#ff4500;color:#fff;", ...args ); } function startBot() { log("Starting Image Collector"); // Show species selection modal showSpeciesSelection(); } function stopBot() { sessionStorage.removeItem(RUN_KEY); sessionStorage.removeItem(SPECIES_KEY); sessionStorage.removeItem(COUNT_KEY); log("Stopped Image Collector"); } function isRunning() { return sessionStorage.getItem(RUN_KEY) === "true"; } function getSelectedSpecies() { return sessionStorage.getItem(SPECIES_KEY); } function setSelectedSpecies(species) { sessionStorage.setItem(SPECIES_KEY, species); } function getImageCount() { return parseInt(sessionStorage.getItem(COUNT_KEY) || "0", 10); } function setImageCount(count) { sessionStorage.setItem(COUNT_KEY, String(count)); } function incrementImageCount() { const newCount = getImageCount() + 1; setImageCount(newCount); return newCount; } function showSpeciesSelection() { // Create modal overlay const overlay = document.createElement("div"); overlay.id = "image-collector-species-overlay"; overlay.style.cssText = ` position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.8); z-index: 10000; display: flex; align-items: center; justify-content: center; `; // Create modal content const modal = document.createElement("div"); modal.style.cssText = ` background: #ff4500; color: white; padding: 30px; border-radius: 10px; font-family: monospace; max-width: 500px; max-height: 80vh; overflow-y: auto; box-shadow: 0 4px 20px rgba(0,0,0,0.5); `; modal.innerHTML = ` <div style="margin-bottom: 20px; text-align: center;"> <h2 style="margin: 0 0 10px 0;">📸 Image Collector</h2> <p style="margin: 0; font-size: 14px;">Select the species you want to collect images for:</p> </div> <div id="species-list" style="margin-bottom: 20px; max-height: 300px; overflow-y: auto;"> ${SPECIES_OPTIONS.map( (species) => ` <div style="margin: 5px 0;"> <label style="display: flex; align-items: center; cursor: pointer; padding: 8px; border-radius: 5px; transition: background-color 0.2s;" onmouseover="this.style.backgroundColor='rgba(255,255,255,0.1)'" onmouseout="this.style.backgroundColor='transparent'"> <input type="radio" name="species" value="${species}" style="margin-right: 10px;"> <span>${species}</span> </label> </div> ` ).join("")} </div> <div style="text-align: center;"> <button id="species-confirm" style="margin: 5px; padding: 10px 20px; font-size: 14px; background: white; color: #ff4500; border: none; border-radius: 5px; cursor: pointer;">Start Collecting</button> <button id="species-cancel" style="margin: 5px; padding: 10px 20px; font-size: 14px; background: #333; color: white; border: none; border-radius: 5px; cursor: pointer;">Cancel</button> </div> `; overlay.appendChild(modal); document.body.appendChild(overlay); // Handle confirm button document.getElementById("species-confirm").onclick = () => { const selectedRadio = document.querySelector( 'input[name="species"]:checked' ); if (selectedRadio) { const species = selectedRadio.value; setSelectedSpecies(species); setImageCount(0); sessionStorage.setItem(RUN_KEY, "true"); log(`Selected species: ${species}`); // Remove modal overlay.remove(); // Start the collection process startCollectionLoop(); } else { alert("Please select a species first!"); } }; // Handle cancel button document.getElementById("species-cancel").onclick = () => { overlay.remove(); log("Species selection cancelled"); }; // Handle ESC key document.addEventListener("keydown", function escHandler(event) { if (event.key === "Escape") { overlay.remove(); document.removeEventListener("keydown", escHandler); log("Species selection cancelled"); } }); } function startCollectionLoop() { if (!isRunning()) return; const species = getSelectedSpecies(); log(`🔄 Starting collection loop for ${species}...`); // Start the main collection process setTimeout(() => { if (isRunning()) { collectCurrentImage(); } }, 100); } function collectCurrentImage() { if (!isRunning()) return; const species = getSelectedSpecies(); const currentCount = incrementImageCount(); const paddedCount = String(currentCount).padStart(4, "0"); const filename = `${species}_${paddedCount}`; log(`📸 Collecting image ${currentCount} for ${species}...`); // Find the image element const imageElement = document.querySelector( "#visualizer > div > form > fieldset.ui-fieldset.ui-widget-content.ui-corner-all.display > div > img" ); if (!imageElement) { log( "❌ Image element not found. Make sure you are on the visualizer page." ); stopBot(); return; } // Get the image URL and download it const imageUrl = imageElement.src; if (imageUrl) { downloadImage(imageUrl, filename) .then(() => { log(`✅ Image saved as ${filename}`); // Wait a moment then click randomize button setTimeout(() => { if (isRunning()) { clickRandomizeButton(); } }, 100); }) .catch((error) => { log(`❌ Failed to save image: ${error}`); // Continue anyway setTimeout(() => { if (isRunning()) { clickRandomizeButton(); } }, 100); }); } else { log("❌ No image URL found"); setTimeout(() => { if (isRunning()) { clickRandomizeButton(); } }, 100); } } function downloadImage(url, filename) { return new Promise((resolve, reject) => { // Convert relative URL to absolute if needed const imageUrl = url.startsWith("//") ? `https:${url}` : url; GM_xmlhttpRequest({ method: "GET", url: imageUrl, responseType: "blob", onload: function (response) { try { // Create blob URL const blob = response.response; const blobUrl = URL.createObjectURL(blob); // Create download link const downloadLink = document.createElement("a"); downloadLink.href = blobUrl; downloadLink.download = `${filename}.jpg`; downloadLink.style.display = "none"; // Add to document and click document.body.appendChild(downloadLink); downloadLink.click(); // Clean up document.body.removeChild(downloadLink); URL.revokeObjectURL(blobUrl); resolve(); } catch (error) { reject(error); } }, onerror: function (error) { reject(error); }, }); }); } function clickRandomizeButton() { if (!isRunning()) return; log("🎲 Clicking randomize button..."); // Find the randomize button const randomizeButton = document.querySelector( "#visualizer > div > form > fieldset.ui-fieldset.ui-widget-content.ui-corner-all.actions > div > div:nth-child(2) > button" ); if (randomizeButton) { randomizeButton.click(); log("✅ Randomize button clicked"); // Wait for the new image to load, then collect it setTimeout(() => { if (isRunning()) { collectCurrentImage(); } }, 100); // Wait for image to update } else { log( "❌ Randomize button not found. Make sure you are on the visualizer page." ); stopBot(); } } function main() { if (!isRunning()) return; // This module doesn't need URL-based navigation // It works on the current page where the user starts it log("🚀 Image Collector running..."); } return { startBot, stopBot, main }; })(); // --- Genetic Profiler Module --- const GeneticProfiler = (function () { const RUN_KEY = "genetic_profiler_running"; const PETS_KEY = "genetic_profiler_pets"; const IDX_KEY = "genetic_profiler_index"; const BASE_KEY = "genetic_profiler_base_href"; const UL_KEY = "genetic_profiler_selected_ul"; const COLORS_KEY = "genetic_profiler_target_colors"; function log(...args) { console.log( "%c[GENETIC-PROFILER]", "background:#4b0082;color:#fff;", ...args ); } function startBot() { log( "Starting Genetic Profiler - First, please enter target color values" ); // Show color input modal showColorInputModal(); } function stopBot() { sessionStorage.removeItem(RUN_KEY); sessionStorage.removeItem(UL_KEY); sessionStorage.removeItem(COLORS_KEY); disableULSelection(); log("Stopped Genetic Profiler"); } function isRunning() { return sessionStorage.getItem(RUN_KEY) === "true"; } function getBase() { return sessionStorage.getItem(BASE_KEY); } function getPets() { try { return JSON.parse(sessionStorage.getItem(PETS_KEY)) || []; } catch { return []; } } function savePets(pets) { sessionStorage.setItem(PETS_KEY, JSON.stringify(pets)); } function getIndex() { return parseInt(sessionStorage.getItem(IDX_KEY) || "0", 10); } function setIndex(i) { sessionStorage.setItem(IDX_KEY, String(i)); } function getSelectedUL() { return sessionStorage.getItem(UL_KEY); } function setSelectedUL(selector) { sessionStorage.setItem(UL_KEY, selector); } function getTargetColors() { try { return JSON.parse(sessionStorage.getItem(COLORS_KEY)) || {}; } catch { return {}; } } function setTargetColors(colors) { sessionStorage.setItem(COLORS_KEY, JSON.stringify(colors)); } function showColorInputModal() { // Create modal overlay const overlay = document.createElement("div"); overlay.id = "genetic-profiler-color-overlay"; overlay.style.cssText = ` position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.8); z-index: 10000; display: flex; align-items: center; justify-content: center; `; // Create modal content const modal = document.createElement("div"); modal.style.cssText = ` background: #4b0082; color: white; padding: 30px; border-radius: 10px; font-family: monospace; max-width: 500px; box-shadow: 0 4px 20px rgba(0,0,0,0.5); `; modal.innerHTML = ` <div style="margin-bottom: 20px; text-align: center;"> <h2 style="margin: 0 0 10px 0;">🧬 Genetic Profiler Setup</h2> <p style="margin: 0; font-size: 14px;">Enter target hex color values (without #):</p> </div> <div style="margin-bottom: 20px;"> <div style="margin-bottom: 10px;"> <label style="display: block; margin-bottom: 5px;">Eyes:</label> <input type="text" id="target-eyes" placeholder="538EA2" style="width: 100%; padding: 8px; border: none; border-radius: 4px; font-family: monospace;" maxlength="6"> </div> <div style="margin-bottom: 10px;"> <label style="display: block; margin-bottom: 5px;">Body1:</label> <input type="text" id="target-body1" placeholder="8C1C2C" style="width: 100%; padding: 8px; border: none; border-radius: 4px; font-family: monospace;" maxlength="6"> </div> <div style="margin-bottom: 10px;"> <label style="display: block; margin-bottom: 5px;">Body2:</label> <input type="text" id="target-body2" placeholder="A4D9E5" style="width: 100%; padding: 8px; border: none; border-radius: 4px; font-family: monospace;" maxlength="6"> </div> <div style="margin-bottom: 10px;"> <label style="display: block; margin-bottom: 5px;">Extra1:</label> <input type="text" id="target-extra1" placeholder="196F52" style="width: 100%; padding: 8px; border: none; border-radius: 4px; font-family: monospace;" maxlength="6"> </div> <div style="margin-bottom: 10px;"> <label style="display: block; margin-bottom: 5px;">Extra2:</label> <input type="text" id="target-extra2" placeholder="383F7E" style="width: 100%; padding: 8px; border: none; border-radius: 4px; font-family: monospace;" maxlength="6"> </div> </div> <div style="text-align: center;"> <button id="colors-confirm" style="margin: 5px; padding: 10px 20px; font-size: 14px; background: white; color: #4b0082; border: none; border-radius: 5px; cursor: pointer;">Next: Select Pets</button> <button id="colors-cancel" style="margin: 5px; padding: 10px 20px; font-size: 14px; background: #333; color: white; border: none; border-radius: 5px; cursor: pointer;">Cancel</button> </div> `; overlay.appendChild(modal); document.body.appendChild(overlay); // Handle confirm button document.getElementById("colors-confirm").onclick = () => { const eyes = document .getElementById("target-eyes") .value.trim() .replace("#", ""); const body1 = document .getElementById("target-body1") .value.trim() .replace("#", ""); const body2 = document .getElementById("target-body2") .value.trim() .replace("#", ""); const extra1 = document .getElementById("target-extra1") .value.trim() .replace("#", ""); const extra2 = document .getElementById("target-extra2") .value.trim() .replace("#", ""); // Validate hex colors const hexPattern = /^[0-9A-Fa-f]{6}$/; if ( !hexPattern.test(eyes) || !hexPattern.test(body1) || !hexPattern.test(body2) || !hexPattern.test(extra1) || !hexPattern.test(extra2) ) { alert( "Please enter valid 6-character hex color codes (e.g., 538EA2)" ); return; } const colors = { eyes: eyes.toUpperCase(), body1: body1.toUpperCase(), body2: body2.toUpperCase(), extra1: extra1.toUpperCase(), extra2: extra2.toUpperCase(), }; setTargetColors(colors); log("Target colors saved:", colors); // Store the current location as base sessionStorage.setItem(BASE_KEY, location.href); sessionStorage.setItem(RUN_KEY, "true"); sessionStorage.setItem(IDX_KEY, "0"); sessionStorage.removeItem(PETS_KEY); sessionStorage.removeItem(UL_KEY); // Remove modal overlay.remove(); // Enable UL selection mode enableULSelection(); }; // Handle cancel button document.getElementById("colors-cancel").onclick = () => { overlay.remove(); log("Color input cancelled"); }; // Handle ESC key document.addEventListener("keydown", function escHandler(event) { if (event.key === "Escape") { overlay.remove(); document.removeEventListener("keydown", escHandler); log("Color input cancelled"); } }); } function enableULSelection() { // Create visual indicators const style = document.createElement("style"); style.id = "genetic-profiler-ul-selection-style"; style.textContent = ` .genetic-profiler-ul-highlight { outline: 3px solid #4b0082 !important; outline-offset: 2px !important; cursor: pointer !important; position: relative !important; } .genetic-profiler-ul-highlight::before { content: "Click to select this list for genetic profiling"; position: absolute; top: -25px; left: 0; background: #4b0082; color: white; padding: 2px 8px; font-size: 12px; border-radius: 3px; white-space: nowrap; z-index: 10000; } .genetic-profiler-selection-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.3); z-index: 9999; pointer-events: none; } .genetic-profiler-instruction { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: #4b0082; color: white; padding: 20px; border-radius: 8px; font-family: monospace; font-size: 16px; z-index: 10001; text-align: center; box-shadow: 0 4px 20px rgba(0,0,0,0.5); } `; document.head.appendChild(style); // Create overlay const overlay = document.createElement("div"); overlay.className = "genetic-profiler-selection-overlay"; overlay.id = "genetic-profiler-overlay"; document.body.appendChild(overlay); // Create instruction const instruction = document.createElement("div"); instruction.className = "genetic-profiler-instruction"; instruction.id = "genetic-profiler-instruction"; instruction.innerHTML = ` <div style="margin-bottom: 10px;"><strong>🧬 Genetic Profiler</strong></div> <div>Hover over UL elements and click the one containing the pets you want to profile</div> <div style="margin-top: 10px; font-size: 12px;">Press ESC to cancel</div> `; document.body.appendChild(instruction); // Find all UL elements and add hover effects const uls = document.querySelectorAll("ul"); uls.forEach((ul) => { ul.addEventListener("mouseenter", handleULHover); ul.addEventListener("mouseleave", handleULLeave); ul.addEventListener("click", handleULClick); }); // ESC to cancel document.addEventListener("keydown", handleEscapeKey); log( `Found ${uls.length} UL elements - hover to highlight, click to select` ); } function disableULSelection() { // Remove style const style = document.getElementById( "genetic-profiler-ul-selection-style" ); if (style) style.remove(); // Remove overlay and instruction const overlay = document.getElementById("genetic-profiler-overlay"); if (overlay) overlay.remove(); const instruction = document.getElementById( "genetic-profiler-instruction" ); if (instruction) instruction.remove(); // Remove event listeners from all ULs const uls = document.querySelectorAll("ul"); uls.forEach((ul) => { ul.removeEventListener("mouseenter", handleULHover); ul.removeEventListener("mouseleave", handleULLeave); ul.removeEventListener("click", handleULClick); ul.classList.remove("genetic-profiler-ul-highlight"); }); document.removeEventListener("keydown", handleEscapeKey); } function handleULHover(event) { const ul = event.target; ul.classList.add("genetic-profiler-ul-highlight"); // Add debug info overlay const debugInfo = document.createElement("div"); debugInfo.id = "genetic-profiler-debug-info"; debugInfo.style.cssText = ` position: fixed; top: 10px; right: 10px; background: #4b0082; color: white; padding: 10px; border-radius: 5px; font-family: monospace; font-size: 12px; z-index: 10002; max-width: 300px; `; const petLinks = ul.querySelectorAll('a.pet[href*="pet="]').length; const totalLis = ul.querySelectorAll("li").length; debugInfo.innerHTML = ` <strong>🔍 UL Preview</strong><br> Total LIs: ${totalLis}<br> Pet Links: ${petLinks}<br> ${ petLinks > 0 ? "✅ Contains pet links!" : "❌ No pet links found" } `; document.body.appendChild(debugInfo); } function handleULLeave(event) { event.target.classList.remove("genetic-profiler-ul-highlight"); // Remove debug info const debugInfo = document.getElementById("genetic-profiler-debug-info"); if (debugInfo) debugInfo.remove(); } function handleULClick(event) { event.preventDefault(); event.stopPropagation(); const ul = event.target; // Remove highlight class ul.classList.remove("genetic-profiler-ul-highlight"); log(`Selected UL with ${ul.children.length} children`); // Clean up selection mode first disableULSelection(); // Collect pets immediately from the selected UL collectPetsFromElement(ul); } function collectPetsFromElement(ul) { if (!isRunning()) return; log("Collecting pet links from selected UL"); if (!ul) { log("No UL element provided"); stopBot(); return; } log(`Found UL element with ${ul.children.length} child elements`); // Extract pet links specifically const petLinks = []; const lis = ul.querySelectorAll("li"); log(`Scanning ${lis.length} li elements for pet links...`); lis.forEach((li, index) => { // Look for pet links var petLink = li.querySelector('a.pet[href*="pet="]') || li.querySelector('a[href*="pet="]') || li.querySelector('a[href*="sub=profile"]'); if (petLink && petLink.href) { const href = petLink.href; // Check if it's a pet profile link (not a user link) const isPetLink = href.includes("pet=") && href.includes("sub=profile"); if (isPetLink) { petLinks.push(href); log(`Found pet link ${index + 1}: ${href}`); } } }); if (!petLinks.length) { log("No pet links found in selected UL"); log("UL HTML preview:", ul.innerHTML.substring(0, 500)); // Debug what we actually found log("Debug: Found links in UL:"); const allLinks = ul.querySelectorAll("a"); allLinks.forEach((link, i) => { log(` Link ${i + 1}: href="${link.href}" class="${link.className}"`); }); stopBot(); return; } log(`Found ${petLinks.length} pets to profile`); // reverse petLinks to profile in reverse order petLinks.reverse(); savePets(petLinks); goToPet(); } function handleEscapeKey(event) { if (event.key === "Escape") { log("Selection cancelled"); stopBot(); } } function goToPet() { if (!isRunning()) return; const pets = getPets(); const idx = getIndex(); if (idx >= pets.length) { log("All pets profiled! Genetic profiling complete!"); stopBot(); location.href = getBase(); return; } log(`🧬 Going to profile pet ${idx + 1}/${pets.length}...`); location.href = pets[idx]; } function hexToRgb(hex) { const r = parseInt(hex.substring(0, 2), 16); const g = parseInt(hex.substring(2, 4), 16); const b = parseInt(hex.substring(4, 6), 16); return { r, g, b }; } function parseColorFromElement(element) { if (!element) return null; // Look for hex color in the text content const text = element.textContent || ""; const hexMatch = text.match(/#([0-9A-Fa-f]{6})/); if (hexMatch) { return hexMatch[1].toUpperCase(); } return null; } function calculateColorDifferences(targetColors, actualColors) { const differences = {}; for (const [colorName, targetHex] of Object.entries(targetColors)) { const actualHex = actualColors[colorName]; if (!actualHex) { differences[colorName] = "N/A"; continue; } const targetRgb = hexToRgb(targetHex); const actualRgb = hexToRgb(actualHex); const rDiff = actualRgb.r - targetRgb.r; const gDiff = actualRgb.g - targetRgb.g; const bDiff = actualRgb.b - targetRgb.b; const formatDiff = (diff) => { if (diff > 0) return `+${diff}`; if (diff < 0) return `${diff}`; return "0"; }; differences[colorName] = `${formatDiff(rDiff)}, ${formatDiff( gDiff )}, ${formatDiff(bDiff)}`; } return differences; } function handlePetProfile() { if (!isRunning()) return; log("🔍 Analyzing pet colors..."); setTimeout(() => { if (!isRunning()) return; // Find the colors fieldset const colorsFieldset = document.querySelector( "#profile > div > form > fieldset.ui-fieldset.ui-widget-content.ui-corner-all.colors" ); if (!colorsFieldset) { log("❌ Colors fieldset not found, skipping this pet"); setIndex(getIndex() + 1); setTimeout(() => { if (isRunning()) { goToPet(); } }, 300); return; } // Extract actual colors from the pet const actualColors = {}; // Eyes color const eyesRow = colorsFieldset.querySelector("#Eyes"); if (eyesRow) { const eyesColor = parseColorFromElement( eyesRow.querySelector("td.c1 var") ); if (eyesColor) actualColors.eyes = eyesColor; } // Body colors const bodyRow = colorsFieldset.querySelector("#Body"); if (bodyRow) { const body1Color = parseColorFromElement( bodyRow.querySelector("td.c1 var") ); const body2Color = parseColorFromElement( bodyRow.querySelector("td.c2 var") ); if (body1Color) actualColors.body1 = body1Color; if (body2Color) actualColors.body2 = body2Color; } // Extra colors const extraRow = colorsFieldset.querySelector("#Extra"); if (extraRow) { const extra1Color = parseColorFromElement( extraRow.querySelector("td.c1 var") ); const extra2Color = parseColorFromElement( extraRow.querySelector("td.c2 var") ); if (extra1Color) actualColors.extra1 = extra1Color; if (extra2Color) actualColors.extra2 = extra2Color; } log("Actual colors found:", actualColors); // Calculate differences const targetColors = getTargetColors(); const differences = calculateColorDifferences( targetColors, actualColors ); log("Color differences:", differences); var sum_of_absolute_value_of_differences_per_color = []; // we want to make a sum row that shows the sum of the absolute value of the differences per color // example data: // Eyes: 1, -2, 3 // Body1: -4, 5, 6 // Body2: 7, -8, 9 // Extra1: 0, 0, 0 // Extra2: 0, 0, 0 // example sum row of example data: Sum: 12, 15, 18 // explanation of sum row: // 12 = 1 + |-4| + 7 // 15 = |-2| + 5 + |-8| // 18 = 3 + 6 + 9 // example is sum of the rgb columns of the below summaryLines for (const color in differences) { const diff = differences[color]; if (diff === "N/A") { sum_of_absolute_value_of_differences_per_color.push("N/A"); continue; } const rgbValues = diff .split(",") .map((v) => Math.abs(parseInt(v.trim(), 10))); if (sum_of_absolute_value_of_differences_per_color.length === 0) { sum_of_absolute_value_of_differences_per_color = rgbValues; } else { sum_of_absolute_value_of_differences_per_color = sum_of_absolute_value_of_differences_per_color.map( (sum, index) => { return sum === "N/A" ? "N/A" : sum + rgbValues[index]; } ); } } log( "Sum of absolute value of differences per color:", sum_of_absolute_value_of_differences_per_color ); // Add sum row to differences differences.sum = sum_of_absolute_value_of_differences_per_color .map((v) => (v === "N/A" ? "N/A" : v.toString())) .join(", "); // Create the summary text const summaryLines = [ `Eyes: ${differences.eyes || "N/A"}`, `Body1: ${differences.body1 || "N/A"}`, `Body2: ${differences.body2 || "N/A"}`, `Extra1: ${differences.extra1 || "N/A"}`, `Extra2: ${differences.extra2 || "N/A"}`, "", `Sum: ${differences.sum || "N/A"}`, ]; const summaryText = summaryLines.join("\n"); log("Generated summary:", summaryText); // Now write this to the pet's description writeToDescription(summaryText); }, 500); // Wait for page to load } function writeToDescription(text) { if (!isRunning()) return; log("🖊️ Writing genetic profile to description..."); // Try to find the description div (either regular or empty) let descDiv = document.querySelector( "#overview > div > form > fieldset.ui-fieldset.ui-widget-content.ui-corner-all.presentation.edit_txt > div > div.parsed_txt" ); if (!descDiv) { descDiv = document.querySelector( "#overview > div > form > fieldset.ui-fieldset.ui-widget-content.ui-corner-all.presentation.edit_txt > div > div.parsed_txt.empty" ); } if (!descDiv) { log("❌ Description div not found, skipping this pet"); setIndex(getIndex() + 1); setTimeout(() => { if (isRunning()) { goToPet(); } }, 300); return; } log("✅ Found description div, clicking to enable editing..."); descDiv.click(); // Wait for textarea to appear setTimeout(() => { if (!isRunning()) return; const textarea = document.querySelector( "#overview > div > form > fieldset.ui-fieldset.ui-widget-content.ui-corner-all.presentation.edit_txt > div > div.ui-input.are > textarea" ); if (!textarea) { log("❌ Textarea not found after clicking description div"); setIndex(getIndex() + 1); setTimeout(() => { if (isRunning()) { goToPet(); } }, 300); return; } log("✅ Found textarea, writing genetic profile..."); // Get existing text and append our analysis const existingText = textarea.value || ""; const newText = existingText ? `${existingText}\n\n--- Genetic Profile ---\n${text}` : `--- Genetic Profile ---\n${text}`; // Clear and write new text textarea.value = ""; textarea.focus(); textarea.value = newText; // Trigger events to ensure change is registered textarea.dispatchEvent(new Event("input", { bubbles: true })); textarea.dispatchEvent(new Event("change", { bubbles: true })); log("📝 Genetic profile written to description"); // Wait a moment then save the description setTimeout(() => { if (isRunning()) { saveDescription(); } }, 300); }, 500); } function saveDescription() { if (!isRunning()) return; log("💾 Saving description..."); // Find the save UL const saveUL = document.querySelector( "#overview > div > form > fieldset.ui-fieldset.ui-widget-content.ui-corner-all.overview > div > ul" ); if (!saveUL) { log("❌ Save UL not found"); setIndex(getIndex() + 1); setTimeout(() => { if (isRunning()) { goToPet(); } }, 300); return; } log("✅ Found save UL, clicking to save..."); saveUL.click(); log("🎉 Pet genetic profile saved! Moving to next pet..."); // Move to next pet setTimeout(() => { if (isRunning()) { setIndex(getIndex() + 1); goToPet(); } }, 500); } function main() { if (!isRunning()) return; const href = location.href; const base = getBase(); log("🚀 Genetic Profiler main executing...", href); // If we're on a pet profile page if (href.includes("sub=profile") && href.includes("pet=")) { log("📍 On pet profile, analyzing genetic data"); setTimeout(() => { if (isRunning()) { handlePetProfile(); } }, 300); } // If we're back at the base page and haven't collected pets yet else if (href === base && !sessionStorage.getItem(PETS_KEY)) { log("📍 At base, need to collect pets"); setTimeout(() => { if (isRunning()) { // Re-enable UL selection enableULSelection(); } }, 300); } // If we're back at the base page and have pets collected else if (href === base) { log("📍 At base, have pets, continuing..."); setTimeout(() => { if (isRunning()) { goToPet(); } }, 300); } // If we're on a different page else { const pets = getPets(); if (pets.length > 0) { log("📍 On different page, going to next pet"); setTimeout(() => { if (isRunning()) { goToPet(); } }, 300); } else { log("📍 No pets collected, returning to base"); location.href = base; } } } return { startBot, stopBot, main }; })(); // --- Feeder Module --- const Feeder = (function () { const RUN_KEY = "feeder_running"; const PETS_KEY = "feeder_pets"; const IDX_KEY = "feeder_index"; const BASE_KEY = "feeder_base_href"; const UL_KEY = "feeder_selected_ul"; function log(...args) { console.log("%c[FEEDER]", "background:#32cd32;color:#fff;", ...args); } function startBot() { log( "Starting Feeder - Please click on the UL element containing the pets to feed" ); // Store the current location as base sessionStorage.setItem(BASE_KEY, location.href); sessionStorage.setItem(RUN_KEY, "true"); sessionStorage.setItem(IDX_KEY, "0"); sessionStorage.removeItem(PETS_KEY); sessionStorage.removeItem(UL_KEY); // Enable UL selection mode enableULSelection(); } function stopBot() { sessionStorage.removeItem(RUN_KEY); sessionStorage.removeItem(UL_KEY); disableULSelection(); log("Stopped Feeder"); } function isRunning() { return sessionStorage.getItem(RUN_KEY) === "true"; } function getBase() { return sessionStorage.getItem(BASE_KEY); } function getPets() { try { return JSON.parse(sessionStorage.getItem(PETS_KEY)) || []; } catch { return []; } } function savePets(pets) { sessionStorage.setItem(PETS_KEY, JSON.stringify(pets)); } function getIndex() { return parseInt(sessionStorage.getItem(IDX_KEY) || "0", 10); } function setIndex(i) { sessionStorage.setItem(IDX_KEY, String(i)); } function getSelectedUL() { return sessionStorage.getItem(UL_KEY); } function setSelectedUL(selector) { sessionStorage.setItem(UL_KEY, selector); } function enableULSelection() { // Create visual indicators const style = document.createElement("style"); style.id = "feeder-ul-selection-style"; style.textContent = ` .feeder-ul-highlight { outline: 3px solid #32cd32 !important; outline-offset: 2px !important; cursor: pointer !important; position: relative !important; } .feeder-ul-highlight::before { content: "Click to select this list for feeding"; position: absolute; top: -25px; left: 0; background: #32cd32; color: white; padding: 2px 8px; font-size: 12px; border-radius: 3px; white-space: nowrap; z-index: 10000; } .feeder-selection-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.3); z-index: 9999; pointer-events: none; } .feeder-instruction { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: #32cd32; color: white; padding: 20px; border-radius: 8px; font-family: monospace; font-size: 16px; z-index: 10001; text-align: center; box-shadow: 0 4px 20px rgba(0,0,0,0.5); } `; document.head.appendChild(style); // Create overlay const overlay = document.createElement("div"); overlay.className = "feeder-selection-overlay"; overlay.id = "feeder-overlay"; document.body.appendChild(overlay); // Create instruction const instruction = document.createElement("div"); instruction.className = "feeder-instruction"; instruction.id = "feeder-instruction"; instruction.innerHTML = ` <div style="margin-bottom: 10px;"><strong>🍖 Feeder Setup</strong></div> <div>Hover over UL elements and click the one containing the pets you want to feed</div> <div style="margin-top: 10px; font-size: 12px;">Press ESC to cancel</div> `; document.body.appendChild(instruction); // Find all UL elements and add hover effects const uls = document.querySelectorAll("ul"); uls.forEach((ul) => { ul.addEventListener("mouseenter", handleULHover); ul.addEventListener("mouseleave", handleULLeave); ul.addEventListener("click", handleULClick); }); // ESC to cancel document.addEventListener("keydown", handleEscapeKey); log( `Found ${uls.length} UL elements - hover to highlight, click to select` ); } function disableULSelection() { // Remove style const style = document.getElementById("feeder-ul-selection-style"); if (style) style.remove(); // Remove overlay and instruction const overlay = document.getElementById("feeder-overlay"); if (overlay) overlay.remove(); const instruction = document.getElementById("feeder-instruction"); if (instruction) instruction.remove(); // Remove event listeners from all ULs const uls = document.querySelectorAll("ul"); uls.forEach((ul) => { ul.removeEventListener("mouseenter", handleULHover); ul.removeEventListener("mouseleave", handleULLeave); ul.removeEventListener("click", handleULClick); ul.classList.remove("feeder-ul-highlight"); }); document.removeEventListener("keydown", handleEscapeKey); } function handleULHover(event) { const ul = event.target; ul.classList.add("feeder-ul-highlight"); // Add debug info overlay const debugInfo = document.createElement("div"); debugInfo.id = "feeder-debug-info"; debugInfo.style.cssText = ` position: fixed; top: 10px; right: 10px; background: #32cd32; color: white; padding: 10px; border-radius: 5px; font-family: monospace; font-size: 12px; z-index: 10002; max-width: 300px; `; const petLinks = ul.querySelectorAll('a.pet[href*="pet="]').length; const totalLis = ul.querySelectorAll("li").length; debugInfo.innerHTML = ` <strong>🔍 UL Preview</strong><br> Total LIs: ${totalLis}<br> Pet Links: ${petLinks}<br> ${ petLinks > 0 ? "✅ Contains pet links!" : "❌ No pet links found" } `; document.body.appendChild(debugInfo); } function handleULLeave(event) { event.target.classList.remove("feeder-ul-highlight"); // Remove debug info const debugInfo = document.getElementById("feeder-debug-info"); if (debugInfo) debugInfo.remove(); } function handleULClick(event) { event.preventDefault(); event.stopPropagation(); const ul = event.target; // Remove highlight class ul.classList.remove("feeder-ul-highlight"); log(`Selected UL with ${ul.children.length} children`); // Clean up selection mode first disableULSelection(); // Collect pets immediately from the selected UL collectPetsFromElement(ul); } function collectPetsFromElement(ul) { if (!isRunning()) return; log("Collecting pet links from selected UL"); if (!ul) { log("No UL element provided"); stopBot(); return; } log(`Found UL element with ${ul.children.length} child elements`); // Extract pet links specifically const petLinks = []; const lis = ul.querySelectorAll("li"); log(`Scanning ${lis.length} li elements for pet links...`); lis.forEach((li, index) => { // Look for pet links const petLink = li.querySelector('a.pet[href*="pet="]') || li.querySelector('a[href*="pet="]') || li.querySelector('a[href*="sub=profile"]'); if (petLink && petLink.href) { const href = petLink.href; // Check if it's a pet profile link (not a user link) const isPetLink = href.includes("pet=") && href.includes("sub=profile"); if (isPetLink) { petLinks.push(href); log(`Found pet link ${index + 1}: ${href}`); } } }); if (!petLinks.length) { log("No pet links found in selected UL"); log("UL HTML preview:", ul.innerHTML.substring(0, 500)); // Debug what we actually found log("Debug: Found links in UL:"); const allLinks = ul.querySelectorAll("a"); allLinks.forEach((link, i) => { log(` Link ${i + 1}: href="${link.href}" class="${link.className}"`); }); stopBot(); return; } log(`Found ${petLinks.length} pets to feed`); savePets(petLinks); goToPet(); } function handleEscapeKey(event) { if (event.key === "Escape") { log("Selection cancelled"); stopBot(); } } function goToPet() { if (!isRunning()) return; const pets = getPets(); const idx = getIndex(); if (idx >= pets.length) { log("All pets fed! Feeding complete!"); stopBot(); location.href = getBase(); return; } log(`🍖 Going to feed pet ${idx + 1}/${pets.length}...`); location.href = pets[idx]; } function handlePetProfile() { if (!isRunning()) return; log("🔍 Looking for feeding button on pet profile..."); let tries = 0; const maxTries = 5; const tryFeedButton = () => { // Look for feeding button with the specific onclick pattern const feedBtn = document.querySelector('button[onclick*="pet_feed"]'); if (feedBtn) { log("✅ Found feeding button, clicking..."); feedBtn.click(); log("🍖 Pet fed successfully! Moving to next pet..."); // Move to next pet with minimal delay setTimeout(() => { if (isRunning()) { setIndex(getIndex() + 1); goToPet(); } }, 250); } else if (tries++ < maxTries) { log( `⏳ Feeding button not found, retrying... (${tries}/${maxTries})` ); setTimeout(tryFeedButton, 50); } else { log("❌ No feeding button found, moving to next pet"); setIndex(getIndex() + 1); setTimeout(() => { if (isRunning()) { goToPet(); } }, 50); } }; // Start looking for button with minimal delay setTimeout(tryFeedButton, 100); } function main() { if (!isRunning()) return; const href = location.href; const base = getBase(); log("🚀 Feeder main executing...", href); // If we're on a pet profile page if (href.includes("sub=profile") && href.includes("pet=")) { log("📍 On pet profile, looking for feeding button"); setTimeout(() => { if (isRunning()) { handlePetProfile(); } }, 50); } // If we're back at the base page and haven't collected pets yet else if (href === base && !sessionStorage.getItem(PETS_KEY)) { log("📍 At base, need to collect pets"); setTimeout(() => { if (isRunning()) { // Re-enable UL selection enableULSelection(); } }, 50); } // If we're back at the base page and have pets collected else if (href === base) { log("📍 At base, have pets, continuing..."); setTimeout(() => { if (isRunning()) { goToPet(); } }, 50); } // If we're on a different page else { const pets = getPets(); if (pets.length > 0) { log("📍 On different page, going to next pet"); setTimeout(() => { if (isRunning()) { goToPet(); } }, 50); } else { log("📍 No pets collected, returning to base"); location.href = base; } } } return { startBot, stopBot, main }; })(); // ...existing code... // --- Hatcher Module --- const Hatcher = (function () { const RUN_KEY = "hatcher_running"; const PETS_KEY = "hatcher_pets"; const IDX_KEY = "hatcher_index"; const BASE_KEY = "hatcher_base_href"; const UL_KEY = "hatcher_selected_ul"; function log(...args) { console.log("%c[HATCHER]", "background:#ffa500;color:#fff;", ...args); } async function waitForPageLoad() { // Initial short wait for DOM await new Promise((r) => setTimeout(r, 100)); // Wait until loading div disappears while (document.querySelector("div.loading")) { log("⏳ Waiting for page to load..."); await new Promise((r) => setTimeout(r, 100)); } } function startBot() { log( "Starting Hatcher - Please click on the UL element containing the pets to hatch" ); // Store the current location as base sessionStorage.setItem(BASE_KEY, location.href); sessionStorage.setItem(RUN_KEY, "true"); sessionStorage.setItem(IDX_KEY, "0"); sessionStorage.removeItem(PETS_KEY); sessionStorage.removeItem(UL_KEY); // Enable UL selection mode enableULSelection(); } function stopBot() { sessionStorage.removeItem(RUN_KEY); sessionStorage.removeItem(UL_KEY); disableULSelection(); log("Stopped Hatcher"); } function isRunning() { return sessionStorage.getItem(RUN_KEY) === "true"; } function getBase() { return sessionStorage.getItem(BASE_KEY); } function getPets() { try { return JSON.parse(sessionStorage.getItem(PETS_KEY)) || []; } catch { return []; } } function savePets(pets) { sessionStorage.setItem(PETS_KEY, JSON.stringify(pets)); } function getIndex() { return parseInt(sessionStorage.getItem(IDX_KEY) || "0", 10); } function setIndex(i) { sessionStorage.setItem(IDX_KEY, String(i)); } function getSelectedUL() { return sessionStorage.getItem(UL_KEY); } function setSelectedUL(selector) { sessionStorage.setItem(UL_KEY, selector); } function enableULSelection() { // Create visual indicators const style = document.createElement("style"); style.id = "hatcher-ul-selection-style"; style.textContent = ` .hatcher-ul-highlight { outline: 3px solid #ffa500 !important; outline-offset: 2px !important; cursor: pointer !important; position: relative !important; } .hatcher-ul-highlight::before { content: "Click to select this list for hatching"; position: absolute; top: -25px; left: 0; background: #ffa500; color: white; padding: 2px 8px; font-size: 12px; border-radius: 3px; white-space: nowrap; z-index: 10000; } .hatcher-selection-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.3); z-index: 9999; pointer-events: none; } .hatcher-instruction { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: #ffa500; color: white; padding: 20px; border-radius: 8px; font-family: monospace; font-size: 16px; z-index: 10001; text-align: center; box-shadow: 0 4px 20px rgba(0,0,0,0.5); } `; document.head.appendChild(style); // Create overlay const overlay = document.createElement("div"); overlay.className = "hatcher-selection-overlay"; overlay.id = "hatcher-overlay"; document.body.appendChild(overlay); // Create instruction const instruction = document.createElement("div"); instruction.className = "hatcher-instruction"; instruction.id = "hatcher-instruction"; instruction.innerHTML = ` <div style="margin-bottom: 10px;"><strong>🥚 Hatcher Setup</strong></div> <div>Hover over UL elements and click the one containing the pets you want to hatch</div> <div style="margin-top: 10px; font-size: 12px;">Press ESC to cancel</div> `; document.body.appendChild(instruction); // Find all UL elements and add hover effects const uls = document.querySelectorAll("ul"); uls.forEach((ul) => { ul.addEventListener("mouseenter", handleULHover); ul.addEventListener("mouseleave", handleULLeave); ul.addEventListener("click", handleULClick); }); // ESC to cancel document.addEventListener("keydown", handleEscapeKey); log( `Found ${uls.length} UL elements - hover to highlight, click to select` ); } function disableULSelection() { // Remove style const style = document.getElementById("hatcher-ul-selection-style"); if (style) style.remove(); // Remove overlay and instruction const overlay = document.getElementById("hatcher-overlay"); if (overlay) overlay.remove(); const instruction = document.getElementById("hatcher-instruction"); if (instruction) instruction.remove(); // Remove event listeners from all ULs const uls = document.querySelectorAll("ul"); uls.forEach((ul) => { ul.removeEventListener("mouseenter", handleULHover); ul.removeEventListener("mouseleave", handleULLeave); ul.removeEventListener("click", handleULClick); ul.classList.remove("hatcher-ul-highlight"); }); document.removeEventListener("keydown", handleEscapeKey); } function handleULHover(event) { const ul = event.target; ul.classList.add("hatcher-ul-highlight"); // Add debug info overlay const debugInfo = document.createElement("div"); debugInfo.id = "hatcher-debug-info"; debugInfo.style.cssText = ` position: fixed; top: 10px; right: 10px; background: #ffa500; color: white; padding: 10px; border-radius: 5px; font-family: monospace; font-size: 12px; z-index: 10002; max-width: 300px; `; const petLinks = ul.querySelectorAll('a.pet[href*="pet="]').length; const totalLis = ul.querySelectorAll("li").length; debugInfo.innerHTML = ` <strong>🔍 UL Preview</strong><br> Total LIs: ${totalLis}<br> Pet Links: ${petLinks}<br> ${petLinks > 0 ? "✅ Contains pet links!" : "❌ No pet links found"} `; document.body.appendChild(debugInfo); } function handleULLeave(event) { event.target.classList.remove("hatcher-ul-highlight"); // Remove debug info const debugInfo = document.getElementById("hatcher-debug-info"); if (debugInfo) debugInfo.remove(); } function handleULClick(event) { event.preventDefault(); event.stopPropagation(); const ul = event.target; // Remove highlight class ul.classList.remove("hatcher-ul-highlight"); log(`Selected UL with ${ul.children.length} children`); // Clean up selection mode first disableULSelection(); // Collect pets immediately from the selected UL collectPetsFromElement(ul); } function collectPetsFromElement(ul) { if (!isRunning()) return; log("Collecting pet links from selected UL"); if (!ul) { log("No UL element provided"); stopBot(); return; } log(`Found UL element with ${ul.children.length} child elements`); // Extract pet links specifically const petLinks = []; const lis = ul.querySelectorAll("li"); log(`Scanning ${lis.length} li elements for pet links...`); lis.forEach((li, index) => { // Look for pet links const petLink = li.querySelector('a.pet[href*="pet="]') || li.querySelector('a[href*="pet="]') || li.querySelector('a[href*="sub=profile"]'); if (petLink && petLink.href) { const href = petLink.href; // Check if it's a pet profile link (not a user link) const isPetLink = href.includes("pet=") && href.includes("sub=profile"); if (isPetLink) { petLinks.push(href); log(`Found pet link ${index + 1}: ${href}`); } } }); if (!petLinks.length) { log("No pet links found in selected UL"); log("UL HTML preview:", ul.innerHTML.substring(0, 500)); // Debug what we actually found log("Debug: Found links in UL:"); const allLinks = ul.querySelectorAll("a"); allLinks.forEach((link, i) => { log(` Link ${i + 1}: href="${link.href}" class="${link.className}"`); }); stopBot(); return; } log(`Found ${petLinks.length} pets to hatch`); savePets(petLinks); goToPet(); } function handleEscapeKey(event) { if (event.key === "Escape") { log("Selection cancelled"); stopBot(); } } async function goToPet() { if (!isRunning()) return; const pets = getPets(); const idx = getIndex(); if (idx >= pets.length) { log("All pets processed! Hatching complete!"); stopBot(); location.href = getBase(); return; } log(`🥚 Going to hatch pet ${idx + 1}/${pets.length}...`); location.href = pets[idx]; } async function handlePetProfile() { if (!isRunning()) return; log("🔍 Looking for hatch egg button on pet profile..."); // Wait for page to fully load await waitForPageLoad(); let tries = 0; const maxTries = 0; const tryHatchButton = async () => { if (!isRunning()) return; // Look for button with "Hatch Egg" text using the base selector const buttons = document.querySelectorAll( "#profile > div > form > fieldset.ui-fieldset.ui-widget-content.ui-corner-all.actions > div > div > button" ); let hatchBtn = null; for (let btn of buttons) { if (btn.textContent.trim().includes("Hatch Egg")) { hatchBtn = btn; break; } } if (hatchBtn) { log("✅ Found hatch egg button, clicking..."); hatchBtn.click(); log("🥚 Egg hatched successfully! Moving to next pet..."); // Wait for any page updates await waitForPageLoad(); // Move to next pet setIndex(getIndex() + 1); await new Promise((r) => setTimeout(r, 100)); if (isRunning()) { goToPet(); } } else if (tries++ < maxTries) { log( `⏳ Hatch egg button not found, retrying... (${tries}/${maxTries})` ); await new Promise((r) => setTimeout(r, 100)); return await tryHatchButton(); } else { log("❌ No hatch egg button found, moving to next pet"); setIndex(getIndex() + 1); await new Promise((r) => setTimeout(r, 100)); if (isRunning()) { goToPet(); } } }; // Start looking for button await tryHatchButton(); } async function main() { if (!isRunning()) return; const href = location.href; const base = getBase(); log("🚀 Hatcher main executing...", href); // If we're on a pet profile page if (href.includes("sub=profile") && href.includes("pet=")) { log("📍 On pet profile, looking for hatch egg button"); await handlePetProfile(); } // If we're back at the base page and haven't collected pets yet else if (href === base && !sessionStorage.getItem(PETS_KEY)) { log("📍 At base, need to collect pets"); await new Promise((r) => setTimeout(r, 100)); if (isRunning()) { // Re-enable UL selection enableULSelection(); } } // If we're back at the base page and have pets collected else if (href === base) { log("📍 At base, have pets, continuing..."); await new Promise((r) => setTimeout(r, 100)); if (isRunning()) { goToPet(); } } // If we're on a different page else { const pets = getPets(); if (pets.length > 0) { log("📍 On different page, going to next pet"); await new Promise((r) => setTimeout(r, 100)); if (isRunning()) { goToPet(); } } else { log("📍 No pets collected, returning to base"); location.href = base; } } } return { startBot, stopBot, main }; })(); // Add this after the other modules (before createControls function) // --- Bidder Module --- // Replace the existing Bidder module with this updated version: // --- Bidder Module --- // --- Bidder Module --- const Bidder = (function () { const RUN_KEY = "bidder_running"; const BID_COUNT_KEY = "bidder_bid_count"; const SELECTED_BUTTON_KEY = "bidder_selected_button"; let retryTimeout = null; function log(...args) { console.log("%c[BIDDER]", "background:#ff6b35;color:#fff;", ...args); } function startBot() { log( "Starting Bidder - Please click on the Place Bid button you want to use" ); sessionStorage.setItem(RUN_KEY, "true"); sessionStorage.setItem(BID_COUNT_KEY, "0"); sessionStorage.removeItem(SELECTED_BUTTON_KEY); clearTimeout(retryTimeout); // Enable button selection mode enableButtonSelection(); } function stopBot() { sessionStorage.removeItem(RUN_KEY); sessionStorage.removeItem(BID_COUNT_KEY); sessionStorage.removeItem(SELECTED_BUTTON_KEY); clearTimeout(retryTimeout); disableButtonSelection(); log("Stopped Bidder"); } function isRunning() { return sessionStorage.getItem(RUN_KEY) === "true"; } function getBidCount() { return parseInt(sessionStorage.getItem(BID_COUNT_KEY) || "0", 10); } function incrementBidCount() { const count = getBidCount() + 1; sessionStorage.setItem(BID_COUNT_KEY, String(count)); return count; } function getSelectedButton() { return sessionStorage.getItem(SELECTED_BUTTON_KEY); } function setSelectedButton(selector) { sessionStorage.setItem(SELECTED_BUTTON_KEY, selector); } function findSelectedBidButton() { const buttonSelector = getSelectedButton(); if (!buttonSelector) return null; let bidButton = null; // Handle text-based selectors if (buttonSelector.startsWith("TEXT:")) { const targetText = buttonSelector.substring(5); // Remove "TEXT:" prefix const allButtons = document.querySelectorAll("button"); bidButton = Array.from(allButtons).find( (btn) => btn.textContent.trim() === targetText ); log(`Looking for button with text: "${targetText}"`); } else { // Handle regular CSS selectors const selectedElement = document.querySelector(buttonSelector); if (selectedElement) { // If we selected a span inside a button, get the button if ( selectedElement.tagName === "SPAN" && selectedElement.parentElement && selectedElement.parentElement.tagName === "BUTTON" ) { bidButton = selectedElement.parentElement; log("Found span inside button, using parent button"); } else if (selectedElement.tagName === "BUTTON") { bidButton = selectedElement; log("Found button directly"); } else { // Look for a button within the selected element bidButton = selectedElement.querySelector("button"); if (bidButton) { log("Found button within selected element"); } } } } return bidButton; } function enableButtonSelection() { // Create visual indicators const style = document.createElement("style"); style.id = "bidder-button-selection-style"; style.textContent = ` .bidder-button-highlight { outline: 3px solid #ff6b35 !important; outline-offset: 2px !important; cursor: pointer !important; position: relative !important; z-index: 10001 !important; } .bidder-button-highlight::before { content: "Click to select this bid button"; position: absolute; top: -25px; left: 0; background: #ff6b35; color: white; padding: 2px 8px; font-size: 12px; border-radius: 3px; white-space: nowrap; z-index: 10002; pointer-events: none; } .bidder-selection-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.3); z-index: 9999; pointer-events: none; } .bidder-instruction { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: #ff6b35; color: white; padding: 20px; border-radius: 8px; font-family: monospace; font-size: 16px; z-index: 10001; text-align: center; box-shadow: 0 4px 20px rgba(0,0,0,0.5); } `; document.head.appendChild(style); // Create overlay const overlay = document.createElement("div"); overlay.className = "bidder-selection-overlay"; overlay.id = "bidder-overlay"; document.body.appendChild(overlay); // Create instruction const instruction = document.createElement("div"); instruction.className = "bidder-instruction"; instruction.id = "bidder-instruction"; instruction.innerHTML = ` <div style="margin-bottom: 10px;"><strong>🔨 Bidder Setup</strong></div> <div>Hover over buttons and click the "Place Bid" button you want to use</div> <div style="margin-top: 10px; font-size: 12px;">Press ESC to cancel</div> `; document.body.appendChild(instruction); // Find all potential bid buttons using proper CSS selectors const allButtons = document.querySelectorAll("button"); const potentialBidButtons = Array.from(allButtons).filter((btn) => { const text = btn.textContent.toLowerCase(); const onclick = btn.onclick?.toString() || ""; return ( text.includes("bid") || text.includes("place bid") || onclick.includes("bid") || onclick.includes("auction") ); }); log( `Found ${potentialBidButtons.length} potential bid buttons - hover to highlight, click to select` ); // Store original handlers for all buttons const originalHandlers = new Map(); potentialBidButtons.forEach((button) => { // Store original onclick handler if (button.onclick) { originalHandlers.set(button, button.onclick); button.onclick = null; } // Add our event listeners button.addEventListener("mouseenter", handleButtonHover); button.addEventListener("mouseleave", handleButtonLeave); button.addEventListener("click", function (event) { handleButtonClick(event, originalHandlers); }); }); // Store handlers map globally for cleanup window.bidderOriginalHandlers = originalHandlers; // ESC to cancel document.addEventListener("keydown", handleEscapeKey); } function handleButtonHover(event) { const button = event.target; button.classList.add("bidder-button-highlight"); // Add debug info overlay const debugInfo = document.createElement("div"); debugInfo.id = "bidder-debug-info"; debugInfo.style.cssText = ` position: fixed; top: 10px; right: 10px; background: #ff6b35; color: white; padding: 10px; border-radius: 5px; font-family: monospace; font-size: 12px; z-index: 10002; max-width: 300px; `; const buttonText = button.textContent.trim(); const hasOnclick = button.onclick ? "Yes" : "No"; const onclickText = button.onclick?.toString().substring(0, 50) || "None"; debugInfo.innerHTML = ` <strong>🔍 Button Preview</strong><br> Text: "${buttonText}"<br> Has onclick: ${hasOnclick}<br> Onclick: ${onclickText}...<br> ${ buttonText.toLowerCase().includes("bid") ? "✅ Contains 'bid'!" : "❓ Check if this is correct" } `; document.body.appendChild(debugInfo); } function handleButtonLeave(event) { event.target.classList.remove("bidder-button-highlight"); // Remove debug info const debugInfo = document.getElementById("bidder-debug-info"); if (debugInfo) debugInfo.remove(); } function handleButtonClick(event, originalHandlers) { // Prevent the original button action event.preventDefault(); event.stopPropagation(); event.stopImmediatePropagation(); const button = event.target; // Remove highlight class button.classList.remove("bidder-button-highlight"); log(`Selected button: "${button.textContent.trim()}"`); // Generate a unique selector for this button const selector = generateUniqueSelector(button); log(`Generated selector: ${selector}`); setSelectedButton(selector); // Clean up selection mode IMMEDIATELY disableButtonSelection(); // Start the bidding process after a short delay setTimeout(() => { if (isRunning()) { attemptBid(); } }, 100); return false; } function disableButtonSelection() { // Remove style const style = document.getElementById("bidder-button-selection-style"); if (style) style.remove(); // Remove overlay and instruction const overlay = document.getElementById("bidder-overlay"); if (overlay) overlay.remove(); const instruction = document.getElementById("bidder-instruction"); if (instruction) instruction.remove(); // Remove event listeners from all buttons and restore original handlers const allButtons = document.querySelectorAll("button"); const originalHandlers = window.bidderOriginalHandlers; allButtons.forEach((button) => { button.removeEventListener("mouseenter", handleButtonHover); button.removeEventListener("mouseleave", handleButtonLeave); button.classList.remove("bidder-button-highlight"); // Restore original onclick handler if we stored one if (originalHandlers && originalHandlers.has(button)) { button.onclick = originalHandlers.get(button); } }); // Clean up global storage if (window.bidderOriginalHandlers) { delete window.bidderOriginalHandlers; } document.removeEventListener("keydown", handleEscapeKey); } function handleEscapeKey(event) { if (event.key === "Escape") { log("Button selection cancelled"); stopBot(); } } function generateUniqueSelector(element) { // If we clicked on a span inside a button, get the button instead let targetElement = element; if ( element.tagName === "SPAN" && element.parentElement && element.parentElement.tagName === "BUTTON" ) { targetElement = element.parentElement; log("Adjusted target from span to parent button"); } // Try ID first - but validate it's a proper CSS ID if (targetElement.id) { // Check if ID starts with a digit (invalid CSS selector) if (!/^[a-zA-Z_]/.test(targetElement.id)) { // Use attribute selector instead for numeric IDs const escapedId = targetElement.id.replace( /[!"#$%&'()*+,.\/:;<=>?@[\\\]^`{|}~]/g, "\\$&" ); const selector = `[id="${escapedId}"]`; if (document.querySelectorAll(selector).length === 1) { return selector; } } else { return `#${targetElement.id}`; } } // Try class combination if (targetElement.className) { const classes = targetElement.className .split(" ") .filter((c) => c.trim() && c !== "bidder-button-highlight"); if (classes.length > 0) { let selector = `button.${classes.join(".")}`; if (document.querySelectorAll(selector).length === 1) { return selector; } } } // Try parent-child relationship let parent = targetElement.parentElement; if (parent) { let parentSelector = ""; // Handle parent ID (with same validation) if (parent.id) { if (!/^[a-zA-Z_]/.test(parent.id)) { const escapedId = parent.id.replace( /[!"#$%&'()*+,.\/:;<=>?@[\\\]^`{|}~]/g, "\\$&" ); parentSelector = `[id="${escapedId}"]`; } else { parentSelector = `#${parent.id}`; } const selector = `${parentSelector} > button`; if (document.querySelectorAll(selector).length === 1) { return selector; } } // Try parent class if (parent.className) { const classes = parent.className.split(" ").filter((c) => c.trim()); if (classes.length > 0) { let selector = `.${classes.join(".")} > button`; if (document.querySelectorAll(selector).length === 1) { return selector; } } } } // Try onclick attribute matching if (targetElement.onclick) { const onclickStr = targetElement.onclick.toString(); if (onclickStr.includes("auction_bid")) { return 'button[onclick*="auction_bid"]'; } } // Text content-based selector (store text, not CSS selector) const buttonText = targetElement.textContent.trim(); if (buttonText) { const buttonsWithSameText = Array.from( document.querySelectorAll("button") ).filter((btn) => btn.textContent.trim() === buttonText); if (buttonsWithSameText.length === 1) { // Return a special text-based identifier return `TEXT:${buttonText}`; } } // Position-based selector as fallback const siblings = Array.from(targetElement.parentElement?.children || []); const buttonSiblings = siblings.filter((el) => el.tagName === "BUTTON"); if (buttonSiblings.length > 1) { const index = buttonSiblings.indexOf(targetElement) + 1; if (parent) { let parentSelector = ""; if (parent.id) { if (!/^[a-zA-Z_]/.test(parent.id)) { const escapedId = parent.id.replace( /[!"#$%&'()*+,.\/:;<=>?@[\\\]^`{|}~]/g, "\\$&" ); parentSelector = `[id="${escapedId}"]`; } else { parentSelector = `#${parent.id}`; } return `${parentSelector} button:nth-of-type(${index})`; } if (parent.className) { const classes = parent.className.split(" ").filter((c) => c.trim()); if (classes.length > 0) { return `.${classes.join(".")} button:nth-of-type(${index})`; } } } } // Final fallback: XPath-like approach but ensure we target the button function getElementPath(el) { if (el.id) { if (!/^[a-zA-Z_]/.test(el.id)) { const escapedId = el.id.replace( /[!"#$%&'()*+,.\/:;<=>?@[\\\]^`{|}~]/g, "\\$&" ); return `[id="${escapedId}"]`; } else { return `#${el.id}`; } } if (el === document.body) return "body"; const parent = el.parentElement; if (!parent) return el.tagName.toLowerCase(); const siblings = Array.from(parent.children); const sameTagSiblings = siblings.filter( (sibling) => sibling.tagName === el.tagName ); if (sameTagSiblings.length === 1) { return `${getElementPath(parent)} > ${el.tagName.toLowerCase()}`; } else { const index = sameTagSiblings.indexOf(el) + 1; return `${getElementPath( parent )} > ${el.tagName.toLowerCase()}:nth-of-type(${index})`; } } return getElementPath(targetElement); } function attemptBid() { if (!isRunning()) return; const bidCount = getBidCount(); const buttonSelector = getSelectedButton(); if (!buttonSelector) { log("❌ No button selected"); stopBot(); return; } log(`Bid attempt #${bidCount + 1} - Looking for selected bid button...`); const bidButton = findSelectedBidButton(); if (!bidButton) { log( `❌ Selected bid button not found with selector: ${buttonSelector}` ); stopBot(); return; } log("✅ Found selected bid button, clicking..."); // Make sure we're clicking the actual button element if (bidButton.tagName === "BUTTON") { bidButton.click(); } else { log("❌ Found element is not a button"); stopBot(); return; } incrementBidCount(); // Wait for confirmation dialog setTimeout(() => { if (isRunning()) { handleBidConfirmation(); } }, 100); } function handleBidConfirmation() { if (!isRunning()) return; log("🔍 Looking for bid confirmation dialog..."); // Look for the Ok button in the confirmation dialog const okButton = document.querySelector( ".ui-dialog-buttonset button.ui-button" ); if (!okButton) { log("❌ No confirmation dialog found, retrying bid..."); setTimeout(() => { if (isRunning()) { attemptBid(); } }, 250); return; } log("✅ Found confirmation dialog, clicking Ok..."); okButton.click(); // Wait for result dialog setTimeout(() => { if (isRunning()) { handleBidResult(); } }, 250); } function handleBidResult() { if (!isRunning()) return; log("🔍 Checking bid result..."); // Look for the result dialog const dialogContent = document.querySelector("#dialog.ui-dialog-content"); if (!dialogContent) { log("❌ No result dialog found, retrying bid..."); setTimeout(() => { if (isRunning()) { attemptBid(); } }, 250); return; } const dialogText = dialogContent.textContent.trim(); log(`Dialog text: "${dialogText}"`); // Check what type of result we got if (dialogText.includes("You were outbid")) { log("🔄 Outbid! Closing dialog and trying again..."); handleOutbid(); } else if (dialogText.includes("You don't have enough Credits")) { log("💰 Out of credits! Waiting 30 seconds before retry..."); handleOutOfCredits(); } else { log(`❓ Unknown dialog result: "${dialogText}"`); // Try to close any dialog and retry closeDialog(); setTimeout(() => { if (isRunning()) { attemptBid(); } }, 1000); } } function handleOutbid() { if (!isRunning()) return; // Close the outbid dialog closeDialog(); // Immediate retry setTimeout(() => { if (isRunning()) { attemptBid(); } }, 250); } function handleOutOfCredits() { if (!isRunning()) return; // Close the credits dialog closeDialog(); // Wait 30 seconds before retry log("⏰ Waiting 30 seconds for more credits..."); retryTimeout = setTimeout(() => { if (isRunning()) { log("🔄 Retrying after credit wait..."); attemptBid(); } }, 30000); } function closeDialog() { // Try multiple selectors to find and click the Ok button const okSelectors = [ ".ui-dialog-buttonset button.ui-button", ".ui-dialog-buttonpane button.ui-button", '.ui-dialog button[type="button"]:last-child', ]; for (const selector of okSelectors) { const okButton = document.querySelector(selector); if (okButton && okButton.textContent.trim() === "Ok") { log("✅ Closing dialog..."); okButton.click(); return true; } } // Fallback: try to find any Ok button const allButtons = document.querySelectorAll("button.ui-button"); for (const button of allButtons) { if (button.textContent.trim() === "Ok") { log("✅ Found Ok button, closing dialog..."); button.click(); return true; } } log("❌ Could not find Ok button to close dialog"); return false; } function main() { if (!isRunning()) return; log("🚀 Bidder main executing..."); // Check if we're on an auction page if (!location.href.includes("auction=")) { log("❌ Not on auction page, stopping bidder"); stopBot(); return; } // Check if we have a selected button const selectedButton = getSelectedButton(); if (!selectedButton) { log("📍 No button selected, please select a bid button"); return; } // If we don't have any pending operations, start bidding const hasDialog = document.querySelector(".ui-dialog"); if (!hasDialog) { log("📍 No active dialogs, starting bid attempt..."); setTimeout(() => { if (isRunning()) { attemptBid(); } }, 100); } else { log("📍 Dialog detected, handling result..."); setTimeout(() => { if (isRunning()) { handleBidResult(); } }, 100); } } function getStatus() { if (!isRunning()) { return "Stopped"; } const bidCount = getBidCount(); const hasButton = getSelectedButton() ? "✅" : "❌"; return `Running - ${bidCount} bids placed ${hasButton}`; } return { startBot, stopBot, main, getStatus, isRunning: isRunning, }; })(); function createControls() { // 1) If the panel already exists, do nothing if (document.getElementById("bot-controls")) return; // 2) Inject a <style> block with modern/tabbed styles const css = ` /* Container panel */ #bot-controls { position: fixed; bottom: 20px; left: 20px; width: 250px; background: rgba(0, 0, 0, 0.8); color: #fff; border-radius: 8px; font-family: "Segoe UI", Tahoma, sans-serif; font-size: 13px; z-index: 9999; user-select: none; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); } /* Header bar (drag handle + title + toggle) */ #bot-header { padding: 8px 12px; border-bottom: 1px solid #444; display: flex; align-items: center; justify-content: space-between; cursor: move; } #bot-header span { display: inline-flex; align-items: center; } #status-dot { width: 8px; height: 8px; border-radius: 50%; background: #e74c3c; /* softer red */ margin-right: 6px; flex-shrink: 0; } /* Collapse / Expand toggle */ #collapse-toggle { cursor: pointer; font-size: 12px; transition: transform 0.2s ease, color 0.2s ease; } #collapse-toggle:hover { color: #ddd; } /* Main content area */ #bot-content { padding: 10px; } /* “Progress” text */ #friend-progress { display: block; margin-bottom: 6px; font-size: 12px; color: #ccc; } /* Queue visualizer box */ #queue-visualizer { margin: 6px 0; padding: 6px; background: rgba(255, 255, 255, 0.1); border-radius: 4px; } .queue-counter { font-size: 12px; color: #ccc; transition: transform 0.15s ease; } #queue-visualizer span:last-child { font-size: 10px; color: #999; } /* Generic button style */ .bot-button { margin: 2px; padding: 5px 10px; font-size: 12px; background: #333; border: none; border-radius: 4px; color: #fff; cursor: pointer; transition: background 0.2s ease; } .bot-button:hover { background: #444; } /* Section wrapper style */ .bot-section { margin-top: 10px; padding-top: 8px; border-top: 1px solid #444; } .bot-section-header { margin-bottom: 5px; font-weight: bold; } /* Tab bar */ #tab-bar { display: flex; margin-top: 10px; border-bottom: 1px solid #444; } .tab-button { flex: 1; text-align: center; padding: 6px 0; cursor: pointer; font-size: 12px; background: #2c2c2c; transition: background 0.2s ease; user-select: none; } .tab-button:hover { background: #3a3a3a; } .tab-button.active { background: #444; border-bottom: 2px solid #e74c3c; } /* Tab panels (only one visible at a time) */ .tab-panel { display: none; } .tab-panel.active { display: block; } /* add an ios style switch to toggle evil mode with the id evil-mode-toggle it has some margin left and the text is on the right side of the switch that says "evil mode" and the switch is a circle in a rectangular "track" where off is having the circle at the right with a green background and "on" is the circle on the right with a red background */ .switch { position: relative; display: inline-block; width: 34px; height: 20px; } .switch input { opacity: 0; width: 0; height: 0; } .slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; transition: .4s; border-radius: 34px; } .slider:before { position: absolute; content: ""; height: 20px; width: 20px; left: 0; bottom: 0; background-color: #fff; transition: .4s; border-radius: 34px; } input:checked + .slider { background-color: #e74c3c; } input:checked + .slider:before { transform: translateX(14px); } `; const styleTag = document.createElement("style"); styleTag.textContent = css; document.head.appendChild(styleTag); // 3) Create the panel container const panel = document.createElement("div"); panel.id = "bot-controls"; document.body.appendChild(panel); // 4) Build out the inner HTML with tabs panel.innerHTML = ` <div id="bot-header"> <span> <div id="status-dot"></div> <span>🤖 Ovipets Bot</span> <label class="switch" style="margin-left: 10px;"> <input type="checkbox" id="evil-mode-toggle"> <span class="slider"></span> <span style="color: #ccc; float: left; white-space: nowrap; margin-left: 40px; margin-bottom: 20px; margin-top: -20px;">Evil Mode</span> </label> </span> <div id="collapse-toggle" title="Collapse panel">▼</div> </div> <div id="bot-content"> <!-- Progress + Queue --> <div id="friend-progress">👥 0/0</div> <div id="queue-visualizer"> <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 4px;"> <span class="queue-counter">🥚 0</span> <span>Egg Queue</span> </div> <!-- Bars inserted dynamically by updateQueueVisualizer() --> </div> <!-- Main Bot Controls (always visible) --> <div id="main-controls" style="margin-bottom: 8px;"> <button id="start-bot" class="bot-button">▶️ START</button> <button id="stop-bot" class="bot-button">⏹️ STOP</button> <!-- peaceful mode checkbox --> <label style="margin-left: 10px;"> <input type="checkbox" style="margin-bottom: -50px;padding-top: 50px;float: inline-end;"id="peaceful-mode"> <span style="color: #ccc; margin-left: 10px;">Peaceful Mode</span> </label> </div> <!-- Tab Bar --> <div id="tab-bar"> <div class="tab-button active" data-tab="old-bots">Old Bots</div> <div class="tab-button" data-tab="my-pets">My Pets</div> <div class="tab-button" data-tab="other">Other</div> </div> <!-- Tab Panels --> <div id="tab-content"> <!-- Old Bots Panel --> <div id="tab-old-bots" class="tab-panel active"> <div class="bot-section"> <div class="bot-section-header">🔄 Old Across</div> <button id="old-across-start" class="bot-button">▶️ Start</button> <button id="old-across-stop" class="bot-button">⏹️ Stop</button> <button id="ovipets-old-across-resume" class="bot-button" style="display: none;">⏯️ Resume</button> </div> <div class="bot-section"> <div class="bot-section-header">🔍 Old Thorough</div> <button id="old-thorough-start" class="bot-button">▶️ Start</button> <button id="old-thorough-stop" class="bot-button">⏹️ Stop</button> <button id="ovipets-old-thorough-resume" class="bot-button" style="display: none;">⏯️ Resume</button> </div> </div> <!-- My Pets Panel --> <div id="tab-my-pets" class="tab-panel"> <div class="bot-section"> <div class="bot-section-header">💕 Breeder</div> <button id="breeder-start" class="bot-button">▶️ Start</button> <button id="breeder-stop" class="bot-button">⏹️ Stop</button> </div> <div class="bot-section"> <div class="bot-section-header">🍖 Feeder</div> <button id="feeder-start" class="bot-button">▶️ Start</button> <button id="feeder-stop" class="bot-button">⏹️ Stop</button> </div> <div class="bot-section"> <div class="bot-section-header">🧬 Genetic Profiler</div> <button id="genetic-profiler-start" class="bot-button">▶️ Start</button> <button id="genetic-profiler-stop" class="bot-button">⏹️ Stop</button> </div> <div class="bot-section"> <div class="bot-section-header">📝 Namer</div> <button id="namer-start" class="bot-button">▶️ Start</button> <button id="namer-stop" class="bot-button">⏹️ Stop</button> </div> <div class="bot-section"> <div class="bot-section-header">🥚 Hatcher</div> <button id="hatcher-start" class="bot-button">▶️ Start</button> <button id="hatcher-stop" class="bot-button">⏹️ Stop</button> </div> </div> <!-- Other Panel --> <div id="tab-other" class="tab-panel"> <div class="bot-section"> <div class="bot-section-header">📨 Befriender</div> <button id="befriender-start" class="bot-button">▶️ Start</button> <button id="befriender-stop" class="bot-button">⏹️ Stop</button> </div> <div class="bot-section"> <div class="bot-section-header">🏠 Adopter</div> <button id="adopter-start" class="bot-button">▶️ Start</button> <button id="adopter-stop" class="bot-button">⏹️ Stop</button> </div> <div class="bot-section"> <div class="bot-section-header">📸 Image Collector</div> <button id="image-collector-start" class="bot-button">▶️ Start</button> <button id="image-collector-stop" class="bot-button">⏹️ Stop</button> </div> <div class="bot-section"> <div class="bot-section-header">🔨 Bidder</div> <button id="bidder-start" class="bot-button">▶️ Start</button> <button id="bidder-stop" class="bot-button">⏹️ Stop</button> </div> </div> </div> </div> `; // 5) Add collapse/expand functionality let isCollapsed = false; const collapseToggle = document.getElementById("collapse-toggle"); const botContent = document.getElementById("bot-content"); const evilModeToggle = document.getElementById("evil-mode-toggle"); const peacefulModeToggle = document.getElementById("peaceful-mode"); collapseToggle.onclick = (e) => { e.stopPropagation(); // Don’t trigger drag when clicking toggle isCollapsed = !isCollapsed; if (isCollapsed) { botContent.style.display = "none"; collapseToggle.textContent = "▲"; collapseToggle.title = "Expand panel"; } else { botContent.style.display = "block"; collapseToggle.textContent = "▼"; collapseToggle.title = "Collapse panel"; } }; // 6) Add drag functionality (header acts as grab handle) let isDragging = false; let dragStartX = 0, dragStartY = 0; let panelStartX = 0, panelStartY = 0; const header = document.getElementById("bot-header"); header.onmousedown = (e) => { // Don’t start if user clicked the collapse toggle if (e.target.id === "collapse-toggle") return; isDragging = true; dragStartX = e.clientX; dragStartY = e.clientY; const rect = panel.getBoundingClientRect(); panelStartX = rect.left; panelStartY = rect.top; // Show grabbing cursor document.body.style.cursor = "grabbing"; header.style.cursor = "grabbing"; e.preventDefault(); }; document.onmousemove = (e) => { if (!isDragging) return; const deltaX = e.clientX - dragStartX; const deltaY = e.clientY - dragStartY; let newX = panelStartX + deltaX; let newY = panelStartY + deltaY; // Keep panel within viewport const maxX = window.innerWidth - panel.offsetWidth; const maxY = window.innerHeight - panel.offsetHeight; newX = Math.max(0, Math.min(newX, maxX)); newY = Math.max(0, Math.min(newY, maxY)); panel.style.left = newX + "px"; panel.style.top = newY + "px"; panel.style.bottom = "auto"; // Clear bottom when dragging }; document.onmouseup = () => { if (isDragging) { isDragging = false; document.body.style.cursor = ""; header.style.cursor = "move"; } }; evilModeToggle.onclick = () => { isEvil = evilModeToggle.classList.toggle("active"); log(`Evil mode is now ${isEvil ? "ON" : "OFF"}`); // make the panel have a deep red background when evil mode is on, with the same transparency though panel.style.background = isEvil ? "rgba(139, 0, 0, 0.8)" // Dark red with transparency : "rgba(0, 0, 0, 0.8)"; // Default black with transparency }; peacefulModeToggle.onclick = () => { isPeaceful = peacefulModeToggle.checked; log(`Peaceful mode is now ${isPeaceful ? "ON" : "OFF"}`); }; // 7) Wire up all button event listeners exactly as before // Main bot controls document.getElementById("start-bot").onclick = startBot; document.getElementById("stop-bot").onclick = stopBot; // Old Across document.getElementById("old-across-start").onclick = OvipetsOldAcross.startBot; document.getElementById("old-across-stop").onclick = OvipetsOldAcross.stopBot; document.getElementById("ovipets-old-across-resume").onclick = OvipetsOldAcross.resumeBot; // Old Thorough document.getElementById("old-thorough-start").onclick = OvipetsOldThorough.startBot; document.getElementById("old-thorough-stop").onclick = OvipetsOldThorough.stopBot; document.getElementById("ovipets-old-thorough-resume").onclick = OvipetsOldThorough.resumeBot; // Breeder document.getElementById("breeder-start").onclick = Breeder.startBot; document.getElementById("breeder-stop").onclick = Breeder.stopBot; // Feeder document.getElementById("feeder-start").onclick = Feeder.startBot; document.getElementById("feeder-stop").onclick = Feeder.stopBot; // Genetic Profiler document.getElementById("genetic-profiler-start").onclick = GeneticProfiler.startBot; document.getElementById("genetic-profiler-stop").onclick = GeneticProfiler.stopBot; // Namer document.getElementById("namer-start").onclick = Namer.startBot; document.getElementById("namer-stop").onclick = Namer.stopBot; // Befriender document.getElementById("befriender-start").onclick = Befriender.startBot; document.getElementById("befriender-stop").onclick = Befriender.stopBot; // Adopter document.getElementById("adopter-start").onclick = Adopter.startBot; document.getElementById("adopter-stop").onclick = Adopter.stopBot; document.getElementById("hatcher-start").onclick = Hatcher.startBot; document.getElementById("hatcher-stop").onclick = Hatcher.stopBot; // In the createControls function, add these event listeners: // Bidder document.getElementById("bidder-start").onclick = Bidder.startBot; document.getElementById("bidder-stop").onclick = Bidder.stopBot; // Image Collector document.getElementById("image-collector-start").onclick = ImageCollector.startBot; document.getElementById("image-collector-stop").onclick = ImageCollector.stopBot; // 8) Tab‐switching logic const tabButtons = Array.from(document.querySelectorAll(".tab-button")); const tabPanels = { "old-bots": document.getElementById("tab-old-bots"), "my-pets": document.getElementById("tab-my-pets"), other: document.getElementById("tab-other"), }; tabButtons.forEach((btn) => { btn.addEventListener("click", () => { const target = btn.getAttribute("data-tab"); // 1. Toggle active class on buttons tabButtons.forEach((b) => b.classList.remove("active")); btn.classList.add("active"); // 2. Show/hide panels Object.keys(tabPanels).forEach((key) => { if (key === target) { tabPanels[key].classList.add("active"); } else { tabPanels[key].classList.remove("active"); } }); }); }); // 9) Initialize status indicator & queue, and resume progress if running log("Controls created successfully"); updateStatusIndicator(); updateQueueVisualizer(); if (isRunning()) { log("Bot already running, resuming progress updates"); updateProgress(); pollInterval = setInterval(() => { pollForEggs(); updateProgress(); }, 3000); } } // Make sure controls are created when DOM is ready if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", () => { createControls(); restoreState(); // Restore state after controls are created }); } else { createControls(); restoreState(); // Restore state immediately if DOM is already ready } // Also try on window load as backup window.addEventListener("load", () => { createControls(); Befriender.main(); Adopter.main(); Breeder.main(); Namer.main(); // Add this line Hatcher.main(); Bidder.main(); Feeder.main(); GeneticProfiler.main(); OvipetsOldAcross.main(); OvipetsOldThorough.main(); }); window.addEventListener("hashchange", () => { Befriender.main(); Adopter.main(); Breeder.main(); Namer.main(); // Add this line Hatcher.main(); Bidder.main(); Feeder.main(); GeneticProfiler.main(); OvipetsOldAcross.main(); OvipetsOldThorough.main(); }); })();