diff --git a/GrepoAutoRuralResources.js b/GrepoAutoRuralResources.js
new file mode 100644
index 0000000..3066308
--- /dev/null
+++ b/GrepoAutoRuralResources.js
@@ -0,0 +1,168 @@
+// ==UserScript==
+// @name GrepoTweaks-AutoRuralResources (Captain-Free Version)
+// @namespace sentinelindicator
+// @version 2.2.0
+// @description Auto farm loot from rural villages using per-town AJAX requests, no Captain required
+// @author Dimitrios
+// @match http://*.grepolis.com/game/*
+// @match https://*.grepolis.com/game/*
+// @grant none
+// ==/UserScript==
+
+(function () {
+ 'use strict';
+ const uw = typeof unsafeWindow !== "undefined" ? unsafeWindow : window;
+
+ const timerInterval = 300000; // 5 minutes
+ const delta_time = 10000;
+ let loop, lastTime, timer;
+ let startOnLogin = true;
+
+ const buttonHtml =
+ '
' +
+ '';
+
+ function getPolisList() {
+ const islandList = [];
+ const polisList = [];
+ const towns = uw.MM.getCollections().Town[0].models;
+
+ for (let town of towns) {
+ const { on_small_island, island_id, id } = town.attributes;
+ if (on_small_island) continue;
+ if (!islandList.includes(island_id)) {
+ islandList.push(island_id);
+ polisList.push(id);
+ }
+ }
+ return polisList;
+ }
+
+ function getLootableFarms(town_id) {
+ const { models: farms } = uw.MM.getOnlyCollectionByName('FarmTown');
+ const { models: relations } = uw.MM.getOnlyCollectionByName('FarmTownPlayerRelation');
+ const town = uw.ITowns.towns[town_id];
+ const x = town.getIslandCoordinateX();
+ const y = town.getIslandCoordinateY();
+ const now = Math.floor(Date.now() / 1000);
+ const result = [];
+
+ for (let farm of farms) {
+ if (farm.attributes.island_x !== x || farm.attributes.island_y !== y) continue;
+
+ for (let rel of relations) {
+ if (
+ rel.attributes.farm_town_id === farm.attributes.id &&
+ rel.attributes.relation_status === 1 &&
+ (!rel.attributes.lootable_at || now >= rel.attributes.lootable_at)
+ ) {
+ result.push({ town_id, farm_town_id: rel.attributes.farm_town_id, relation_id: rel.id });
+ }
+ }
+ }
+
+ return result;
+ }
+
+ function claimSingle(town_id, farm_town_id, relation_id) {
+ const data = {
+ model_url: `FarmTownPlayerRelation/${relation_id}`,
+ action_name: 'claim',
+ arguments: { farm_town_id: farm_town_id, type: 'resources', option: 1 },
+ town_id: town_id,
+ };
+ uw.gpAjax.ajaxPost('frontend_bridge', 'execute', data);
+ }
+
+ async function claimIndividually(polisList) {
+ let max = 60;
+ for (let town_id of polisList) {
+ const farms = getLootableFarms(town_id);
+ for (let farm of farms) {
+ claimSingle(farm.town_id, farm.farm_town_id, farm.relation_id);
+ await new Promise(r => setTimeout(r, 500));
+ if (!--max) return;
+ }
+ }
+ }
+
+ function getNextCollectionTime() {
+ const models = uw.MM.getCollections().FarmTownPlayerRelation[0].models;
+ let lootTimes = {};
+
+ for (let m of models) {
+ const time = m.attributes.lootable_at;
+ lootTimes[time] = (lootTimes[time] || 0) + 1;
+ }
+
+ let max = 0;
+ let next = 0;
+ for (let key in lootTimes) {
+ if (lootTimes[key] > max) {
+ max = lootTimes[key];
+ next = key;
+ }
+ }
+
+ const seconds = next - Math.floor(Date.now() / 1000);
+ return seconds > 0 ? seconds * 1000 : 0;
+ }
+
+ async function mainLoop() {
+ const next = getNextCollectionTime();
+ if (timer + 2 * delta_time < next) {
+ timer = next + Math.floor(Math.random() * delta_time);
+ }
+
+ if (timer < 1) {
+ const polisList = getPolisList();
+ await claimIndividually(polisList);
+ timer = timerInterval + Math.floor(Math.random() * delta_time);
+ setTimeout(() => uw.WMap.removeFarmTownLootCooldownIconAndRefreshLootTimers(), 2000);
+ }
+
+ const now = Date.now();
+ timer -= now - lastTime;
+ lastTime = now;
+
+ const bt = document.getElementById('ptimer');
+ bt.innerHTML = timer > 0 ? parseInt(timer / 1000) : '0';
+ }
+
+ function handleClick() {
+ const isCaptainActive = uw.GameDataPremium.isAdvisorActivated('captain');
+
+ if (!loop) {
+ timer = getNextCollectionTime() + Math.random() * delta_time;
+ lastTime = Date.now();
+ loop = setInterval(mainLoop, 1000);
+
+ uw.$('#btbutton').css(
+ 'filter',
+ isCaptainActive
+ ? 'brightness(100%) sepia(100%) hue-rotate(90deg) saturate(1500%) contrast(0.8)'
+ : 'brightness(294%) sepia(100%) hue-rotate(15deg) saturate(1000%) contrast(0.8)'
+ );
+ } else {
+ clearInterval(loop);
+ loop = null;
+ uw.$('#btbutton').css(
+ 'filter',
+ 'brightness(70%) sepia(100%) hue-rotate(-50deg) saturate(1000%) contrast(0.8)'
+ );
+ }
+ }
+
+ setTimeout(() => {
+ if (!document.getElementById('btbutton')) {
+ uw.$('.tb_activities, .toolbar_activities').find('.middle').append(buttonHtml);
+ }
+ if (startOnLogin) handleClick();
+ }, 4000);
+
+ uw.$(document).on('click', '#btbutton', handleClick);
+
+ console.log('[GrepoTweaks-AutoRuralResources] Captain-free script loaded 🚀');
+})();