// ==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
})();