Greasy Fork

Greasy Fork is available in English.

Bulk Offer & Accept Helper

Oferta itens por nome/quantidade/preço e aceita ofertas/presentes/compras em massa por nome e preço máximo.

当前为 2025-08-04 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Bulk Offer & Accept Helper
// @namespace    http://tampermonkey.net/
// @version      3.0
// @description  Oferta itens por nome/quantidade/preço e aceita ofertas/presentes/compras em massa por nome e preço máximo.
// @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.0) ---
    // =================================================================================
    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';

        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);
            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);
            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 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 - CORRIGIDO PARA VALORES BRASILEIROS
                let itemPrice = 0;
                let priceText = "Presente";
                const costMatch = paragraph.textContent.match(/custo:\s*([\d.,]+)\s*M\$/);
                if (costMatch && costMatch[1]) {
                    // Usar função específica para converter valores brasileiros
                    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) {
                    statusDiv.textContent = `Aceitando '${itemName}' (${priceText})...`;
                    foundItemToAccept = true;

                    // Encontrar o botão de aceitar neste parágrafo
                    const acceptButton = paragraph.querySelector('input[value="Comprar e pagar pela entrega"]');

                    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. Concluído!";
                await stopBulkAccept();
            }
        }

        async function checkAcceptStateOnLoad() {
            createAcceptUI();
            if (await GM_getValue(STORAGE_KEY_RUNNING_ACCEPT, false)) {
                disableAcceptButtons(true);
                document.getElementById('bulkAcceptStatusScript').textContent = "Status: Recarregado, buscando próxima oferta para aceitar...";
                await new Promise(resolve => setTimeout(resolve, 500));
                await processNextAccept();
            } else {
                disableAcceptButtons(false);
            }
        }

        checkAcceptStateOnLoad();
    }

    // Inicia o script
    initializeScript();
})();