commit a804f6ecb31aa0817b33203dee06cf44405dc5c4 Author: Les De Ridder Date: Sun Nov 5 04:18:36 2017 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..463a9b8 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/grepolis-js diff --git a/background.js b/background.js new file mode 100644 index 0000000..4cb6d7c --- /dev/null +++ b/background.js @@ -0,0 +1,178 @@ +let autoClaimOption = 1; + +let state; +let autoClaimInterval; +let claiming; +let lastClaimOption = 0; +let lastClaimTime = Date.now(); + +let injectionPort; + +chrome.runtime.onConnectExternal.addListener(function(port) { + injectionPort = port; + + injectionPort.onMessage.addListener(function(message) { + if(message.action == 'endClaimAll') { + onEndClaimAll(); + } + }); +}); + +function getClaimOptionLength(option) { + return [0, 10, 40, 3 * 60, 8 * 60][option] * 60 * 1000; +} + +function updateBrowserActionStatus() { + chrome.tabs.query({ url: '*://*.grepolis.com/game/index*' }, function(tabs) { + if(tabs.length == 0) { + chrome.browserAction.disable(); + + updateState('manual'); + } else { + chrome.browserAction.enable(); + } + }); +} + +chrome.tabs.onCreated.addListener(updateBrowserActionStatus); +chrome.tabs.onUpdated.addListener(updateBrowserActionStatus); +chrome.tabs.onRemoved.addListener(updateBrowserActionStatus); + +chrome.browserAction.onClicked.addListener(function() { + chrome.tabs.query({ url: '*://*.grepolis.com/game/index*' }, function(tabs) { + if(tabs.length == 0) { + return; + } + + chrome.tabs.update(tabs[0].id, { active: true }); + }); +}); + +function claimAll(option) { + if(claiming) { + console.log('Error: already claiming!'); + return; + } + + lastClaimOption = option; + claiming = true; + + updateBadgeText(); + + injectionPort.postMessage({ action: 'claimAll', args: [option] }); +} + +function onEndClaimAll() { + claiming = false; + + lastClaimTime = Date.now(); + if(state == 'automatic') { + autoClaimInterval = setInterval(function() { claimAll(autoClaimOption); }, getClaimOptionLength(autoClaimOption)); + } + + updateBadgeText(); +} + +function updateBadgeText() { + let timeSinceClaim = Date.now() - lastClaimTime; + let lastClaimLength = getClaimOptionLength(lastClaimOption); + + if(state == 'automatic' && claiming) { + chrome.browserAction.setBadgeText({ text: '…' }); + } else if(state == 'manual' && timeSinceClaim >= lastClaimLength) { + chrome.browserAction.setBadgeText({ text: '(∞)' }); + } else { + let timeLeft = lastClaimLength - timeSinceClaim; + + let timeText; + if(timeLeft <= 0) { + timeText = '0s'; + } else if(timeLeft < 60 * 1000) { + timeText = Math.trunc(timeLeft / 1000) + 's'; + } else if(timeLeft < 60 * 60 * 1000) { + timeText = Math.trunc(timeLeft / 60 / 1000) + 'm'; + } else { + timeText = Math.trunc(timeLeft / 60 / 60 / 1000) + 'h' + Math.trunc((timeLeft % (60 * 60 * 1000)) / 60/ 1000) + 'm'; + } + + chrome.browserAction.setBadgeText({ text: state == 'manual' ? '(' + timeText + ')' : timeText }); + } +} + +function claimAllWhenReady(option) { + let timeSinceClaim = Date.now() - lastClaimTime; + let lastClaimLength = getClaimOptionLength(lastClaimOption); + let timeLeft = lastClaimLength - timeSinceClaim; + + setTimeout(function() { claimAll(option); }, timeLeft); +} + +function updateState(newState) { + state = newState; + + if(newState == 'automatic') { + let timeSinceClaim = Date.now() - lastClaimTime; + let lastClaimLength = getClaimOptionLength(lastClaimOption); + let timeLeft = lastClaimLength - timeSinceClaim; + setTimeout(function() { + claimAll(autoClaimOption); + }, timeLeft); + + chrome.browserAction.setBadgeBackgroundColor({ color: 'green' }); + } else if(newState == 'manual') { + clearInterval(autoClaimInterval); + + chrome.browserAction.setBadgeBackgroundColor({ color: 'red' }); + } + + updateBadgeText(); + chrome.contextMenus.update(state, { checked: true }); +} + +chrome.contextMenus.create({ + id: 'automatic', + type: 'radio', + title: 'Automatic', + contexts: ['browser_action'], + onclick: function() { updateState('automatic'); } +}); + +chrome.contextMenus.create({ + id: 'manual', + type: 'radio', + title: 'Manual', + contexts: ['browser_action'], + onclick: function() { updateState('manual'); } +}); + +chrome.contextMenus.create({ + type: 'normal', + title: 'Claim for 10m', + contexts: ['browser_action'], + onclick: function() { updateState('manual'); claimAllWhenReady(1); } +}); + +chrome.contextMenus.create({ + type: 'normal', + title: 'Claim for 40m', + contexts: ['browser_action'], + onclick: function() { updateState('manual'); claimAllWhenReady(2); } +}); + +chrome.contextMenus.create({ + type: 'normal', + title: 'Claim for 3h', + contexts: ['browser_action'], + onclick: function() { updateState('manual'); claimAllWhenReady(3); } +}); + +chrome.contextMenus.create({ + type: 'normal', + title: 'Claim for 8h', + contexts: ['browser_action'], + onclick: function() { updateState('manual'); claimAllWhenReady(4); } +}); + +updateBrowserActionStatus(); +updateState('manual'); +setInterval(updateBadgeText, 500); diff --git a/content.js b/content.js new file mode 100644 index 0000000..30c5331 --- /dev/null +++ b/content.js @@ -0,0 +1,9 @@ +let headElement = document.getElementsByTagName('head')[0]; + +let idInjection = document.createElement('script'); +idInjection.innerHTML = 'let extensionId = "' + chrome.runtime.id + '";'; +headElement.appendChild(idInjection); + +let scriptInjection = document.createElement('script'); +scriptInjection.src = chrome.runtime.getURL('injection.js'); +headElement.appendChild(scriptInjection); diff --git a/icon128.png b/icon128.png new file mode 100644 index 0000000..a557cf4 Binary files /dev/null and b/icon128.png differ diff --git a/icon16.png b/icon16.png new file mode 100644 index 0000000..948313c Binary files /dev/null and b/icon16.png differ diff --git a/icon48.png b/icon48.png new file mode 100644 index 0000000..0202a3e Binary files /dev/null and b/icon48.png differ diff --git a/injection.js b/injection.js new file mode 100644 index 0000000..5ad18a1 --- /dev/null +++ b/injection.js @@ -0,0 +1,117 @@ +let extensionPort; + +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms + (Math.random() * 2.5 * 1000))); +} + +function townSwitch(townId) { + let TownSwitch = require('helpers/town_switch'); + + (new TownSwitch()).townSwitch(townId); +} + +function toIslandView() { + $.Observer(GameEvents.ui.bull_eye.radiobutton.island_view.click).publish(); +} + +function toCityOverview() { + $.Observer(GameEvents.ui.bull_eye.radiobutton.city_overview.click).publish(); +} + +function getCurrentTown() { + return MM.getCollections().Town[0].getCurrentTown(); +} + +function getAllTownIds() { + return MM.getCollections().Town[0].models.map(m => m.id); +} + +function getPlayerGods() { + return MM.getModels().PlayerGods[Game.player_id]; +} + +//example: getTownResource.call(getCurrentTown(), 'stone') +//NOTE: Unused function +function getTownResource(resource) { + return this.resources.getResource(resource); +} + +function getFarmTownPlayerRelation(farmTownId) { + return MM.getCollections().FarmTownPlayerRelation[0].getRelationForFarmTown(farmTownId); +} + +function getCurrentTownFarmTownIds() { + return MM.getCollections().FarmTown[0].getAllForIslandViaXY(getCurrentTown().getIslandX(), getCurrentTown().getIslandY()).map(f => f.id); +} + +function claimResources(farmTownId, option, success, error) { + let farmTownPlayerRelation = getFarmTownPlayerRelation(farmTownId); + + farmTownPlayerRelation.claim("resources", option, { success: success.bind(this), error: error.bind(this) }); +} + +function willWasteResources(farmTownId, option) { + let farmTownPlayerRelation = getFarmTownPlayerRelation(farmTownId); + let claimResourceValues = farmTownPlayerRelation.getClaimResourceValues(); + let claimResourceValue = claimResourceValues[option - 1]; + + let WastedResourcesHelper = require('WastedResourcesHelper') + wr_helper = new WastedResourcesHelper(getCurrentTown(), getPlayerGods()); + + return wr_helper.hasWastedResources({ + wood : claimResourceValue, + stone : claimResourceValue, + iron : claimResourceValue + }); +} + +function canClaim(farmTownId) { + let farmTownPlayerRelation = getFarmTownPlayerRelation(farmTownId); + + return farmTownPlayerRelation.isLootable(); +} + +async function claimAll(option) { + let originalId = Game.townId; + toIslandView(); + + for(townId of getAllTownIds()) { + await sleep(1000); + + townSwitch(townId); + + for(farmTownId of getCurrentTownFarmTownIds()) { + await sleep(1000); + + if(!canClaim(farmTownId)) { + continue; + } + //TODO: Fix this + if(willWasteResources(farmTownId, option)) { + //TODO: Notification? + continue; + } + + claimResources( + farmTownId, + option, + function() { }, + function() { console.log('Couldn\'t claim farm town ' + farmTownId + ' for town ' + townId + '!') } + ); + } + } + + await sleep(1000); + + townSwitch(originalId); + toCityOverview(); + + extensionPort.postMessage({ action: 'endClaimAll', args: [] }); +} + +extensionPort = chrome.runtime.connect(extensionId); +extensionPort.onMessage.addListener(function(message) { + if(message.action == 'claimAll') { + claimAll(message.args[0]); + } +}); diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..dce31fe --- /dev/null +++ b/manifest.json @@ -0,0 +1,28 @@ +{ + "manifest_version": 2, + "name": "Grepolis Bot", + "version": "0.0.1", + + "description": "A Grepolis bot", + "icons": { + "16": "icon16.png", + "48": "icon48.png", + "128": "icon128.png" + }, + "author": "Les De Ridder", + + "browser_action": { }, + "background": { "scripts": ["background.js"] }, + "content_scripts": [ + { + "matches": ["*://*.grepolis.com/game/index*"], + "js": ["content.js"] + } + ], + "web_accessible_resources": ["injection.js"], + "externally_connectable": { "matches": ["*://*.grepolis.com/game/index*"] }, + "homepage_url": "https://git.fuwafuwa.moe/lesderid/grepolis-bot", + "offline_enabled": false, + "permissions": ["tabs", "contextMenus"], + "short_name": "Grepolis Bot" +}