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.');
    }

})();