Autofarm V7

FarmGod auto-farm with configurable Enter interval and FarmGod call interval

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да инсталирате разширение, като например Tampermonkey .

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==UserScript==
// @name         Autofarm V7
// @version      7
// @include      https://*/game.php*screen=am_farm*
// @namespace    https://greasyfork.org/users/1388863
// @description  FarmGod auto-farm with configurable Enter interval and FarmGod call interval
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    const domain = window.location.hostname.split('.')[0];

    // --- Default settings ---
    const DEFAULTS = {
        isRunning: false,
        enterMin: 250,
        enterMax: 500,
        reloadMin: 600,
        reloadMax: 900,
        webhookUrl: ''
    };

    function loadSettings() {
        try {
            const saved = JSON.parse(localStorage.getItem(domain + '_autofarm_settings') || '{}');
            return Object.assign({}, DEFAULTS, saved);
        } catch (e) {
            return Object.assign({}, DEFAULTS);
        }
    }

    function saveSettings(s) {
        localStorage.setItem(domain + '_autofarm_settings', JSON.stringify(s));
    }

    function loadPosition() {
        try {
            return JSON.parse(localStorage.getItem(domain + '_autofarm_pos') || 'null');
        } catch (e) { return null; }
    }

    function savePosition(x, y) {
        localStorage.setItem(domain + '_autofarm_pos', JSON.stringify({ x, y }));
    }

    let settings = loadSettings();
    let isRunning = JSON.parse(localStorage.getItem(domain + '_isRunning')) || false;
    let intervalId;
    let countdownInterval;
    let emptyFarmChecks = 0;
    let captchaAlertSent = false;
    const EMPTY_FARM_THRESHOLD = 4;

    function randomDelay(min, max) {
        return Math.floor(Math.random() * (max - min + 1)) + min;
    }

    // --- FarmGod completion detection ---
    function hasFarmsRemaining() {
        // Strategy 1: FarmGod progress bar text (e.g. "8 / 8")
        const allElements = document.querySelectorAll('*');
        for (const el of allElements) {
            if (el.children.length > 0) continue;
            const text = (el.innerText || el.textContent || '').trim();
            const match = text.match(/^(\d+)\s*\/\s*(\d+)$/);
            if (match) {
                const current = parseInt(match[1]);
                const total = parseInt(match[2]);
                if (total > 0) {
                    console.log(`[Autofarm] FarmGod Fortschritt: ${current}/${total}`);
                    return current < total;
                }
            }
        }

        // Strategy 2: progress bar at 100% width
        const progressBars = document.querySelectorAll(
            '.progress-bar, .farmgod_bar, [class*="progress"], [id*="progress"], [style*="width: 100%"]'
        );
        for (const bar of progressBars) {
            const style = bar.getAttribute('style') || '';
            const computedWidth = window.getComputedStyle(bar).width;
            const parentWidth = bar.parentElement
                ? window.getComputedStyle(bar.parentElement).width
                : null;
            if (style.includes('width: 100%') || style.includes('width:100%')) {
                console.log('[Autofarm] Fortschrittsbalken bei 100% erkannt.');
                return false;
            }
            if (parentWidth && computedWidth === parentWidth) {
                console.log('[Autofarm] Fortschrittsbalken füllt Parent komplett.');
                return false;
            }
        }

        // Strategy 3: FarmGod table rows (exclude "Letzte Plünderungen")
        const allTables = document.querySelectorAll('table');
        for (const table of allTables) {
            const section = table.closest
                ? table.closest('[id*="last"], [class*="last"], [id*="report"], [class*="report"]')
                : null;
            if (section) continue;
            const heading = table.previousElementSibling;
            if (heading && /letzte|plünderung|report/i.test(heading.textContent)) continue;
            const rows = table.querySelectorAll('tbody tr');
            if (rows.length > 0) return true;
        }

        return false;
    }

    // --- Captcha detection ---
    function isCaptchaPresent() {
        const selectors = [
            '#captcha', '.captcha',
            'iframe[src*="captcha"]', 'iframe[src*="recaptcha"]',
            '.g-recaptcha', '#recaptcha',
            'form[action*="captcha"]',
            '[class*="captcha"]', '[id*="captcha"]'
        ];
        for (const sel of selectors) {
            if (document.querySelector(sel)) return true;
        }
        return false;
    }

    function sendDiscordAlert() {
        settings = loadSettings();
        const webhookUrl = settings.webhookUrl;
        if (!webhookUrl || webhookUrl.trim() === '') {
            console.warn('[Autofarm] Kein Discord Webhook hinterlegt – Alert übersprungen.');
            return;
        }
        const payload = {
            username: 'Autofarm V7',
            avatar_url: 'https://i.imgur.com/4M34hi2.png',
            embeds: [{
                title: '⚠️ Captcha erkannt!',
                description: 'Der Autofarm wurde gestoppt weil ein Captcha erkannt wurde.',
                color: 0xe74c3c,
                fields: [
                    { name: '🌐 Server', value: domain, inline: true },
                    { name: '🕒 Zeit', value: new Date().toLocaleString('de-DE'), inline: true },
                    { name: '🔗 Seite', value: window.location.href, inline: false }
                ],
                footer: { text: 'Autofarm V7 – Captcha Alert' }
            }]
        };
        fetch(webhookUrl, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(payload)
        })
        .then(res => res.ok
            ? console.log('[Autofarm] Discord Alert gesendet.')
            : console.error('[Autofarm] Discord Alert fehlgeschlagen:', res.status))
        .catch(err => console.error('[Autofarm] Discord Webhook Fehler:', err));
    }

    function handleCaptchaDetected() {
        if (captchaAlertSent) return;
        captchaAlertSent = true;
        console.warn('[Autofarm] ⚠️ Captcha erkannt! Stoppe Autofarm...');
        sendDiscordAlert();
        stopProcess();
        const countdownElement = document.getElementById('af-countdown');
        if (countdownElement) {
            countdownElement.style.display = 'block';
            countdownElement.style.color = '#e74c3c';
            countdownElement.innerText = '⚠️ Captcha erkannt! Autofarm gestoppt.';
        }
    }

    function startCaptchaObserver() {
        const observer = new MutationObserver(() => {
            if (isRunning && isCaptchaPresent()) handleCaptchaDetected();
        });
        observer.observe(document.body, { childList: true, subtree: true });
        if (isCaptchaPresent()) handleCaptchaDetected();
    }

    function pressEnterRandomly() {
        settings = loadSettings();
        const delay = randomDelay(settings.enterMin, settings.enterMax);
        document.dispatchEvent(new KeyboardEvent('keydown', {
            key: 'Enter', code: 'Enter', which: 13, keyCode: 13, bubbles: true
        }));
        setTimeout(() => {
            if (!hasFarmsRemaining()) {
                emptyFarmChecks++;
                console.log(`[Autofarm] Keine Farmen erkannt (${emptyFarmChecks}/${EMPTY_FARM_THRESHOLD})`);
                if (emptyFarmChecks >= EMPTY_FARM_THRESHOLD) {
                    console.log('[Autofarm] Farmliste leer – Enter-Loop pausiert.');
                    clearTimeout(intervalId);
                    const cd = document.getElementById('af-countdown');
                    if (cd) cd.style.color = '#e74c3c';
                    startCountdown();
                    return;
                }
            } else {
                emptyFarmChecks = 0;
                const cd = document.getElementById('af-countdown');
                if (cd) cd.style.color = '#f0a500';
            }
        }, 300);
        intervalId = setTimeout(pressEnterRandomly, delay);
    }

    function loadFarmGodScript() {
        $.getScript('https://higamy.github.io/TW/Scripts/Approved/FarmGodCopy.js')
            .done((s, t) => console.log('[Autofarm] FarmGod loaded:', t))
            .fail((j, s, e) => console.error('[Autofarm] Error loading FarmGod:', e));
    }

    function clickOptionButton(retries = 3) {
        const button = document.querySelector('input.btn.optionButton[value="Plan farms"]');
        if (button) {
            button.click();
            console.log('[Autofarm] "Plan farms" clicked');
        } else if (retries > 0) {
            setTimeout(() => clickOptionButton(retries - 1), randomDelay(2000, 4000));
        }
    }

    function startProcess() {
        console.log('[Autofarm] Starting...');
        captchaAlertSent = false;
        setTimeout(() => {
            loadFarmGodScript();
            setTimeout(() => {
                clickOptionButton();
                setTimeout(() => pressEnterRandomly(), randomDelay(3000, 5000));
            }, randomDelay(3000, 5000));
        }, randomDelay(4000, 7000));
    }

    function stopProcess() {
        clearTimeout(intervalId);
        clearInterval(countdownInterval);
        isRunning = false;
        localStorage.setItem(domain + '_isRunning', false);
        updateButtonState();
        const cd = document.getElementById('af-countdown');
        if (cd && !captchaAlertSent) cd.style.display = 'none';
        console.log('[Autofarm] Stopped.');
    }

    function toggleProcess() {
        if (isRunning) {
            stopProcess();
        } else {
            startProcess();
            isRunning = true;
            localStorage.setItem(domain + '_isRunning', true);
            updateButtonState();
        }
    }

    function startCountdown() {
        settings = loadSettings();
        const cd = document.getElementById('af-countdown');
        let timeLeft = randomDelay(settings.reloadMin, settings.reloadMax);
        if (cd) cd.style.display = 'block';
        countdownInterval = setInterval(() => {
            if (timeLeft <= 0) {
                clearInterval(countdownInterval);
                if (cd) cd.style.display = 'none';
                location.reload();
            } else {
                if (cd) cd.innerText = `Nächster Loop in: ${Math.floor(timeLeft / 60)}m ${timeLeft % 60}s`;
                timeLeft--;
            }
        }, 1000);
    }

    function updateButtonState() {
        const btn = document.getElementById('af-toggle-btn');
        if (!btn) return;
        btn.textContent = isRunning ? 'Stop Looting' : 'Start Looting';
        btn.style.backgroundColor = isRunning ? '#c0392b' : '#27ae60';
    }

    // --- Drag & Drop ---
    function makeDraggable(container, handle) {
        let isDragging = false;
        let startX, startY, startLeft, startTop;

        handle.style.cursor = 'grab';

        handle.addEventListener('mousedown', (e) => {
            isDragging = true;
            startX = e.clientX;
            startY = e.clientY;
            startLeft = parseInt(container.style.left) || 0;
            startTop = parseInt(container.style.top) || 0;
            handle.style.cursor = 'grabbing';
            e.preventDefault();
        });

        document.addEventListener('mousemove', (e) => {
            if (!isDragging) return;
            const dx = e.clientX - startX;
            const dy = e.clientY - startY;
            const newLeft = Math.max(0, Math.min(window.innerWidth - container.offsetWidth, startLeft + dx));
            const newTop = Math.max(0, Math.min(window.innerHeight - container.offsetHeight, startTop + dy));
            container.style.left = newLeft + 'px';
            container.style.top = newTop + 'px';
            container.style.bottom = 'auto';
            container.style.right = 'auto';
        });

        document.addEventListener('mouseup', () => {
            if (!isDragging) return;
            isDragging = false;
            handle.style.cursor = 'grab';
            savePosition(parseInt(container.style.left), parseInt(container.style.top));
        });

        // Touch support
        handle.addEventListener('touchstart', (e) => {
            const touch = e.touches[0];
            isDragging = true;
            startX = touch.clientX;
            startY = touch.clientY;
            startLeft = parseInt(container.style.left) || 0;
            startTop = parseInt(container.style.top) || 0;
            e.preventDefault();
        }, { passive: false });

        document.addEventListener('touchmove', (e) => {
            if (!isDragging) return;
            const touch = e.touches[0];
            const dx = touch.clientX - startX;
            const dy = touch.clientY - startY;
            const newLeft = Math.max(0, Math.min(window.innerWidth - container.offsetWidth, startLeft + dx));
            const newTop = Math.max(0, Math.min(window.innerHeight - container.offsetHeight, startTop + dy));
            container.style.left = newLeft + 'px';
            container.style.top = newTop + 'px';
            container.style.bottom = 'auto';
            container.style.right = 'auto';
            e.preventDefault();
        }, { passive: false });

        document.addEventListener('touchend', () => {
            if (!isDragging) return;
            isDragging = false;
            savePosition(parseInt(container.style.left), parseInt(container.style.top));
        });
    }

    function initializeUI() {
        const existing = document.getElementById('af-container');
        if (existing) existing.remove();

        const container = document.createElement('div');
        container.id = 'af-container';

        // Restore saved position or default to bottom-left
        const savedPos = loadPosition();
        Object.assign(container.style, {
            position: 'fixed',
            backgroundColor: '#2c2c2c',
            color: '#fff',
            padding: '12px 14px',
            borderRadius: '8px',
            zIndex: 10000,
            fontFamily: 'Arial, sans-serif',
            fontSize: '13px',
            minWidth: '240px',
            boxShadow: '0 4px 12px rgba(0,0,0,0.5)'
        });
        if (savedPos) {
            container.style.left = savedPos.x + 'px';
            container.style.top = savedPos.y + 'px';
        } else {
            container.style.bottom = '20px';
            container.style.left = '20px';
        }

        // Title (drag handle)
        const title = document.createElement('div');
        title.textContent = '⚔ Autofarm V7';
        Object.assign(title.style, {
            fontWeight: 'bold',
            fontSize: '14px',
            marginBottom: '10px',
            borderBottom: '1px solid #555',
            paddingBottom: '6px',
            userSelect: 'none'
        });
        container.appendChild(title);

        const countdown = document.createElement('div');
        countdown.id = 'af-countdown';
        countdown.style.display = 'none';
        Object.assign(countdown.style, {
            marginBottom: '8px',
            color: '#f0a500',
            fontWeight: 'bold'
        });
        container.appendChild(countdown);

        function addSetting(label, idMin, idMax, defaultMin, defaultMax, unit) {
            const row = document.createElement('div');
            Object.assign(row.style, { marginBottom: '7px' });
            const lbl = document.createElement('div');
            lbl.textContent = label;
            lbl.style.marginBottom = '2px';
            lbl.style.color = '#aaa';
            row.appendChild(lbl);
            const inputs = document.createElement('div');
            Object.assign(inputs.style, { display: 'flex', gap: '6px', alignItems: 'center' });
            function makeInput(id, value) {
                const inp = document.createElement('input');
                inp.id = id; inp.type = 'number'; inp.value = value; inp.min = 0;
                Object.assign(inp.style, {
                    width: '70px', padding: '3px 5px', borderRadius: '4px',
                    border: '1px solid #555', backgroundColor: '#1a1a1a',
                    color: '#fff', fontSize: '12px'
                });
                return inp;
            }
            inputs.appendChild(makeInput(idMin, defaultMin));
            const dash = document.createElement('span');
            dash.textContent = '–'; dash.style.color = '#aaa';
            inputs.appendChild(dash);
            inputs.appendChild(makeInput(idMax, defaultMax));
            const unitSpan = document.createElement('span');
            unitSpan.textContent = unit; unitSpan.style.color = '#aaa';
            inputs.appendChild(unitSpan);
            row.appendChild(inputs);
            return row;
        }

        container.appendChild(addSetting('Enter-Intervall (Enter-Tempo):', 'af-enter-min', 'af-enter-max', settings.enterMin, settings.enterMax, 'ms'));
        container.appendChild(addSetting('Reload-Intervall (Loop-Zeit):', 'af-reload-min', 'af-reload-max', settings.reloadMin, settings.reloadMax, 's'));

        const webhookRow = document.createElement('div');
        Object.assign(webhookRow.style, { marginBottom: '7px' });
        const webhookLabel = document.createElement('div');
        webhookLabel.textContent = '🔔 Discord Webhook URL:';
        webhookLabel.style.marginBottom = '2px';
        webhookLabel.style.color = '#aaa';
        webhookRow.appendChild(webhookLabel);
        const webhookInput = document.createElement('input');
        webhookInput.id = 'af-webhook-url';
        webhookInput.type = 'text';
        webhookInput.placeholder = 'https://discord.com/api/webhooks/...';
        webhookInput.value = settings.webhookUrl || '';
        Object.assign(webhookInput.style, {
            width: '100%', padding: '3px 5px', borderRadius: '4px',
            border: '1px solid #555', backgroundColor: '#1a1a1a',
            color: '#fff', fontSize: '11px', boxSizing: 'border-box'
        });
        webhookRow.appendChild(webhookInput);
        container.appendChild(webhookRow);

        const saveBtn = document.createElement('button');
        saveBtn.textContent = 'Einstellungen speichern';
        Object.assign(saveBtn.style, {
            display: 'block', width: '100%', padding: '5px', marginBottom: '8px',
            border: 'none', borderRadius: '4px', cursor: 'pointer',
            backgroundColor: '#2980b9', color: '#fff', fontSize: '12px'
        });
        saveBtn.addEventListener('click', () => {
            const newSettings = {
                enterMin: Math.max(50, parseInt(document.getElementById('af-enter-min').value) || DEFAULTS.enterMin),
                enterMax: Math.max(50, parseInt(document.getElementById('af-enter-max').value) || DEFAULTS.enterMax),
                reloadMin: Math.max(10, parseInt(document.getElementById('af-reload-min').value) || DEFAULTS.reloadMin),
                reloadMax: Math.max(10, parseInt(document.getElementById('af-reload-max').value) || DEFAULTS.reloadMax),
                webhookUrl: document.getElementById('af-webhook-url').value.trim()
            };
            if (newSettings.enterMin > newSettings.enterMax)
                [newSettings.enterMin, newSettings.enterMax] = [newSettings.enterMax, newSettings.enterMin];
            if (newSettings.reloadMin > newSettings.reloadMax)
                [newSettings.reloadMin, newSettings.reloadMax] = [newSettings.reloadMax, newSettings.reloadMin];
            saveSettings(newSettings);
            settings = newSettings;
            saveBtn.textContent = '✓ Gespeichert!';
            setTimeout(() => { saveBtn.textContent = 'Einstellungen speichern'; }, 1500);
        });
        container.appendChild(saveBtn);

        const controlButton = document.createElement('button');
        controlButton.id = 'af-toggle-btn';
        Object.assign(controlButton.style, {
            display: 'block', width: '100%', padding: '7px',
            border: 'none', borderRadius: '4px', cursor: 'pointer',
            color: '#fff', fontSize: '13px', fontWeight: 'bold'
        });
        controlButton.addEventListener('click', toggleProcess);
        container.appendChild(controlButton);

        document.body.appendChild(container);
        updateButtonState();

        // Make draggable via title bar
        makeDraggable(container, title);
    }

    initializeUI();
    startCaptchaObserver();

    if (isRunning) {
        console.log('[Autofarm] Resuming from saved state...');
        startProcess();
    } else {
        console.log('[Autofarm] Ready. Press Start Looting to begin.');
    }

})();