// ==UserScript==
// @name FarmRPG - Quick Tasks
// @namespace duck.wowow
// @version 0.1.1
// @description Adds buttons to easily complete farm tasks, PHRs and exchange cneter trades with a single click.. Visit farm supply to confirm you have unlocked the perks for chickens, cows and pigs for farm tasks.
// @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;
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();
else if (page === 'quests') quests();
else if (page === 'exchange') exchange();
}
}
});
observer.observe(element, { attributes: true });
}
async function farm() {
const id = getFarmId();
if (!id) return;
const farmData = await GM.getValue('farm', {chicken: 0, cow: 0, pig: 0, storehouse: 0, farmhouse: 0, raptor: 0});
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 (e) => {
e.preventDefault();
e.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);
button.textContent = 'Error';
setTimeout(() => {
button.textContent = title;
}, 1000);
}
});
return button;
}
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 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);
}
function quests() {
const phrTitle = Array.from(document.querySelectorAll('.content-block-title')).find(e => e.textContent.trim().startsWith('Personal Requests'));
if (!phrTitle) return;
const phrs = phrTitle.nextElementSibling?.querySelectorAll('a.item-link.close-panel') || [];
phrs.forEach(phr => {
const text = phr.querySelector('.item-after')?.textContent.trim();
if (text === 'READY!') {
const href = phr.getAttribute('href');
const idMatch = href.match(/id=(\d+)/);
if (!idMatch) return;
const id = idMatch[1];
const placement = phr.querySelector('.item-after');
const button = document.createElement('button');
button.textContent = 'Complete';
button.style.cssText = `display: flex; align-items: center; justify-content: center; padding: 8px 12px; margin-right: 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('click', async e => {
e.preventDefault();
e.stopPropagation();
try {
const response = await fetch(`https://farmrpg.com/worker.php?go=collectquest&id=${id}`, {method: 'POST'});
console.log(response);
if (response.ok) {
button.remove();
placement.closest('li')?.remove();
phrTitle.textContent = phrTitle.textContent.replace(/\((\d+)\)/, (_, n) => `(${n - 1})`);
} else {
button.textContent = 'Error';
setTimeout(() => {
button.textContent = 'Complete';
}, 1000);
}
} catch (error) {
console.error(error);
button.textContent = 'Error';
setTimeout(() => {
button.textContent = 'Complete';
}, 1000);
}
});
if (placement) placement.prepend(button);
}
});
}
function exchange() {
const buttons = Array.from(document.querySelectorAll('.button.btngreen.acceptbtn'));
const ids = buttons.map(btn => btn.dataset.id);
const trades = {};
buttons.forEach(btn => {
const id = btn.dataset.id;
const get = btn.dataset.get;
const req = btn.dataset.req;
trades[id] = { get, req };
});
async function disableTrades() {
const disabledTrades = await GM.getValue('disabled-trades', {});
const contentBlocks = document.querySelectorAll('.page-content .content-block');
if (!contentBlocks[1] || document.querySelector('.odung-ec')) return;
const container = document.createElement('div');
container.className = 'odung-ec';
container.style.cssText = 'color:#fff;background:#0c131d';
const header = document.createElement('div');
header.textContent = 'Disable Trades';
header.style.cssText = 'border:1px solid #666;background:#1a1b1d;padding:5px;cursor:pointer;';
const list = document.createElement('div');
list.style.cssText = 'display:none;margin-top:5px;';
header.addEventListener('click', () => {
list.style.display = list.style.display === 'none' ? 'block' : 'none';
});
const toggleTrades = () => {
buttons.forEach(btn => {
const id = btn.dataset.id, get = btn.dataset.get, req = btn.dataset.req;
const match = disabledTrades[id];
const shouldHide = match && match.get === get && match.req === req;
btn.style.display = shouldHide ? 'none' : '';
if (btn.previousElementSibling) btn.previousElementSibling.style.display = shouldHide ? 'none' : '';
});
};
const createCheckbox = (id, trade, isChecked) => {
const row = document.createElement('div');
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.checked = isChecked;
checkbox.addEventListener('change', async () => {
if (checkbox.checked) disabledTrades[id] = trade;
else delete disabledTrades[id];
await GM.setValue('disabled-trades', disabledTrades);
toggleTrades();
});
const label = document.createElement('label');
label.textContent = ` ${trade.req} > ${trade.get}`;
label.prepend(checkbox);
row.appendChild(label);
list.appendChild(row);
};
Object.entries(disabledTrades).forEach(([id, trade]) => createCheckbox(id, trade, true));
Object.entries(trades).forEach(([id, trade]) => {
if (!disabledTrades[id]) createCheckbox(id, trade, false);
});
container.append(header, list);
contentBlocks[1].prepend(container);
toggleTrades();
}
disableTrades();
buttons.forEach((btn, i) => {
const row = btn.previousElementSibling?.querySelector('.row');
if (!row) return;
const children = Array.from(row.children);
children.forEach(child => {
child.classList.add('odung-33');
});
if (children.length === 2) {
const newButton = document.createElement('button');
newButton.textContent = 'Quick Accept';
newButton.className = 'odung-trade-btn';
newButton.style.cssText = 'margin:0 5px;padding:4px 8px;font-size:12px;background:#003300;color:#fff;border:1px solid #006600;cursor:pointer;align-self:center';
newButton.addEventListener('click', async e => {
try {
const response = await fetch(`https://farmrpg.com/worker.php?go=exchtradeaccept&id=${ids[i]}`, { method: 'POST'});
console.log(response);
if (response.ok) {
newButton.remove();
btn.outerHTML = '<a href="#" class="button btngray" style="margin-bottom:15px">Trade Complete</a>';
} else {
newButton.textContent = 'Error';
setTimeout(() => {
newButton.textContent = 'Quick Accept';
}, 1000);
}
} catch (error) {
console.error(error);
newButton.textContent = 'Error';
setTimeout(() => {
newButton.textContent = 'Quick Accept';
}, 1000);
}
});
row.insertBefore(newButton, children[1]);
}
});
const style = document.createElement('style');
style.textContent = `.odung-33 { width: calc((100% - 15px*1) / 3) !important; }`;
document.head.appendChild(style);
}
const observer = new MutationObserver(mutations => {
const element = document.querySelector('#fireworks');
if (element) {
observePage(element);
observer.disconnect();
}
});
observer.observe(document.body, { childList: true, subtree: true });
})();