From a804f6ecb31aa0817b33203dee06cf44405dc5c4 Mon Sep 17 00:00:00 2001 From: Les De Ridder Date: Sun, 5 Nov 2017 04:18:36 +0100 Subject: [PATCH] Initial commit --- .gitignore | 1 + background.js | 178 ++++++++++++++++++++++++++++++++++++++++++++++++++ content.js | 9 +++ icon128.png | Bin 0 -> 710 bytes icon16.png | Bin 0 -> 488 bytes icon48.png | Bin 0 -> 684 bytes injection.js | 117 +++++++++++++++++++++++++++++++++ manifest.json | 28 ++++++++ 8 files changed, 333 insertions(+) create mode 100644 .gitignore create mode 100644 background.js create mode 100644 content.js create mode 100644 icon128.png create mode 100644 icon16.png create mode 100644 icon48.png create mode 100644 injection.js create mode 100644 manifest.json 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 0000000000000000000000000000000000000000..a557cf4f0b05f17f1bdcce62dd8fd434b0df0d1e GIT binary patch literal 710 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H3?#oinD`S&v7|ftIx;Y9?C1WI$O_~uBzpw; zGB8xB0oAoIF#H0kf5E^|YQVtoDuIE)Y6b&?c)^@qfi^&ijR8I(uJ>04*7>m7rA+h6 zo}5?LrQz4$99MgCP43St-LKE|{J+t)XGxy2N2Osv#g8jpZw?ns>?(M2qDS7TaP^F= z|7X&yg9~)M3+DD`OWUM>Je+oAW15s@;<4qa=H5x$=cTNinj&Qp|7d6GvE?b!<}usn zCWpktA6uR*Z5rVg7=3D%Ye;mArd{O4`EJLSB}p3xy9I=ApOdhAg3Fd3=R;FnS5AsA z%#E`5iIg@9SX`vm6D#`v|No;r;hTVtXH4>Tcd_#0JOc6}dx@v7EBixkCP7`vsT&rQ z0;PpKT^vIyZoR$Y>CfaS;E-5wm{qjUInXh%@c#Ax|Bvt4voFL*(pTj4kA06r%AbBs zmE!euDn7(xaG+riW3cHm!{CMQ_A#>9;$yg)5~7-QEYGtYi;?ZzRqM|T*Qc}~b*ILude@wXa8gWW0q4F11dzbzbg zhRYc^Smrk{vOVFKSfpchnC}Q!>*kach{7+&2fPfx*+&&t;ucLK6UmKo9-^ literal 0 HcmV?d00001 diff --git a/icon16.png b/icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..948313c2cda876be451796a9550a2d66875040ef GIT binary patch literal 488 zcmVP)KJNd2&b{Xzt@8S=65l9`mCkhPV+RLjKj*S8-2?Es)&uLIE2EqHQ!hf95VK*Mr00CJgq zK7L(i^o`MZ1pwc3(a%WUmkjo|_qa0AP+)+u8#W_@d3>)CLn?y6a~Qwll5(2deBMRV za$}WVeTGc3Lg1bL7r_Cy`X%NDRDanW57zYH(OZBZ3Usv*Na3NUlUN55DSQR&H8dWN zBpAAwz_MX>Iixr(R6e0B83J&@XxS}YYX}~^j0ny3km8iD&c+BdDVY5x=*?;j6e9ny zwyL!GhdT%Zp3OKcELJED*0@@TVuS&DO0fLB3R0?A#~2BH%%46J9}J4aX-d~N*u5-q z1Bsqa@MG^hbBhjy{?Y&7kC;%h1M99S+3dty${-nxADuo$?8hw eSYZ8ZH@^WQPrNz7YzMIb0000!lvI6-E$sR$z z3=CCj3=9n|3=F@3LJcn%7)pT}UnMXwSj}Ky5HFasE6@fgu`$3W#P$Blz&amRyOe2O z*^~3?x-|S6oa1UwuF3s*rTg`np8q$x_AJR$_NX)rsQ7WE>&@YUiCqOxPV~q-6|SC< z_5Vzob#Q^Mcfs8LY-yYHkB8H)Y)q4~Ogy$c)!aL2`@EEuQ&XfY;velyJ+?eW+B|0a z+~km$_+!hHrA;H;0;5mOat(=&(X@-aIN$BqvLtEaV7Gwq?Q;@#PjK1N<9ukU>&i*- zg}G7oK9SN!0gH>&dSXTY|NnoKCwvpo@r+5{?k-k-oJT-jWH0gbb!C6Z%_OKRId#K= zQlQWePZ!4!kK<>rUiWJW5NLQ9Dl%o$mwVPhOSJd=@2)(=DcxiE@L1vg)ro5MoZ&Hg z1Qib=V;RC zs$k<&@tl+z87LAOx|G@Xa!KrQ?7l?l*qR92MXsuFVeVYmKynN_NrB{ zcIC}tvu9#|KYLs5?sd+-_rD)D=y`16d)emvk6-$XpFSRb%OX=Vk3sUz?9|9}|Ce7c z{rmd?_pMnoXW74*z8x3_swJ)wB`Jv|saDBFsX&Us$iUEC*U(Vcz$nDf%*xcn%EVOL tz|hLTfKj$P5=BF9eoAIqC7K2kQ!68=h8f9ybATEcJYD@<);T3K0RRD(5DowU literal 0 HcmV?d00001 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" +}