Greasy Fork

Greasy Fork is available in English.

Bulk Offer Helper

Oferta itens por nome/quantidade/preço para o personagem da página atual.

当前为 2025-05-02 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Bulk Offer Helper
// @namespace    http://tampermonkey.net/
// @version      1.2
// @description  Oferta itens por nome/quantidade/preço para o personagem da página atual.
// @author       Popper para Roque
// @match        *://*.popmundo.com/World/Popmundo.aspx/Character/OfferItem/*
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @run-at       document-idle
// ==/UserScript==

(function() {
    'use strict';

    // --- CONFIGURAÇÃO ---
    const ITEM_DROPDOWN_SELECTOR = '#ctl00_cphLeftColumn_ctl00_ddlItem';
    const OFFER_BUTTON_SELECTOR = '#ctl00_cphLeftColumn_ctl00_btnGive';
    const PRICE_INPUT_SELECTOR = '#ctl00_cphLeftColumn_ctl00_txtPriceTag'; // Seletor do campo de preço ORIGINAL da página
    const BASE_DELAY_MS = 2000; // Atraso principal entre ofertas
    const POST_PRICE_SET_DELAY_MS = 100; // Pequeno atraso após definir o preço (ms)
    const STORAGE_KEY_ITEMS = 'popmundo_offerItem_items_swqp';
    const STORAGE_KEY_RUNNING = 'popmundo_offerItem_running_swqp';
    // --- FIM DA CONFIGURAÇÃO ---

    let itemDropdown = document.querySelector(ITEM_DROPDOWN_SELECTOR);
    let offerButton = document.querySelector(OFFER_BUTTON_SELECTOR);
    let pagePriceInput = document.querySelector(PRICE_INPUT_SELECTOR);

    if (!itemDropdown) { console.warn("Script Popmundo AdvOffer: Dropdown não encontrado:", ITEM_DROPDOWN_SELECTOR); return; }
    if (!offerButton) { console.warn("Script Popmundo AdvOffer: Botão Ofertar não encontrado:", OFFER_BUTTON_SELECTOR); }
    if (!pagePriceInput) { console.warn("Script Popmundo AdvOffer: Campo de Preço da página não encontrado:", PRICE_INPUT_SELECTOR, "- O preço não será definido."); }

    function createUI() {
        if (document.getElementById('bulkOfferUIScript')) return;
        const scriptUIArea = document.createElement('div');
        scriptUIArea.id = 'bulkOfferUIScript';
        scriptUIArea.innerHTML = `
            <div style="border: 1px solid #ccc; padding: 15px; margin: 20px 0; background-color: #f9f9f9;">
                <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" 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;">Ofertar</button>
                    <button id="stopBulkOfferBtnScript" type="button" style="margin-left: 10px; background-color: #f44336; color: white; padding: 8px 12px;">Parar Oferta</button>
                </div>
                <div id="bulkOfferStatusScript" style="margin-top: 15px; font-weight: bold;">Status: Pronto.</div>
            </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; }`);
    }

    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);

        itemDropdown = document.querySelector(ITEM_DROPDOWN_SELECTOR);
        offerButton = document.querySelector(OFFER_BUTTON_SELECTOR);
        pagePriceInput = document.querySelector(PRICE_INPUT_SELECTOR);

        if (!itemDropdown || !offerButton) { statusDiv.textContent = "Erro Crítico: Elementos não encontrados."; console.error("StartOfferSWQP: Elementos não encontrados."); return; }
        if (!inputText) { statusDiv.textContent = "Erro: Digite o início do nome."; itemNameInput.focus(); return; }
        if (isNaN(requestedQuantity) || requestedQuantity < 1) { statusDiv.textContent = "Erro: Quantidade inválida."; quantityInput.focus(); return; }
        if (isNaN(requestedPrice) || requestedPrice < 0) { statusDiv.textContent = "Erro: Preço inválido."; priceInput.focus(); return; }
        if (!pagePriceInput && requestedPrice > 0) { statusDiv.textContent = "Aviso: Campo de preço da página não encontrado."; console.warn("Campo de preço da página não encontrado."); }

        const allItemsFound = [];
        const inputTextLower = inputText.toLowerCase();
        console.log(`Buscando itens começando com "${inputText}" para ofertar ${requestedQuantity} por ${requestedPrice} M$ cada.`);

        for (let option of itemDropdown.options) {
            if (option.value && option.value !== "-1" && option.textContent.trim().toLowerCase().startsWith(inputTextLower)) {
                allItemsFound.push({ value: option.value, text: option.textContent.trim() });
            }
        }

        if (allItemsFound.length === 0) { statusDiv.textContent = `Status: Nenhum item encontrado começando com "${inputText}".`; console.log("Nenhum item correspondente."); return; }

        const actualQuantityToTransfer = Math.min(allItemsFound.length, requestedQuantity);
        const itemsToOfferThisRun = allItemsFound.slice(0, actualQuantityToTransfer);

        let startMessage = `Encontrado(s) ${allItemsFound.length}. `;
        startMessage += `Ofertando ${itemsToOfferThisRun.length} por ${requestedPrice} M$...`;
        statusDiv.textContent = startMessage;
        console.log(`Itens para esta execução (${itemsToOfferThisRun.length}):`, itemsToOfferThisRun);

        // Salvar também o preço alvo para que seja consistente entre reloads
        await GM_setValue('popmundo_offerItem_targetPrice', requestedPrice);
        await GM_setValue(STORAGE_KEY_ITEMS, JSON.stringify(itemsToOfferThisRun));
        await GM_setValue(STORAGE_KEY_RUNNING, true);
        disableButtons(true);
        await processNextOffer();
    }

    async function stopOffer() {
        console.log("StopOffer (SWQP) chamada!");
        const statusDiv = document.getElementById('bulkOfferStatusScript');
        await GM_deleteValue(STORAGE_KEY_ITEMS);
        await GM_deleteValue(STORAGE_KEY_RUNNING);
        await GM_deleteValue('popmundo_offerItem_targetPrice'); // Limpa preço alvo também
        if (statusDiv) { statusDiv.textContent = "Status: Oferta interrompida pelo usuário."; }
        console.log("Oferta em massa (SWQP) interrompida. Flags, lista e preço limpos.");
        disableButtons(false);
    }

    function disableButtons(disabled) {
         const startBtn = document.getElementById('startOfferByNameQtyPriceBtnScript');
         const stopBtn = document.getElementById('stopBulkOfferBtnScript');
         const nameInput = document.getElementById('itemNameInputScript');
         const quantityInput = document.getElementById('itemQuantityInputScript');
         const priceInput = document.getElementById('itemPriceInputScript');
         if(startBtn) startBtn.disabled = disabled;
         if(stopBtn) stopBtn.disabled = !disabled;
         if(nameInput) nameInput.disabled = disabled;
         if(quantityInput) quantityInput.disabled = disabled;
         if(priceInput) priceInput.disabled = disabled;
    }

    async function processNextOffer() {
        const isRunning = await GM_getValue(STORAGE_KEY_RUNNING, false);
        if (!isRunning) { /* ... código de parada ... */ return; }

        const itemsJson = await GM_getValue(STORAGE_KEY_ITEMS, '[]');
        let itemsToOffer = JSON.parse(itemsJson);
        const statusDiv = document.getElementById('bulkOfferStatusScript');
        const targetPrice = await GM_getValue('popmundo_offerItem_targetPrice', 0); // Pega preço alvo do storage

        if (itemsToOffer.length === 0) { /* ... código de conclusão/parada ... */ await stopOffer(); return; }
        if (!statusDiv) { console.error("Status UI não encontrado."); await stopOffer(); return; }

        itemDropdown = document.querySelector(ITEM_DROPDOWN_SELECTOR);
        offerButton = document.querySelector(OFFER_BUTTON_SELECTOR);
        pagePriceInput = document.querySelector(PRICE_INPUT_SELECTOR); // Re-seleciona

        if (!itemDropdown || !offerButton) { /* ... código de erro ... */ await stopOffer(); return; }

        const itemToOffer = itemsToOffer.shift();

        // *** LÓGICA DE PREÇO ATUALIZADA ***
        if (pagePriceInput) {
            const targetPriceString = String(targetPrice); // Garante que é string para comparação e atribuição
            if (pagePriceInput.value !== targetPriceString) {
                 console.log(`(SWQP) Definindo preço na página para: ${targetPriceString}`);
                 pagePriceInput.value = targetPriceString;
                 // Disparar eventos para tentar simular interação do usuário
                 console.log("(SWQP) Disparando eventos 'input' e 'change' no campo de preço.");
                 pagePriceInput.dispatchEvent(new Event('input', { bubbles: true }));
                 pagePriceInput.dispatchEvent(new Event('change', { bubbles: true }));
            }
        } else {
            console.warn("(SWQP) Campo de preço da página não encontrado neste ciclo.");
             if (targetPrice > 0) {
                 statusDiv.textContent = `Aviso: Não foi possível definir o preço ${targetPrice}M$.`;
             }
        }
        // *** FIM DA LÓGICA DE PREÇO ATUALIZADA ***

        // Seleciona o item (depois de tentar definir o preço)
        const initialListJsonForCount = await GM_getValue(STORAGE_KEY_ITEMS, '[]');
        const initialTotalCount = JSON.parse(initialListJsonForCount).length + itemsToOffer.length + 1;
        const currentItemNumber = initialTotalCount - itemsToOffer.length;
        statusDiv.textContent = `Ofertando ${currentItemNumber}/${initialTotalCount}: '${itemToOffer.text}' por ${targetPrice} M$...`;
        console.log(`(SWQP) Preparando oferta: ${itemToOffer.text} (ID: ${itemToOffer.value}) por ${targetPrice} M$`);
        itemDropdown.value = itemToOffer.value;

        // Valida seleção do item
        const selectedOptionText = itemDropdown.options[itemDropdown.selectedIndex]?.textContent;
        if (itemDropdown.value !== itemToOffer.value || (selectedOptionText && selectedOptionText.trim() !== itemToOffer.text)) {
             statusDiv.textContent = `Erro: Não selecionou '${itemToOffer.text}' corretamente. Pulando...`;
             console.warn(`(SWQP) Falha ao selecionar item ${itemToOffer.value}. Pulando.`);
             await GM_setValue(STORAGE_KEY_ITEMS, JSON.stringify(itemsToOffer));
             setTimeout(processNextOffer, BASE_DELAY_MS / 2); return;
        }

        // Salva lista restante
        await GM_setValue(STORAGE_KEY_ITEMS, JSON.stringify(itemsToOffer));

        // *** ATRASO ANTES DO CLIQUE ***
        console.log(`(SWQP) Aguardando ${POST_PRICE_SET_DELAY_MS}ms após definir campos...`);
        await new Promise(resolve => setTimeout(resolve, POST_PRICE_SET_DELAY_MS));

        // Atraso principal (opcional aqui, pode ser combinado com o acima ou removido se POST_PRICE_SET_DELAY_MS for suficiente)
        console.log(`(SWQP) Aguardando ${BASE_DELAY_MS}ms antes do clique final...`);
        await new Promise(resolve => setTimeout(resolve, BASE_DELAY_MS));


        console.log("(SWQP) Clicando 'Ofertar item'...");
        offerButton.click();
    }

    async function checkOfferStateOnLoad() {
        createUI();
        const statusDiv = document.getElementById('bulkOfferStatusScript');
        const isRunning = await GM_getValue(STORAGE_KEY_RUNNING, false);
        const itemsPendingJson = await GM_getValue(STORAGE_KEY_ITEMS, '[]');

        if (isRunning && itemsPendingJson !== '[]' && statusDiv) {
            console.log("(SWQP) Script recarregado, continuando oferta.");
            disableButtons(true);
            await processNextOffer();
        } else {
             console.log("(SWQP) Nenhuma oferta em andamento ou lista vazia.");
             if (isRunning) {
                 console.log("(SWQP) Flag 'running' encontrada, mas lista vazia. Limpando.");
                 await stopOffer();
             } else if (statusDiv) {
                 disableButtons(false);
                 statusDiv.textContent = "Status: Pronto.";
             }
        }
    }

    checkOfferStateOnLoad();

})();