您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Exibe doentes dos locais.
// ==UserScript== // @name [Popmundo] Detector de Doentes v3.9.2 // @namespace http://tampermonkey.net/ // @version 3.9.2 // @description Exibe doentes dos locais. // @author Popper // @match *://*.popmundo.com/World/Popmundo.aspx/Locale/CharactersPresent/* // @match *://popmundo.com/World/Popmundo.aspx/Locale/CharactersPresent/* // @match *://*.popmundo.com/World/Popmundo.aspx/City/PeopleOnline/* // @match *://popmundo.com/World/Popmundo.aspx/City/PeopleOnline/* // @match *://*.popmundo.com/World/Popmundo.aspx/Character/Diary/* // Necessário para buscar diário // @match *://popmundo.com/World/Popmundo.aspx/Character/Diary/* // Necessário para buscar diário // @match *://*.popmundo.com/World/Popmundo.aspx/Character/* // Necessário para links de perfil nos resultados // @match *://popmundo.com/World/Popmundo.aspx/Character/* // Necessário para links de perfil nos resultados // @icon https://www.google.com/s2/favicons?sz=64&domain=popmundo.com // @grant GM_xmlhttpRequest // @grant GM_addStyle // @connect self // @connect *.popmundo.com // @license MIT // ==/UserScript== (function() { 'use strict'; console.log("Detector Doentes v3.9.2 (Somente Detecção) Popmundo - Ativado"); // --- Configurações --- const REQUEST_DELAY_MS = 1500; // Atraso entre verificações de diário const SCRIPT_ID_PREFIX = 'pm-sick-detector-v3-9-2'; // Prefixo para IDs const RESULTS_DIV_ID = `${SCRIPT_ID_PREFIX}-results`; const SCAN_BUTTON_ID = `${SCRIPT_ID_PREFIX}-scan-button`; const CONTAINER_ID = `${SCRIPT_ID_PREFIX}-container`; const PROGRESS_LIST_ID = `${SCRIPT_ID_PREFIX}-progress-list`; const ENABLE_DETAILED_LOGGING = false; // Mude para true para mais detalhes no console // --- Mensagens Chave (lowercase) --- const realIllnessMessages = [ 'não estou me sentindo bem', 'zumbi me mordeu', 'clamídia está acabando comigo' // Adicione outras mensagens de doença real aqui, em minúsculas ]; const cureMessage = 'curou minha indisposição'; const ignoredMessage = 'sobrecarga sináptica está acabando comigo'; // Exemplo de mensagem a ignorar // --- Variáveis Globais --- let charactersToCheck = []; // Armazena { id, name, profileUrl, diaryUrl } let currentlySickCharacters = []; // Armazena { id, name, profileUrl, diaryUrl } let scanInProgress = false; let contextName = "Contexto não encontrado"; let isCityPage = window.location.pathname.includes('/City/PeopleOnline/'); // --- Funções Auxiliares --- function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } function extractContextInfo() { if (isCityPage) { const h1 = document.querySelector('#ppm-content h1'); if (h1) { const h1Text = h1.textContent.trim(); const cityMatch = h1Text.match(/^Pessoas em (.*)$/i); contextName = (cityMatch && cityMatch[1]) ? cityMatch[1].trim() : "Cidade desconhecida"; if(ENABLE_DETAILED_LOGGING) console.log(`Contexto (Cidade): ${contextName}`); } else { contextName = "Cidade desconhecida"; console.warn("H1 não encontrado na página da cidade."); } } else { const localeInfoDiv = document.querySelector('div[id*="divLocaleInfo"]'); if (localeInfoDiv) { const localeBox = localeInfoDiv.querySelector('.localebox'); if (localeBox) { const h2 = localeBox.querySelector('h2'); let localeName = h2 ? h2.textContent.trim() : "Local desconhecido"; const cityLink = localeBox.querySelector('.right a'); let cityName = cityLink ? cityLink.textContent.trim() : "Cidade desconhecida"; contextName = `${localeName} (${cityName})`; if(ENABLE_DETAILED_LOGGING) console.log(`Contexto (Local): ${localeName}, Cidade: ${cityName}`); } else { contextName = "Local/Cidade desconhecidos"; console.warn(".localebox não encontrado."); } } else { contextName = "Local/Cidade desconhecidos"; console.warn("divLocaleInfo não encontrado."); } } } // --- Coleta de Dados da Tabela --- function getCharactersFromTable() { charactersToCheck = []; const localeTable = document.getElementById('tablechars'); const cityTable = document.getElementById('tablepeople'); let targetTable = localeTable || cityTable; let nameCellIndex = localeTable ? 1 : (cityTable ? 0 : -1); if (!targetTable) { console.error("Nenhuma tabela de personagens encontrada."); return; } if(ENABLE_DETAILED_LOGGING) console.log(`Tabela encontrada: #${targetTable.id}`); const tbody = targetTable.querySelector('tbody'); if (!tbody) { console.error("TBODY não encontrado."); return; } const rows = tbody.querySelectorAll('tr'); rows.forEach(row => { const cells = row.querySelectorAll('td'); if (cells.length > nameCellIndex) { const nameCell = cells[nameCellIndex]; const nameLink = nameCell.querySelector('a'); if (nameLink) { const name = nameLink.textContent.trim(); const profileHref = nameLink.getAttribute('href'); const charIdMatch = profileHref.match(/Character\/(\d+)/); if (charIdMatch) { const id = charIdMatch[1]; const profileUrl = profileHref; // URL Relativa do Perfil const diaryUrl = `/World/Popmundo.aspx/Character/Diary/${id}`; // URL Relativa do Diário if (!charactersToCheck.some(c => c.id === id)) { charactersToCheck.push({ id: id, name: name, profileUrl: profileUrl, diaryUrl: diaryUrl }); } } else if (ENABLE_DETAILED_LOGGING) { console.warn(`Link encontrado para ${name}, mas não foi possível extrair o ID do personagem de: ${profileHref}`); } } } }); console.log(`Encontrados ${charactersToCheck.length} personagens na tabela para verificar.`); } // --- Verificação de Diário --- function fetchAndCheckDiaryReverse(character) { return new Promise((resolve) => { const fullDiaryUrl = window.location.origin + character.diaryUrl; if(ENABLE_DETAILED_LOGGING) console.log(`--- [${character.name}] Analisando Diário: ${fullDiaryUrl} ---`); GM_xmlhttpRequest({ method: "GET", url: fullDiaryUrl, timeout: 15000, onload: function(response) { let isCurrentlyConsideredSickState = false; if (response.status >= 200 && response.status < 300) { const parser = new DOMParser(); const diaryDoc = parser.parseFromString(response.responseText, "text/html"); const diaryContainer = diaryDoc.querySelector('#ppm-content'); if (diaryContainer) { const diaryEventEntries = Array.from(diaryContainer.querySelectorAll('ul > li > ul > li')); for (let index = diaryEventEntries.length - 1; index >= 0; index--) { // Verifica do mais recente para o mais antigo const item = diaryEventEntries[index]; let rawHtml = item.innerHTML; // Limpa o HTML e extrai o texto relevante rawHtml = rawHtml.replace(/^<span.*?<\/span>\s*/i, ''); // Remove span de tempo/ícone no início let textContentOnly = rawHtml.replace(/<[^>]*>/g, ' '); // Remove tags HTML textContentOnly = textContentOnly.replace(/^\s*\d{2}:\d{2}:\s*/, ''); // Remove hora se presente let textToSearch = textContentOnly.replace(/\s+/g, ' ').trim().toLowerCase(); // Normaliza espaços e caixa if (!textToSearch) continue; // Pula entradas vazias let illnessDetectedInThisItem = false; // Verifica se é uma mensagem de doença real for (const illness of realIllnessMessages) { if (textToSearch.includes(illness) && !textToSearch.includes(ignoredMessage)) { isCurrentlyConsideredSickState = true; // Encontrou doença, marca como doente illnessDetectedInThisItem = true; if (ENABLE_DETAILED_LOGGING) console.log(`[${character.name}] DOENTE detectado: "${textToSearch}" (Match: ${illness})`); break; // Para de procurar doenças nesta entrada } } // Se não encontrou doença nesta entrada, verifica se é uma mensagem de cura if (!illnessDetectedInThisItem && textToSearch.includes(cureMessage)) { isCurrentlyConsideredSickState = false; // Encontrou cura, marca como não doente if (ENABLE_DETAILED_LOGGING) console.log(`[${character.name}] CURADO detectado: "${textToSearch}"`); // IMPORTANTE: Não para aqui, pois pode haver uma mensagem de doença mais recente } // Se encontrou uma condição de doença ou cura, pode decidir parar (break) se a lógica for "a mais recente define tudo" // Se a lógica for "qualquer doença desde a última cura", continue verificando } } else { console.warn(`[${character.name}] #ppm-content não encontrado no diário.`); } } else { console.error(`[${character.name}] Erro ${response.status} ao buscar diário.`); } resolve({ character: character, isCurrentlySick: isCurrentlyConsideredSickState }); }, onerror: function(error) { console.error(`[${character.name}] Erro de rede ao buscar diário:`, error); resolve({ character: character, isCurrentlySick: false }); }, ontimeout: function() { console.error(`[${character.name}] Timeout ao buscar diário.`); resolve({ character: character, isCurrentlySick: false }); } }); }); } // --- Controle da Varredura --- async function startDiaryScan() { if (scanInProgress) return; scanInProgress = true; currentlySickCharacters = []; getCharactersFromTable(); const resultsDiv = document.getElementById(RESULTS_DIV_ID); const scanButton = document.getElementById(SCAN_BUTTON_ID); const progressList = document.getElementById(PROGRESS_LIST_ID); if (!resultsDiv || !scanButton || !progressList) { console.error("UI não encontrada."); scanInProgress = false; return; } if (charactersToCheck.length === 0) { updateProgressList("<p>Nenhum personagem encontrado na tabela para verificar.</p>"); resultsDiv.style.display = 'block'; scanInProgress = false; return; } scanButton.disabled = true; scanButton.value = 'Verificando... (0%)'; resultsDiv.style.display = 'block'; progressList.innerHTML = `<p>Iniciando verificação... Total: ${charactersToCheck.length}.</p><p class="small italic">(Atraso: ${REQUEST_DELAY_MS / 1000}s/diário)</p>`; if (ENABLE_DETAILED_LOGGING) { progressList.innerHTML += `<p class="small bold">VERIFIQUE O CONSOLE (F12) PARA LOGS DETALHADOS!</p>`; } for (let i = 0; i < charactersToCheck.length; i++) { const character = charactersToCheck[i]; const progressPercent = Math.round(((i + 1) / charactersToCheck.length) * 100); scanButton.value = `Verificando... (${progressPercent}%)`; updateProgressList(`(${i + 1}/${charactersToCheck.length}) ${character.name}... `, 'progress-item'); try { const scanResult = await fetchAndCheckDiaryReverse(character); const lastProgressItem = progressList.querySelector('.progress-item:last-child'); if (scanResult.isCurrentlySick) { // Adiciona à lista de doentes currentlySickCharacters.push({ id: character.id, name: character.name, profileUrl: character.profileUrl, // URL relativa do perfil diaryUrl: window.location.origin + character.diaryUrl // URL completa do diário }); if(lastProgressItem) lastProgressItem.innerHTML += `<span class="sick-found"> DOENTE!</span>`; } else { if(lastProgressItem) lastProgressItem.innerHTML += `<span class="ok-status"> OK.</span>`; } } catch (error) { const lastProgressItem = progressList.querySelector('.progress-item:last-child'); if(lastProgressItem) lastProgressItem.innerHTML += `<span class="error-status"> ERRO!</span>`; console.error(`Erro ao processar ${character.name} durante scan:`, error); } if (i < charactersToCheck.length - 1) { await sleep(REQUEST_DELAY_MS); } } // --- Atualização Final da Varredura --- let finalHtml = `<h3>Verificação Concluída!</h3>`; finalHtml += `<p><strong>${isCityPage ? 'Cidade' : 'Contexto'}:</strong> ${contextName}</p>`; if (currentlySickCharacters.length > 0) { finalHtml += `<p class="sick-summary"><strong>Personagens DOENTES encontrados (${currentlySickCharacters.length}):</strong></p><ul id="${SCRIPT_ID_PREFIX}-sick-list">`; currentlySickCharacters.forEach(char => { // Constrói URL completa do perfil para o link const fullProfileUrl = window.location.origin + char.profileUrl; // Lista sem o span de status de cura finalHtml += `<li id="${SCRIPT_ID_PREFIX}-sick-${char.id}"><a href="${fullProfileUrl}" target="_blank">${char.name}</a> (<a href="${char.diaryUrl}" target="_blank">Ver Diário</a>)</li>`; }); finalHtml += `</ul>`; } else { finalHtml += `<p class="ok-summary">Nenhum personagem atualmente doente foi encontrado.</p>`; } progressList.innerHTML = finalHtml; scanButton.disabled = false; scanButton.value = 'Iniciar Nova Varredura'; scanInProgress = false; console.log("Verificação de diários concluída."); } // --- Função Helper para UI --- function updateProgressList(message, className = '') { const progressList = document.getElementById(PROGRESS_LIST_ID); if (progressList) { const p = document.createElement('p'); if (className) p.className = className; p.innerHTML = message; // Use innerHTML para permitir spans de formatação progressList.appendChild(p); progressList.scrollTop = progressList.scrollHeight; // Auto-scroll } else { console.log("Fallback Log:", message); // Log se a UI não for encontrada } } // --- Criação da UI --- function createUI() { const oldContainer = document.getElementById(CONTAINER_ID); if (oldContainer) oldContainer.remove(); // Remove UI antiga se existir const containerDiv = document.createElement('div'); containerDiv.className = 'box'; containerDiv.id = CONTAINER_ID; const title = document.createElement('h2'); title.textContent = 'Detector de Doentes (v3.9.2)'; // Título atualizado containerDiv.appendChild(title); const buttonContainer = document.createElement('p'); buttonContainer.className = 'actionbuttons tmargin10'; // Apenas o botão de Scan const scanButton = document.createElement('input'); scanButton.setAttribute('type', 'button'); scanButton.id = SCAN_BUTTON_ID; scanButton.value = 'Iniciar Varredura'; scanButton.addEventListener('click', startDiaryScan); buttonContainer.appendChild(scanButton); containerDiv.appendChild(buttonContainer); const resultsDiv = document.createElement('div'); resultsDiv.id = RESULTS_DIV_ID; resultsDiv.style.marginTop = '10px'; resultsDiv.style.maxHeight = '350px'; resultsDiv.style.overflowY = 'auto'; resultsDiv.style.display = 'none'; // Começa escondido const progressListDiv = document.createElement('div'); progressListDiv.id = PROGRESS_LIST_ID; resultsDiv.appendChild(progressListDiv); containerDiv.appendChild(resultsDiv); // Insere a UI na página const targetTable = document.getElementById('tablechars') || document.getElementById('tablepeople'); if (targetTable) { targetTable.parentNode.insertBefore(containerDiv, targetTable); console.log(`UI Detector v3.9.2 (Detect Only) inserida antes da tabela #${targetTable.id}.`); } else { // Fallback para inserir a UI se a tabela não for encontrada const contentDiv = document.getElementById('ppm-content'); const firstHeading = contentDiv ? contentDiv.querySelector('h1') : null; if (firstHeading) { firstHeading.parentNode.insertBefore(containerDiv, firstHeading.nextSibling); console.log("UI Detector v3.9.2 (Detect Only) inserida após H1."); } else if (contentDiv) { contentDiv.insertBefore(containerDiv, contentDiv.firstChild); console.log("UI Detector v3.9.2 (Detect Only) inserida no início de #ppm-content."); } else { document.body.insertBefore(containerDiv, document.body.firstChild); console.error("Local ideal para UI Detector v3.9.2 não encontrado."); } } } // --- Estilos CSS (removidos os de cura) --- GM_addStyle(` #${CONTAINER_ID} { margin-bottom: 15px; } #${SCAN_BUTTON_ID} { padding: 4px 10px; font-size: 1em; font-weight: bold; cursor: pointer; border: 1px solid #a0a0a0; background-color: #e0e0e0; color: #333; border-radius: 3px; margin: 0; vertical-align: middle; } #${SCAN_BUTTON_ID}:hover:not(:disabled) { background-color: #d0d0d0; border-color: #888888; } #${SCAN_BUTTON_ID}:disabled { background-color: #f0f0f0; border-color: #c0c0c0; color: #999; cursor: not-allowed; } #${RESULTS_DIV_ID} { border-top: 1px solid #ccc; padding-top: 10px; font-size: 0.9em; } #${PROGRESS_LIST_ID} p { margin: 3px 0; line-height: 1.4; } #${PROGRESS_LIST_ID} p.small { font-size: 0.9em; } #${PROGRESS_LIST_ID} p.italic { font-style: italic; color: #666; } #${PROGRESS_LIST_ID} p.bold { font-weight: bold; } #${PROGRESS_LIST_ID} .sick-found, #${RESULTS_DIV_ID} .sick-summary { color: red; font-weight: bold; } #${PROGRESS_LIST_ID} .ok-status, #${RESULTS_DIV_ID} .ok-summary { color: green; } #${PROGRESS_LIST_ID} .error-status { color: orange; font-weight: bold; } #${RESULTS_DIV_ID} ul { list-style: disc; margin-left: 25px; margin-top: 5px; margin-bottom: 5px; } #${RESULTS_DIV_ID} li { margin-bottom: 4px; } #${RESULTS_DIV_ID} a {} /* Estilo padrão para links */ #${RESULTS_DIV_ID} h3 { margin-bottom: 8px; } `); // --- Execução Principal --- setTimeout(() => { try { extractContextInfo(); createUI(); } catch (e) { console.error("Erro ao inicializar o script Detector de Doentes:", e); } }, 800); // Atraso para garantir que a página carregou })();