FarmRPG - Quick Farm Tasks

Adds buttons outside of the relevant page to allow the user to do daily tasks quickly. Visit farm supply to allow the script to confirm you own the perks for chickens, cows and pigs in order to unlock their pet/feed buttons

As of 2025-04-18. See the latest version.

// ==UserScript==
// @name         FarmRPG - Quick Farm Tasks
// @namespace    duck.wowow
// @version      0.1
// @description  Adds buttons outside of the relevant page to allow the user to do daily tasks quickly. Visit farm supply to allow the script to confirm you own the perks for chickens, cows and pigs in order to unlock their pet/feed buttons
// @author       Odung
// @match        https://farmrpg.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=farmrpg.com
// @grant        GM.getValue
// @grant        GM.setValue
// @require      https://cdn.jsdelivr.net/npm/[email protected]/build/global/luxon.min.js
// @license      MIT
// ==/UserScript==

(async function() {
    'use strict';

    const now = luxon.DateTime.now().setZone("America/Chicago").startOf("day");
    const current = now.day;

    const farmData = await GM.getValue('farm', {chicken: 0, cow: 0, pig: 0, storehouse: 0, farmhouse: 0, raptor: 0});
    let perks = await GM.getValue('perks', {});

    function observePage(element) {
        const observer = new MutationObserver((mutationsList) => {
            for (const mutation of mutationsList) {
                if (mutation.type === 'attributes' && mutation.attributeName === 'data-page') {
                    const page = mutation.target.getAttribute('data-page');
                    if (page === 'xfarm') farm();
                    else if (page === 'supply') supply();
                }
            }
        });

        observer.observe(element, { attributes: true });
    }

    function farm() {
        const id = getFarmId();
        if (!id) return;

        const observer = new MutationObserver(() => {
            const elements = [
                { selector: 'div.page:nth-child(2) > div:nth-child(1) > div:nth-child(2) > div:nth-child(7) > div:nth-child(1) > div:nth-child(1) > ul:nth-child(1) > li:nth-child(1) > a:nth-child(1) > div:nth-child(1) > div:nth-child(1)', title: "Pet", url: "https://farmrpg.com/worker.php?go=petallchickens", id: 'chicken' },
                { selector: 'div.page:nth-child(2) > div:nth-child(1) > div:nth-child(2) > div:nth-child(7) > div:nth-child(1) > div:nth-child(1) > ul:nth-child(1) > li:nth-child(2) > a:nth-child(1) > div:nth-child(1) > div:nth-child(1)', title: "Pet", url: "https://farmrpg.com/worker.php?go=petallcows", id: 'cow' },
                { selector: 'div.page:nth-child(2) > div:nth-child(1) > div:nth-child(2) > div:nth-child(7) > div:nth-child(1) > div:nth-child(1) > ul:nth-child(1) > li:nth-child(3) > a:nth-child(1) > div:nth-child(1) > div:nth-child(1)', title: "Feed", url: "https://farmrpg.com/worker.php?go=feedallpigs", id: 'pig' },
                { selector: 'div.page:nth-child(2) > div:nth-child(1) > div:nth-child(2) > div:nth-child(7) > div:nth-child(1) > div:nth-child(1) > ul:nth-child(1) > li:nth-child(4) > a:nth-child(1) > div:nth-child(1) > div:nth-child(1)', title: "Work", url: `https://farmrpg.com/worker.php?go=work&id=${id}`, id: 'storehouse' },
                { selector: 'div.page:nth-child(2) > div:nth-child(1) > div:nth-child(2) > div:nth-child(7) > div:nth-child(1) > div:nth-child(1) > ul:nth-child(1) > li:nth-child(5) > a:nth-child(1) > div:nth-child(1) > div:nth-child(1)', title: "Rest", url: `https://farmrpg.com/worker.php?go=rest&id=${id}`, id: 'farmhouse' },
                { selector: 'div.page:nth-child(2) > div:nth-child(1) > div:nth-child(2) > div:nth-child(7) > div:nth-child(1) > div:nth-child(1) > ul:nth-child(1) > li:nth-child(6) > a:nth-child(1) > div:nth-child(1) > div:nth-child(1)', title: "Pet", url: "https://farmrpg.com/worker.php?go=incuallraptors", id: 'raptor' }
            ];

            let found = false;

            elements.forEach(({ selector, title, url, id }) => {
                const element = document.querySelector(selector);
                if (element) {
                    found = true;
                    if (!element.querySelector("button") && farmData[id] !== current) {
                        if (id === 'chicken' && !perks['Pet-o-Matic I']) return;
                        else if (id === 'cow' && !perks['Pet-o-Matic II']) return;
                        else if (id === 'pig' && !perks['Feed-o-Matic I']) return;
                        element.appendChild(createButton(title, url, id, selector));
                    }
                }
            });

            if (found) observer.disconnect();
        });

        observer.observe(document.body, { childList: true, subtree: true });
    }

    function createButton(title, url, id, selector) {
        const button = document.createElement("button");
        button.textContent = title;
        button.id = id;
        button.style.cssText = `display: flex; width: 50px; align-items: center; justify-content: center; padding: 8px 12px; margin-left: 10px; font-size: 14px; font-weight: bold; border: none; border-radius: 5px; background: #333; color: #fff; cursor: pointer; transition: background 0.2s ease, transform 0.1s ease;`;

        button.addEventListener("mouseenter", () => {
            button.style.background = "#444";
            button.style.transform = "scale(1.05)";
        });

        button.addEventListener("mouseleave", () => {
            button.style.background = "#333";
            button.style.transform = "scale(1)";
        });

        button.addEventListener("click", async () => {
            event.preventDefault();
            event.stopPropagation();
            try {
                const response = await fetch(url, { method: "POST" });
                console.log(response);

                if (response.ok) {
                    farmData[id] = current;
                    await GM.setValue('farm', farmData);
                    button.remove();
                    const calendar = document.querySelector(selector)?.parentElement.querySelector('.f7-icons')?.parentElement;
                    if (calendar) calendar.remove();
                } else {
                    button.textContent = 'Error';
                    setTimeout(() => {
                        button.textContent = title;
                    }, 1000);
                }
            } catch (error) {
                console.error(error);
            }
        });

        return button;
    }

    function getFarmId() {
        const match = window.location.href.match(/id=(\d+)/);
        return match ? match[1] : null;
    }

    async function supply() {
        const playerPerks = {};
        const requiredPerks = ['Pet-o-Matic I', 'Pet-o-Matic II', 'Feed-o-Matic I'];

        if (!Array.from(document.querySelectorAll(".item-title strong")).find(el => el.textContent.trim() === 'Pet-o-Matic I')) return;

        requiredPerks.forEach(perk => {
            if (Array.from(document.querySelectorAll(".item-title strong")).find(el => el.textContent.trim() === perk).parentElement.nextElementSibling.firstElementChild.classList.contains('btnblue')) playerPerks[perk] = true;
        });

        perks = playerPerks;
        await GM.setValue('perks', playerPerks);
    }

    const observer = new MutationObserver(mutations => {
        const element = document.querySelector('#fireworks');
        if (element) {
            observePage(element);
            observer.disconnect();
        }
    });

    observer.observe(document.body, { childList: true, subtree: true });
})();