您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Go, go, go, go, go, go, go Gotta go fast Gotta go fast Gotta go faster, faster, faster, faster, faster! Sonic X
// ==UserScript== // @name Gotta go fast - PPM Autographs // @namespace Violentmonkey Scripts // @author Drinkwater // @license MIT // @match https://*.popmundo.com/World/Popmundo.aspx/Character/Items/* // @grant none // @version 1.7 // @description Go, go, go, go, go, go, go Gotta go fast Gotta go fast Gotta go faster, faster, faster, faster, faster! Sonic X // ==/UserScript== (function () { 'use strict'; let timeInFirstCollect = 0; let firstBookTimestamp = null; let minuteDelay = 5; // Delay padrão de 5 minutos let remainingDelay = 0; let firstBookId = 0; let indexPeopleBloc = 0; let fixedBookIds = []; let isProcessingBlock = false; // Para evitar múltiplas chamadas ao temporizador let continuaColeta = false; // Função para coletar as pessoas dentro do contexto do iframe async function getPeopleToCollect(iframe) { let iframeDocument = iframe.contentDocument || iframe.contentWindow.document; let people = []; let initialPeopleTable = iframeDocument.querySelector('#tablepeople'); // Check the #ctl00_cphLeftColumn_ctl00_chkAutograph checkbox let autographCheckbox = iframeDocument.querySelector('#ctl00_cphLeftColumn_ctl00_chkAutograph'); if (autographCheckbox) { autographCheckbox.checked = true; } // Uncheck other checkboxes let otherCheckboxes = [ '#ctl00_cphLeftColumn_ctl00_chkOnline', '#ctl00_cphLeftColumn_ctl00_chkGame', '#ctl00_cphLeftColumn_ctl00_chkRelationships' ]; otherCheckboxes.forEach(selector => { let checkbox = iframeDocument.querySelector(selector); if (checkbox && checkbox.checked) { checkbox.checked = false; } }); // Trigger the filter button click let filterButton = iframeDocument.querySelector('#ctl00_cphLeftColumn_ctl00_btnFilter'); if (filterButton) { filterButton.click(); } else { throw new Error("Filter button not found."); } // Check every second if the people table has updated return new Promise((resolve) => { let interval = setInterval(() => { let newIframeDocument = iframe.contentDocument || iframe.contentWindow.document; let newPeopleTable = newIframeDocument.querySelector('#tablepeople'); if (newPeopleTable && newPeopleTable !== initialPeopleTable) { clearInterval(interval); // Stop checking // Collect data from the new table Array.from(newPeopleTable.querySelectorAll('tbody tr')).forEach(row => { let characterLink = row.querySelector('a'); let statusText = row.querySelectorAll('td')[1]?.textContent.trim(); let status = (!statusText || statusText === "Online") ? "Disponível" : "Ocupado"; if (status === "Disponível" && characterLink) { people.push({ name: characterLink.textContent, id: characterLink.href.split('/').pop(), status: status }); } }); resolve(people); // Resolve the Promise with available people } }, 1000); }); } // Função para criar o iframe e garantir que ele esteja completamente carregado async function createIframe() { let domain = window.location.hostname; let path = '/World/Popmundo.aspx/City/PeopleOnline/'; let url = 'https://' + domain + path; // Cria o iframe let iframe = document.createElement('iframe'); iframe.src = url; iframe.style.display = 'none'; document.body.appendChild(iframe); // Retorna o iframe, mas garante que ele esteja carregado antes de usá-lo return new Promise((resolve, reject) => { iframe.onload = function () { resolve(iframe); }; iframe.onerror = function () { reject('Erro ao carregar o iframe'); }; }); } // Função para logar dados let LOG_INDEX = 0; function log(data) { if (window.parent === window) { jQuery("#logs-autografos").append(`<tr class="${LOG_INDEX % 2 == 0 ? "odd" : "even"}" drinkwater><td drinkwater>${data}</td></tr>`); LOG_INDEX++; } } async function goToLocation(iframe, charId, charName) { let iframeActualHost = iframe.contentWindow.location.host; let domain = iframeActualHost; let path = `/World/Popmundo.aspx/Character/${charId}`; let url = 'https://' + domain + path; // Carrega a primeira URL e espera carregar iframe.src = url; await waitForIframeLoad(iframe); let iframeDocument = iframe.contentDocument || iframe.contentWindow.document; // Tenta acessar o link de interação let locationLink = iframeDocument.querySelector('#ctl00_cphRightColumn_ctl00_lnkInteract')?.href || iframeDocument.querySelector('#ctl00_cphRightColumn_ctl00_btnInteract')?.href; let links if (!locationLink) { let characterPresentation = iframeDocument.querySelector('.characterPresentation'); if (characterPresentation) { links = characterPresentation.querySelectorAll('a'); if (links.length > 0) { let lastLink = links[links.length - 1]; let href = lastLink.getAttribute('href'); let locationId = href.split('/').pop(); } if (!locationId) { log(`Talvez ${charName} não está mais na cidade, ou algo aconteceu!`); return; } locationLink = `https://${domain}/World/Popmundo.aspx/Locale/MoveToLocale/${locationId}/${charId}`; } } if (locationLink.startsWith('javascript:')) { //click the link if it starts with javascript: const interactBtn = iframeDocument.querySelector('#ctl00_cphRightColumn_ctl00_btnInteract'); interactBtn.click(); await waitForIframeLoad(iframe); return; } // Remove o domínio, mantendo apenas a parte a partir de /World/ let relativePath = locationLink.includes('/World/') ? locationLink.split('/World/')[1] : null; if (!relativePath) { log('Algo de errado não está certo! Mas continuamos !'); console.log(relativePath) console.log(locationLink) return; } // Cria a nova URL com o caminho relativo let newUrl = 'https://' + iframe.contentWindow.location.host + '/World/' + relativePath; log(`Movendo até o local de <b>${charName}</b>`); iframe.src = newUrl; // Aguarda o segundo carregamento do iframe await waitForIframeLoad(iframe); } // Função para aguardar o carregamento do iframe function waitForIframeLoad(iframe) { return new Promise((resolve) => { iframe.onload = function () { //simular tempo humano de espera setTimeout(() => { resolve(); }, 2000); // Espera 1 segundo para simular o tempo de carregamento humano }; }); } async function getBookIds(iframe, person) { let iframeDocument = iframe.contentDocument || iframe.contentWindow.document; let bookIds = []; let select = jQuery(iframeDocument).find('#ctl00_cphTopColumn_ctl00_ddlUseItem'); if (select.length === 0) { log(`Aparentemente <b>${person.name}</b> não está mais disponível ou não deixa usar itens`); // Armazenar o char no localStorage para não tentar novamente let blockedChars = JSON.parse(localStorage.getItem('chars-block-itens')) || []; if (!blockedChars.includes(person.id)) { blockedChars.push(person.id); localStorage.setItem('chars-block-itens', JSON.stringify(blockedChars)); log(`Armazenando char ${person.name} (${person.id}) no localStorage para não tentar novamente.`); } await new Promise(resolve => setTimeout(resolve, 1000)); return []; } jQuery(iframeDocument).find('#ctl00_cphTopColumn_ctl00_ddlUseItem option').each(function () { let optionText = jQuery(this).text().trim(); let optionValue = jQuery(this).val(); if (optionText === 'Livro de autógrafos' || optionText === 'Autograph book') { bookIds.push(optionValue); if (firstBookId === 0) { firstBookId = optionValue; } } }); return bookIds; } async function collectAutograph(iframe, bookId, person, isLastPersonInBlock = false, iframeLoadsInCycle = 1) { let iframeDocument = iframe.contentDocument || iframe.contentWindow.document; let select = iframeDocument.querySelector('#ctl00_cphTopColumn_ctl00_ddlUseItem'); if (!select || select.length === 0) { return; } select.value = bookId; if (bookId === firstBookId && !firstBookTimestamp) { firstBookTimestamp = Date.now(); log(`Primeiro uso do livro às: ${new Date(firstBookTimestamp).toLocaleTimeString()}`); } let submitButton = iframeDocument.querySelector('#ctl00_cphTopColumn_ctl00_btnUseItem'); if (!submitButton) { log(`Não foi possível encontrar o botão de uso do item para <b>${person.name}</b>`); return; } submitButton.click(); await waitForIframeLoad(iframe); // Na última pessoa, calcular o delay restante baseado no primeiro uso do livro if (isLastPersonInBlock && firstBookTimestamp) { // Considera o tempo de humanização (2s por carregamento de iframe) let now = Date.now(); let elapsedMs = now - firstBookTimestamp; let iframeHumanizationMs = iframeLoadsInCycle * 2000; let elapsedTime = Math.floor((elapsedMs + iframeHumanizationMs) / 60000); remainingDelay = Math.max(0, minuteDelay - elapsedTime); // Armazenar o delay calculado log(`Tempo decorrido desde o primeiro uso (incluindo humanização): ${elapsedTime} minutos.`); log(`Delay restante para o próximo bloco: ${remainingDelay} minutos.`); // Resetar o timestamp para o próximo bloco firstBookTimestamp = null; } } function startDelayTimer(minutes) { let timerMessage = jQuery('#timer-message'); let totalSeconds = minutes * 60; return new Promise((resolve) => { const interval = setInterval(() => { let minutesLeft = Math.floor(totalSeconds / 60); let secondsLeft = totalSeconds % 60; timerMessage.text(`Esperando: ${minutesLeft} minutos e ${secondsLeft} segundos restantes...`); if (totalSeconds <= 0) { clearInterval(interval); timerMessage.text('Continuando a coleta de autógrafos...'); setTimeout(() => timerMessage.text(''), 2000); // Limpa a mensagem após 2 segundos resolve(); } totalSeconds--; }, 1000); }); } jQuery(document).ready(function () { jQuery('#checkedlist').before('<div class="box" id="autografos-box" drinkwater><h2 drinkwater>Coletar Autógrafos</h2></div>'); jQuery('#autografos-box').append('<p drinkwater>O script usará todos os livros do seu inventário, para coletar autógrafos de popstars presentes na cidade!</p>'); jQuery('#autografos-box').append('<p class="actionbuttons" drinkwater> <input type="button" name="btn-iniciar-coleta" value="Iniciar" id="inicar-coleta" class="rmargin5" drinkwater> <input type="button" name="btn-parar-coleta" value="Parar" id="parar-coleta" class="rmargin5" drinkwater> <input type="button" name="btn-clear-storage" value="Limpar chars que não aceitam uso de itens" id="limpar-chars" class="rmargin5" drinkwater></p>'); jQuery('#autografos-box').append('<div id="timer-message" style="font-weight: bold; color: red;" drinkwater></div>'); jQuery('#autografos-box').append('<table id="logs-autografos" class="data dataTable" drinkwater></table>'); jQuery('#logs-autografos').append('<tbody drinkwater><tr drinkwater><th drinkwater>Logs</th></tr></tbody>'); let bookAmount; const bookElement = jQuery('#checkedlist a:contains("Livro de autógrafos")'); if (bookElement.length > 0) { const bookQuantity = bookElement.closest('td').find('em').text().trim(); debugger if (bookQuantity.startsWith('x')) { bookAmount = parseInt(bookQuantity.substring(1)); log(`Quantidade de livros de autógrafos encontrada: ${bookAmount}`); } else { bookAmount = bookElement.length; } } else { log('Nenhum Livro de autógrafos encontrado.'); } let bookIndex = 0; // Inicia o índice do livro let lastCycleIds = []; // ← NOVO: quem recebeu autógrafo no ciclo anterior jQuery('#inicar-coleta').click(async function () { continuaColeta = true; jQuery('#inicar-coleta').prop('disabled', true); jQuery('#parar-coleta').prop('disabled', false); jQuery('#inicar-coleta').prop('value', 'Coletando Autografos...'); // utilitário de espera const esperarSegundos = s => new Promise(r => setTimeout(r, s * 1000)); /* ------------------------------------------------------------------ * LOOP PRINCIPAL • tenta usar exatamente "bookAmount" livros por ciclo * -----------------------------------------------------------------*/ while (continuaColeta) { try { /* ---------- snapshot inicial ---------- */ let iframe = await createIframe(); let queue = await getPeopleToCollect(iframe); const blockedChars = JSON.parse(localStorage.getItem('chars-block-itens')) || []; queue = queue.filter( p => !blockedChars.includes(p.id) && !lastCycleIds.includes(p.id) ); if (queue.length === 0) { log('Nenhuma pessoa elegível encontrada. Tentarei novamente em 60 s.'); await esperarSegundos(60); continue; } let livrosUsados = 0; // quantos livros já foram consumidos neste ciclo let primeiroUsoTs = null; // marca o 1.º autógrafo do ciclo let currentCycleIds = []; // ← NOVO let iframeLoadsInCycle = 0; // NOVO: conta carregamentos de iframe /* ---------- continua até consumir "bookAmount" livros ---------- */ while (livrosUsados < bookAmount && continuaColeta) { // se a fila esvaziar antes de completar a cota, faz novo snapshot if (queue.length === 0) { iframe = await createIframe(); iframeLoadsInCycle++; // conta carregamento extra queue = (await getPeopleToCollect(iframe)) .filter(p => !blockedChars.includes(p.id)); if (queue.length === 0) break; // ninguém mais disponível } const person = queue.shift(); if (!person) continue; // segurança await goToLocation(iframe, person.id, person.name); iframeLoadsInCycle++; // conta carregamento do goToLocation const bookIds = await getBookIds(iframe, person); if (bookIds.length === 0) continue; // não aceita itens → tenta próximo if (!primeiroUsoTs) primeiroUsoTs = Date.now(); const livroId = bookIds[bookIndex % bookIds.length]; log(`Coletando autógrafo de <b>${person.name}</b> usando livro ID: ${livroId}`); await collectAutograph(iframe, livroId, person, (livrosUsados + 1 === bookAmount), iframeLoadsInCycle); currentCycleIds.push(person.id); // NOVO bookIndex = (bookIndex + 1) % bookIds.length; livrosUsados++; } lastCycleIds = currentCycleIds; /* ---------- cooldown baseado no 1.º uso do ciclo ---------- */ if (livrosUsados > 0 && primeiroUsoTs) { // Considera o tempo de humanização (2s por carregamento de iframe) const elapsedMin = Math.floor((Date.now() - primeiroUsoTs + iframeLoadsInCycle * 2000) / 60000); const delayMinRest = Math.max(0, minuteDelay - elapsedMin); if (delayMinRest > 0) { log(`Cooldown de ${delayMinRest} min antes do próximo ciclo…`); await startDelayTimer(delayMinRest); } } } catch (err) { console.error(err); log('Erro durante a execução do script – consulte o console.'); } } jQuery('#inicar-coleta').prop('disabled', false); jQuery('#inicar-coleta').prop('value', 'Iniciar'); jQuery('#parar-coleta').prop('disabled', true); log('Coleta de autógrafos interrompida.'); }); jQuery('#parar-coleta').prop('disabled', true); jQuery('#parar-coleta').click(function () { continuaColeta = false; jQuery('#parar-coleta').prop('disabled', true); jQuery('#inicar-coleta').prop('disabled', false); jQuery('#inicar-coleta').prop('value', 'Iniciar'); log('Coleta de autógrafos interrompida pelo usuário.'); }); // Limpar chars que não aceitam uso de itens jQuery('#limpar-chars').click(function () { localStorage.removeItem('chars-block-itens'); log('Storage "chars-block-itens" limpo.'); }); }); })();