Greasy Fork is available in English.
Oferta itens por nome/quantidade/preço e aceita ofertas/presentes/compras em massa por nome e preço máximo (apenas valores inteiros).
当前为
// ==UserScript==
// @name Bulk Offer & Accept Helper
// @namespace http://tampermonkey.net/
// @version 3.1
// @description Oferta itens por nome/quantidade/preço e aceita ofertas/presentes/compras em massa por nome e preço máximo (apenas valores inteiros).
// @author Popper
// @match *://*.popmundo.com/World/Popmundo.aspx/Character/OfferItem/*
// @match *://*.popmundo.com/World/Popmundo.aspx/Character/ItemsOffered
// @grant GM_addStyle
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_deleteValue
// @run-at document-idle
// ==/UserScript==
(function() {
'use strict';
// =================================================================================
// --- ROTEADOR PRINCIPAL ---
// =================================================================================
function initializeScript() {
const currentUrl = window.location.href;
if (currentUrl.includes('/Character/OfferItem/')) {
console.log("Bulk Helper: Página de OFERTAR item detectada.");
setupOfferPage();
} else if (currentUrl.includes('/Character/ItemsOffered')) {
console.log("Bulk Helper: Página de ACEITAR ofertas detectada.");
setupAcceptPage();
}
}
// =================================================================================
// --- FUNCIONALIDADE 1: OFERTAR ITENS EM MASSA (CÓDIGO COMPLETO) ---
// =================================================================================
function setupOfferPage() {
const ITEM_DROPDOWN_SELECTOR = '#ctl00_cphLeftColumn_ctl00_ddlItem';
const OFFER_BUTTON_SELECTOR = '#ctl00_cphLeftColumn_ctl00_btnGive';
const PRICE_INPUT_SELECTOR = '#ctl00_cphLeftColumn_ctl00_txtPriceTag';
const BASE_DELAY_MS = 2000;
const POST_PRICE_SET_DELAY_MS = 100;
const STORAGE_KEY_ITEMS_OFFER = 'popmundo_offerItem_items_swqp';
const STORAGE_KEY_RUNNING_OFFER = 'popmundo_offerItem_running_swqp';
const STORAGE_KEY_PRICE_OFFER = 'popmundo_offerItem_targetPrice';
let itemDropdown = document.querySelector(ITEM_DROPDOWN_SELECTOR);
if (!itemDropdown) { console.warn("Bulk Offer Helper: Dropdown de itens não encontrado."); return; }
function createOfferUI() {
if (document.getElementById('bulkOfferUIScript')) return;
const scriptUIArea = document.createElement('div');
scriptUIArea.id = 'bulkOfferUIScript';
scriptUIArea.style.border = '1px solid #ccc';
scriptUIArea.style.padding = '15px';
scriptUIArea.style.margin = '20px 0 15px 0';
scriptUIArea.style.backgroundColor = '#f9f9f9';
scriptUIArea.innerHTML = `
<h3 style="margin-top:0;">Bulk Offer Helper</h3>
<div style="display: flex; flex-wrap: wrap; gap: 15px; margin-bottom: 10px;">
<div>
<label for="itemNameInputScript">Início do Nome:</label><br>
<input type="text" id="itemNameInputScript" style="width: 250px; padding: 5px;" placeholder="Ex: Analgésicos">
</div>
<div>
<label for="itemQuantityInputScript">Quantidade:</label><br>
<input type="number" id="itemQuantityInputScript" min="1" value="1" style="width: 70px; padding: 5px;">
</div>
<div>
<label for="itemPriceInputScript">Preço (M$):</label><br>
<input type="number" id="itemPriceInputScript" min="0" step="1" value="0" style="width: 90px; padding: 5px;">
</div>
</div>
<div style="margin-top: 15px;">
<button id="startOfferByNameQtyPriceBtnScript" type="button" style="background-color: #4CAF50; color: white; padding: 8px 12px; border: none; cursor: pointer;">Ofertar</button>
<button id="stopBulkOfferBtnScript" type="button" style="margin-left: 10px; background-color: #f44336; color: white; padding: 8px 12px; border: none; cursor: pointer;">Parar Oferta</button>
</div>
<div id="bulkOfferStatusScript" style="margin-top: 15px; font-weight: bold;">Status: Pronto.</div>
`;
itemDropdown.parentNode.insertBefore(scriptUIArea, itemDropdown);
document.getElementById('startOfferByNameQtyPriceBtnScript').addEventListener('click', startOfferByNameQuantityPrice);
document.getElementById('stopBulkOfferBtnScript').addEventListener('click', stopOffer);
GM_addStyle(`
#itemNameInputScript, #itemQuantityInputScript, #itemPriceInputScript { border: 1px solid #ccc; border-radius: 3px; box-sizing: border-box; }
#startOfferByNameQtyPriceBtnScript:disabled, #stopBulkOfferBtnScript:disabled { cursor: not-allowed; opacity: 0.6; }
`);
}
async function startOfferByNameQuantityPrice() {
const itemNameInput = document.getElementById('itemNameInputScript');
const quantityInput = document.getElementById('itemQuantityInputScript');
const priceInput = document.getElementById('itemPriceInputScript');
const statusDiv = document.getElementById('bulkOfferStatusScript');
const inputText = itemNameInput.value.trim();
const requestedQuantity = parseInt(quantityInput.value, 10);
const requestedPrice = parseInt(priceInput.value, 10);
if (!inputText) { statusDiv.textContent = "Erro: Digite o início do nome do item."; return; }
if (isNaN(requestedQuantity) || requestedQuantity < 1) { statusDiv.textContent = "Erro: Quantidade inválida."; return; }
if (isNaN(requestedPrice) || requestedPrice < 0) { statusDiv.textContent = "Erro: Preço inválido."; return; }
const allItemsFound = Array.from(document.querySelector(ITEM_DROPDOWN_SELECTOR).options)
.filter(option => option.value && option.value !== "-1" && option.textContent.trim().toLowerCase().startsWith(inputText.toLowerCase()))
.map(option => ({ value: option.value, text: option.textContent.trim() }));
if (allItemsFound.length === 0) {
statusDiv.textContent = `Status: Nenhum item encontrado começando com "${inputText}".`;
return;
}
const itemsToOfferThisRun = allItemsFound.slice(0, requestedQuantity);
statusDiv.textContent = `Encontrado(s) ${allItemsFound.length}. Ofertando ${itemsToOfferThisRun.length} por ${requestedPrice} M$...`;
await GM_setValue(STORAGE_KEY_PRICE_OFFER, requestedPrice);
await GM_setValue(STORAGE_KEY_ITEMS_OFFER, JSON.stringify(itemsToOfferThisRun));
await GM_setValue(STORAGE_KEY_RUNNING_OFFER, true);
disableOfferButtons(true);
await processNextOffer();
}
async function stopOffer() {
await GM_deleteValue(STORAGE_KEY_ITEMS_OFFER);
await GM_deleteValue(STORAGE_KEY_RUNNING_OFFER);
await GM_deleteValue(STORAGE_KEY_PRICE_OFFER);
const statusDiv = document.getElementById('bulkOfferStatusScript');
if (statusDiv) statusDiv.textContent = "Status: Oferta interrompida pelo usuário.";
disableOfferButtons(false);
}
function disableOfferButtons(disabled) {
document.getElementById('startOfferByNameQtyPriceBtnScript').disabled = disabled;
document.getElementById('stopBulkOfferBtnScript').disabled = !disabled;
document.getElementById('itemNameInputScript').disabled = disabled;
document.getElementById('itemQuantityInputScript').disabled = disabled;
document.getElementById('itemPriceInputScript').disabled = disabled;
}
async function processNextOffer() {
const isRunning = await GM_getValue(STORAGE_KEY_RUNNING_OFFER, false);
if (!isRunning) { disableOfferButtons(false); return; }
let itemsToOffer = JSON.parse(await GM_getValue(STORAGE_KEY_ITEMS_OFFER, '[]'));
const statusDiv = document.getElementById('bulkOfferStatusScript');
if (itemsToOffer.length === 0) {
statusDiv.textContent = "Status: Todas as ofertas foram concluídas!";
await stopOffer();
return;
}
const itemDropdown = document.querySelector(ITEM_DROPDOWN_SELECTOR);
const offerButton = document.querySelector(OFFER_BUTTON_SELECTOR);
const pagePriceInput = document.querySelector(PRICE_INPUT_SELECTOR);
if (!itemDropdown || !offerButton) {
statusDiv.textContent = "Erro Crítico: Elementos da página desapareceram.";
await stopOffer(); return;
}
const itemToOffer = itemsToOffer.shift();
const targetPrice = await GM_getValue(STORAGE_KEY_PRICE_OFFER, 0);
if (pagePriceInput) pagePriceInput.value = String(targetPrice);
await new Promise(resolve => setTimeout(resolve, POST_PRICE_SET_DELAY_MS));
const initialTotalCount = JSON.parse(await GM_getValue(STORAGE_KEY_ITEMS_OFFER, '[]')).length + itemsToOffer.length + 1;
statusDiv.textContent = `Ofertando ${initialTotalCount - itemsToOffer.length}/${initialTotalCount}: '${itemToOffer.text}'...`;
itemDropdown.value = itemToOffer.value;
if (itemDropdown.value !== itemToOffer.value) {
statusDiv.textContent = `Erro: Falha ao selecionar '${itemToOffer.text}'. Pulando...`;
await GM_setValue(STORAGE_KEY_ITEMS_OFFER, JSON.stringify(itemsToOffer));
setTimeout(processNextOffer, BASE_DELAY_MS / 2); return;
}
await GM_setValue(STORAGE_KEY_ITEMS_OFFER, JSON.stringify(itemsToOffer));
await new Promise(resolve => setTimeout(resolve, BASE_DELAY_MS));
if (!await GM_getValue(STORAGE_KEY_RUNNING_OFFER, false)) { disableOfferButtons(false); return; }
offerButton.click();
}
async function checkOfferStateOnLoad() {
createOfferUI();
const isRunning = await GM_getValue(STORAGE_KEY_RUNNING_OFFER, false);
if (isRunning) {
disableOfferButtons(true);
document.getElementById('bulkOfferStatusScript').textContent = "Status: Recarregado, continuando oferta...";
await new Promise(resolve => setTimeout(resolve, 500));
await processNextOffer();
} else {
disableOfferButtons(false);
}
}
checkOfferStateOnLoad();
}
// =================================================================================
// --- FUNCIONALIDADE 2: ACEITAR OFERTAS EM MASSA (CÓDIGO CORRIGIDO v3.1) ---
// =================================================================================
function setupAcceptPage() {
const BASE_DELAY_MS = 2000;
const STORAGE_KEY_RUNNING_ACCEPT = 'popmundo_acceptItem_running';
const STORAGE_KEY_MAX_PRICE_ACCEPT = 'popmundo_acceptItem_maxPrice';
const STORAGE_KEY_ITEM_NAME_ACCEPT = 'popmundo_acceptItem_itemName';
const STORAGE_KEY_ACCEPTED_COUNT = 'popmundo_acceptItem_accepted_count';
function createAcceptUI() {
if (document.getElementById('bulkAcceptUIScript')) return;
// Localizar a seção de ofertas
const offersSection = Array.from(document.querySelectorAll('.box'))
.find(box => box.textContent.includes('Itens sendo ofertados a você'));
if (!offersSection) {
console.log("Bulk Accept Helper: Seção 'Itens sendo ofertados a você' não encontrada.");
return;
}
const scriptUIArea = document.createElement('div');
scriptUIArea.id = 'bulkAcceptUIScript';
scriptUIArea.style.border = '1px solid #ccc';
scriptUIArea.style.padding = '15px';
scriptUIArea.style.margin = '10px 0';
scriptUIArea.style.backgroundColor = '#f9f9f9';
scriptUIArea.innerHTML = `
<h3 style="margin-top:0;">Bulk Accept Helper</h3>
<div style="display: grid; grid-template-columns: 1fr; gap: 10px;">
<div>
<label for="itemNameInputAcceptScript">Nome do Item (ou parte dele):</label><br>
<input type="text" id="itemNameInputAcceptScript" style="width: 250px; padding: 5px;" placeholder="Deixe em branco para qualquer nome">
</div>
<div>
<label for="maxPriceInputScript">Aceitar com preço MÁXIMO de (M$):</label><br>
<input type="number" id="maxPriceInputScript" min="0" step="1" value="100" style="width: 120px; padding: 5px;">
</div>
</div>
<div style="margin-top: 15px;">
<button id="startAcceptBtnScript" type="button" style="background-color: #4CAF50; color: white; padding: 8px 12px; border: none; cursor: pointer;">Aceitar em Massa</button>
<button id="stopAcceptBtnScript" type="button" style="margin-left: 10px; background-color: #f44336; color: white; padding: 8px 12px; border: none; cursor: pointer;">Parar</button>
</div>
<div id="bulkAcceptStatusScript" style="margin-top: 15px; font-weight: bold;">Status: Pronto.</div>
`;
// Inserir a UI no início da seção de ofertas
offersSection.insertBefore(scriptUIArea, offersSection.firstChild);
document.getElementById('startAcceptBtnScript').addEventListener('click', startBulkAccept);
document.getElementById('stopAcceptBtnScript').addEventListener('click', stopBulkAccept);
GM_addStyle(`
#maxPriceInputScript, #itemNameInputAcceptScript { border: 1px solid #ccc; border-radius: 3px; box-sizing: border-box; }
#startAcceptBtnScript:disabled, #stopAcceptBtnScript:disabled { cursor: not-allowed; opacity: 0.6; }
`);
}
async function startBulkAccept() {
const priceInput = document.getElementById('maxPriceInputScript');
const nameInput = document.getElementById('itemNameInputAcceptScript');
const statusDiv = document.getElementById('bulkAcceptStatusScript');
const maxPrice = parseInt(priceInput.value.replace(/[.,]/g, ''), 10);
const targetName = nameInput.value.trim().toLowerCase();
if (isNaN(maxPrice) || maxPrice < 0) {
statusDiv.textContent = "Erro: Preço máximo inválido.";
priceInput.focus();
return;
}
statusDiv.textContent = `Iniciando... Buscando ofertas para "${targetName || 'qualquer item'}" com preço até ${maxPrice} M$.`;
await GM_setValue(STORAGE_KEY_MAX_PRICE_ACCEPT, maxPrice);
await GM_setValue(STORAGE_KEY_ITEM_NAME_ACCEPT, targetName);
await GM_setValue(STORAGE_KEY_RUNNING_ACCEPT, true);
await GM_setValue(STORAGE_KEY_ACCEPTED_COUNT, 0);
disableAcceptButtons(true);
await processNextAccept();
}
async function stopBulkAccept() {
await GM_deleteValue(STORAGE_KEY_RUNNING_ACCEPT);
await GM_deleteValue(STORAGE_KEY_MAX_PRICE_ACCEPT);
await GM_deleteValue(STORAGE_KEY_ITEM_NAME_ACCEPT);
await GM_deleteValue(STORAGE_KEY_ACCEPTED_COUNT);
const statusDiv = document.getElementById('bulkAcceptStatusScript');
if (statusDiv) statusDiv.textContent = "Status: Processo interrompido pelo usuário.";
disableAcceptButtons(false);
}
function disableAcceptButtons(disabled) {
document.getElementById('startAcceptBtnScript').disabled = disabled;
document.getElementById('stopAcceptBtnScript').disabled = !disabled;
document.getElementById('maxPriceInputScript').disabled = disabled;
document.getElementById('itemNameInputAcceptScript').disabled = disabled;
}
// Função para converter valor monetário brasileiro para inteiro
function parseBrazilianCurrency(valueStr) {
// Remover símbolos e espaços
let cleanStr = valueStr.replace(/[^\d.,]/g, '');
// Separar parte inteira e decimal
let parts = cleanStr.split(',');
let integerPart = parts[0].replace(/\./g, ''); // Remove pontos de milhar
// Converter para inteiro
return parseInt(integerPart, 10) || 0;
}
async function processNextAccept() {
if (!await GM_getValue(STORAGE_KEY_RUNNING_ACCEPT, false)) {
disableAcceptButtons(false);
return;
}
const maxPrice = await GM_getValue(STORAGE_KEY_MAX_PRICE_ACCEPT, -1);
const targetName = await GM_getValue(STORAGE_KEY_ITEM_NAME_ACCEPT, '');
const acceptedCount = await GM_getValue(STORAGE_KEY_ACCEPTED_COUNT, 0);
const statusDiv = document.getElementById('bulkAcceptStatusScript');
// Localizar a seção de ofertas
const offersSection = Array.from(document.querySelectorAll('.box'))
.find(box => box.textContent.includes('Itens sendo ofertados a você'));
if (!offersSection) {
statusDiv.textContent = "Status: Seção de ofertas não encontrada. Concluindo.";
await stopBulkAccept();
return;
}
// Encontrar todos os parágrafos que representam ofertas
const offerParagraphs = Array.from(offersSection.querySelectorAll('p.nobmargin'))
.filter(p => p.textContent.includes('Oferecido por') && p.textContent.includes('custo:'));
if (offerParagraphs.length === 0) {
statusDiv.textContent = "Status: Nenhuma oferta encontrada para processar. Concluindo!";
await stopBulkAccept();
return;
}
let foundItemToAccept = false;
for (const paragraph of offerParagraphs) {
// Extrair nome do item
const itemLink = paragraph.querySelector('a[id*="lnkItem"]');
const itemName = itemLink ? itemLink.textContent.trim() : '';
if (!itemName) continue;
// Extrair preço
let itemPrice = 0;
let priceText = "Presente";
const costMatch = paragraph.textContent.match(/custo:\s*([\d.,]+)\s*M\$/);
if (costMatch && costMatch[1]) {
itemPrice = parseBrazilianCurrency(costMatch[1]);
priceText = `${itemPrice} M$`;
}
console.log(`Verificando: Nome='${itemName}', Preço=${itemPrice} M$. Alvo: Nome='${targetName}', Preço <= ${maxPrice} M$.`);
const isNameOk = (targetName === '' || itemName.toLowerCase().includes(targetName));
const isPriceOk = (itemPrice <= maxPrice);
if (isNameOk && isPriceOk) {
const newCount = acceptedCount + 1;
await GM_setValue(STORAGE_KEY_ACCEPTED_COUNT, newCount);
statusDiv.textContent = `Aceitando #${newCount}: '${itemName}' (${priceText})...`;
foundItemToAccept = true;
// Encontrar o botão de aceitar neste parágrafo
// Tenta diferentes seletores para o botão de aceitar
let acceptButton = paragraph.querySelector('input[value="Comprar e pagar pela entrega"]');
if (!acceptButton) {
acceptButton = paragraph.querySelector('input[id*="btnAccept"]');
}
if (!acceptButton) {
acceptButton = paragraph.querySelector('input[name*="btnAccept"]');
}
if (!acceptButton) {
acceptButton = paragraph.querySelector('input[type="submit"]:not([value="Rejeitar"])');
}
if (!acceptButton) {
console.log("Botão de aceitar não encontrado para este item");
continue;
}
await new Promise(resolve => setTimeout(resolve, BASE_DELAY_MS));
if (!await GM_getValue(STORAGE_KEY_RUNNING_ACCEPT, false)) {
console.log("Processo interrompido durante o delay final.");
disableAcceptButtons(false);
return;
}
console.log(`Clicando no botão: "${acceptButton.value}"`);
acceptButton.click();
break;
}
}
if (!foundItemToAccept) {
statusDiv.textContent = `Status: Nenhuma outra oferta corresponde aos critérios. Total aceito: ${acceptedCount}. Concluído!`;
await stopBulkAccept();
}
}
async function checkAcceptStateOnLoad() {
createAcceptUI();
if (await GM_getValue(STORAGE_KEY_RUNNING_ACCEPT, false)) {
disableAcceptButtons(true);
const acceptedCount = await GM_getValue(STORAGE_KEY_ACCEPTED_COUNT, 0);
document.getElementById('bulkAcceptStatusScript').textContent = `Status: Recarregado, continuando aceitação... Total aceito: ${acceptedCount}`;
await new Promise(resolve => setTimeout(resolve, 500));
await processNextAccept();
} else {
disableAcceptButtons(false);
}
}
checkAcceptStateOnLoad();
}
// Inicia o script
initializeScript();
})();