├── .eslintrc
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── db.json
├── main.js
└── package.json
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "es6": true
4 | },
5 |
6 | "rules": {
7 | "comma-dangle": 2,
8 | "no-underscore-dangle": 0,
9 | "guard-for-in": 2,
10 | "no-else-return": 2,
11 | "no-self-compare": 2,
12 | "no-throw-literal": 2,
13 | "wrap-iife": [2, "outside"],
14 | "no-catch-shadow": 2,
15 | "indent": [2, "tab"],
16 | "consistent-this": [2, "self"],
17 | "func-names": 2,
18 | "no-inline-comments": 2,
19 | "max-nested-callbacks": [2, 3],
20 | "new-cap": 2,
21 | "new-parens": 2,
22 | "no-array-constructor": 2,
23 | "no-multiple-empty-lines": 2,
24 | "no-nested-ternary": 2,
25 | "one-var": [2, "never"],
26 | "operator-assignment": [2, "always"],
27 | "quotes": [2, "double", "avoid-escape"],
28 | "semi": [2, "always"],
29 | "spaced-comment": [1, "always"],
30 | "keyword-spacing": 2,
31 | "space-infix-ops": 2,
32 | "space-in-parens": [2, "never"],
33 | "space-before-function-paren": [2, "never"],
34 | "space-before-blocks": [2, "always"],
35 | "no-var": 2,
36 | "prefer-const": 1,
37 | "prefer-template": 1,
38 | "require-yield": 1,
39 | "arrow-spacing": 2,
40 | "generator-star-spacing": [2, "after"],
41 | "no-confusing-arrow": 2,
42 | "no-const-assign": 2,
43 | "brace-style": 1
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | output
3 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "6.10.0"
4 | script:
5 | - "npm test"
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 @snollygolly
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # sourceio-automation [](https://travis-ci.org/snollygolly/sourceio-automation)
2 |
3 | A bot to automatically play s0urce.io for you
4 |
5 | ### Note: As of Beta 2.2, OCR is unreliable and disabled by default
6 |
7 | ## Configuration
8 |
9 | The main file (`main.js`) contains several values you may want to change. They are grouped together inside of a `config` object at the top of the code. All of them are commented, so be sure to read the comments before changing any of the values.
10 |
11 | ## How To Use
12 |
13 | > Note: It's recommended that you use Google Chrome
14 |
15 | * Go to [http://s0urce.io/](s0urce.io) and start a game.
16 | * Open the Console (Under View -> Developer -> JavaScript Console)
17 | * Paste in the full contents of `main.js`
18 | * Type `app.start()` to start the automated bot
19 | * If you need to stop, you can type `app.stop()`
20 |
21 | ## Contributing
22 |
23 | If you want to contribute, that would be awesome! Please make sure if you're contributing, you're following the following guidelines:
24 |
25 | * Don't submit PRs to change sample configuration options. For instance, if you don't like the message I use by default, feel free to fork my repository and change it, but a PR with a changed message won't be merged.
26 | * Make sure to lint your code before submitting a PR. We use ESLint for our linting, so all you need to do is run `npm install` and then `npm run test`. If you see any errors, fix them before submitting your PR.
27 |
--------------------------------------------------------------------------------
/db.json:
--------------------------------------------------------------------------------
1 | {
2 | "-2022313631" : "get",
3 | "668315514" : "add",
4 | "-1626066211" : "dir",
5 | "-2118249843" : "list",
6 | "19874074" : "file",
7 | "-2017827979" : "ghost",
8 | "1059114713" : "http",
9 | "1144523139" : "pass",
10 | "-505734822" : "ping",
11 | "1557099499" : "port",
12 | "-105754243" : "write",
13 | "-596486220" : "remove",
14 | "1025302195" : "anon",
15 | "1647872193" : "user",
16 | "-1411454256" : "bytes",
17 | "2127289026" : "socket",
18 | "1768556497" : "net",
19 | "1683542193" : "val",
20 | "-1138367387" : "count",
21 | "456062031" : "point",
22 | "1420184963" : "event",
23 | "-418857538" : "reset",
24 | "-1672823040" : "com",
25 | "979066172" : "signal",
26 | "1129543136" : "load",
27 | "1136257323" : "handle",
28 | "-29353888" : "key",
29 | "78482505" : "stat",
30 | "-92886755" : "buffer",
31 | "514120436" : "send",
32 | "1302383528" : "host",
33 | "1788506574" : "delete",
34 | "-1616694298" : "bit",
35 | "-23912487" : "size",
36 | "-1249268365" : "upload",
37 | "1653524095" : "poly",
38 | "-1468886921" : "temp",
39 | "-626053887" : "system",
40 | "1719618689" : "root",
41 | "-1156711839" : "client",
42 | "567102362" : "data",
43 | "-81054603" : "init",
44 | "267308791" : "info",
45 | "-141168411" : "xml",
46 | "1672574785" : "log",
47 | "605282908" : "loop",
48 | "979636581" : "global",
49 | "657256265" : "cookies",
50 | "-1345603115" : "part",
51 | "-679234549" : "emit",
52 | "-1587204252" : "set",
53 | "98536489" : "url",
54 | "-1308065871" : "call",
55 | "-1266608331" : "type",
56 | "398561552" : "add",
57 | "-193204407" : "left",
58 | "2091854663" : "join",
59 | "1684232653" : "status",
60 | "-1628499386" : "right",
61 | "419398556" : "num",
62 | "-287565771" : "domain",
63 | "-1728017658" : "intel",
64 |
65 | "974918423" : "constructor",
66 | "47175883" : "encryptfile",
67 | "-1064389816" : "getpass",
68 | "1442699894" : "responder",
69 | "-1164465758" : "setstats",
70 | "-1127904142" : "encrypt",
71 | "901243531" : "newline",
72 | "1733767903" : "mysql",
73 | "670312741" : "writefile",
74 | "1609121367" : "eventtype",
75 | "-406197419" : "accountname",
76 | "1856168944" : "download",
77 | "1226991364" : "setcookie",
78 | "-945581080" : "setnewid",
79 | "599598412" : "command",
80 | "1207346080" : "thread",
81 | "-1222053453" : "process",
82 | "1183279706" : "listconfig",
83 | "907181842" : "syscall",
84 | "-508873827" : "proxy",
85 | "-1354700300" : "filedir",
86 | "-196679888" : "newhost",
87 | "1277154470" : "getid",
88 | "-1461353192" : "server",
89 | "-683593749" : "number",
90 | "1567951055" : "sizeof",
91 | "15311015" : "module",
92 | "1939344114" : "urlcheck",
93 | "-836893237" : "gridwidth",
94 | "-2012064394" : "decrypt",
95 | "1224854595" : "datatype",
96 | "-294405445" : "config",
97 | "-39918655" : "getinfo",
98 | "-1476080363" : "userport",
99 | "1372243801" : "hostserver",
100 | "-553981589" : "account",
101 | "589336711" : "generate",
102 | "-2131859196" : "filetype",
103 | "-700611194" : "decryptfile",
104 | "-179602858" : "setport",
105 | "-317210335" : "threat",
106 | "1150909553" : "package",
107 | "-1896953293" : "userid",
108 | "1297139737" : "loadbytes",
109 | "-689028013" : "channel",
110 | "-934078508" : "hexagon",
111 | "-222362882" : "disconnect",
112 | "1472847174" : "getfile",
113 | "1987630692" : "encode",
114 | "-2047298844" : "protocol",
115 | "121093601" : "password",
116 | "-487602046" : "getkey",
117 | "1594121102" : "serverproxy",
118 | "1638349176" : "gridheight",
119 | "1116412930" : "getping",
120 | "-32035591" : "getlog",
121 | "-900965004" : "export",
122 | "-1327076929" : "connect",
123 | "-517864587" : "newserver",
124 | "-440931039" : "findpackage",
125 | "1987315504" : "length",
126 | "-1172656994" : "vector",
127 | "1297084254" : "fillgrid",
128 | "348636898" : "username",
129 | "-616163997" : "response",
130 | "-1986603776" : "setping",
131 |
132 | "861244767" : "joinnetworkclient",
133 | "-1941630950" : "unpacktmpfile",
134 | "2044041787" : "getdatapassword",
135 | "2114328775" : "bufferpingset",
136 | "1852667296" : "emitconfiglist",
137 | "148244845" : "rootcookieset",
138 | "847167870" : "setnewproxy",
139 | "1159860793" : "sizeofhexagon",
140 | "-75721908" : "getmysqldomain",
141 | "968556280" : "callmodule",
142 | "1338515272" : "getfirewallchannel",
143 | "902745501" : "changeusername",
144 | "-1695566202" : "httpbuffersize",
145 | "-819294973" : "removeoldcookie",
146 | "1956925611" : "fileexpresslog",
147 | "-2135624083" : "getpartoffile",
148 | "-410733505" : "disconnectserver",
149 | "-1100099911" : "batchallfiles",
150 | "-1008193379" : "exportconfigpackage",
151 | "985712586" : "statusofprocess",
152 | "938919465" : "getxmlprotocol",
153 | "-363307462" : "loadloggedpassword",
154 | "-841313941" : "sendintelpass",
155 | "101243014" : "eventlistdir",
156 | "1905636872" : "changepassword",
157 | "1036824887" : "uploaduserstats",
158 | "-486089412" : "decryptdatabatch",
159 | "-414160473" : "loadregisterlist",
160 | "-1136603235" : "systemgridtype",
161 | "-1274291847" : "encodenewfolder",
162 | "-1758655872" : "encryptunpackedbatch",
163 | "-797896268" : "destroybatch",
164 | "-1293468912" : "dodecahedron",
165 | "782829474" : "respondertimeout",
166 | "1346956584" : "channelsetpackage",
167 | "1770626869" : "checkhttptype",
168 | "401153668" : "tempdatapass",
169 | "1999688087" : "blockthreat",
170 | "130603323" : "deleteallids",
171 | "1605981605" : "removenewcookie",
172 | "618676639" : "ghostfilesystem",
173 | "-2089156425" : "includedirectory",
174 | "648763823" : "create3axisvector",
175 | "-1811003021" : "systemportkey",
176 | "-580269682" : "mergesocket",
177 | "1726234710" : "patcheventlog",
178 | "-1898909613" : "createnewpackage",
179 | "1882426025" : "disconnectchannel",
180 | "-1505677585" : "createfilethread",
181 | "854884272" : "wordcounter",
182 | "2146605725" : "create2axisvector",
183 | "-745659096" : "generatecodepack",
184 | "-1279135495" : "createnewsocket",
185 | "1171225457" : "hostnewserver",
186 | "248513203" : "loadaltevent"
187 | }
188 |
--------------------------------------------------------------------------------
/main.js:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line prefer-const, one-var
2 | let config, vars, app, loops, gui;
3 |
4 | // eslint-disable-next-line prefer-const
5 | config = {
6 | // the message you send to others when you hack them
7 | message: "papa bless, it's everyday bro /r/javascript",
8 | autoTarget: true,
9 | autoAttack: true,
10 | // the base64 database url
11 | db: "https://raw.githubusercontent.com/snollygolly/sourceio-automation/master/db.json",
12 | // all things timing related
13 | freq: {
14 | // how often to guess
15 | word: 1500,
16 | // how often to attempt to upgrade mining tools
17 | mine: 3000,
18 | // how often to attempt to upgrade firewalls
19 | upgrade: 4500,
20 | // how long to wait before attempting to rehack, not enough money for hack
21 | broke: 6000,
22 | // how long to wait before restarting the hacking loop
23 | hack: 3500
24 | },
25 | // which player in the index of the list, 0 is the first player (the bot target a player with index between playerToAttack and playerToAttack + 3 (random).
26 | playerToAttack: 0,
27 | // how many hacks to try (and fail) before restarting
28 | maxHackFails: 5,
29 | // how high to upgrade all of your miner types except quantum-servers and botnets.
30 | maxMinerLevel: 20,
31 | // how high to upgrade quantum-servers and botnets (quantum-servers will always be purchased in priority and botnets quantity will be equal to quantum-servers quantity.
32 | maxQBLevel: 50,
33 | // the max BTC the bot will spend per upgrade. (current BTC * maxUpgradeCost).
34 | maxUpgradeCost: .33,
35 | // all the gui settings
36 | gui: {
37 | enabled: true,
38 | width: "320px",
39 | height: "412px"
40 | },
41 | // all the ocr settings, disabled by default
42 | ocr: {
43 | enabled: false,
44 | url: "http://api.ocr.space/parse/image",
45 | key: "XXX"
46 | }
47 | };
48 |
49 | // eslint-disable-next-line prefer-const
50 | vars = {
51 | // the object that contains a mapping of image urls to words (built over time)
52 | listingURL: {},
53 | // the object that contains b64 hashes to words (loaded on start)
54 | listingB64: {},
55 | // how much BT you have
56 | balance: 0,
57 | flags: {
58 | // we're waiting for OCR to complete
59 | ocrBlock: false,
60 | // we're waiting for the bar to move in response to our word
61 | progressBlock: false
62 | },
63 | loops: {
64 | word: null,
65 | upgrade: null,
66 | miner: null
67 | },
68 | hackProgress: 0,
69 | hackFailures: 0,
70 | // the different types of miners and their current rank
71 | minerStatus: [
72 | { name: "shop-basic-miner", value: 0 },
73 | { name: "shop-advanced-miner", value: 0 },
74 | { name: "shop-mining-drill", value: 0 },
75 | { name: "shop-data-center", value: 0 },
76 | { name: "shop-bot-net", value: 0 },
77 | { name: "shop-quantum-server", value: 0 }
78 | ],
79 | fireWall: [
80 | { name: "A", index: 1, needUpgrade: true },
81 | { name: "B", index: 2, needUpgrade: true },
82 | { name: "C", index: 3, needUpgrade: true },
83 | { name: "ALL", needUpgrade: true }
84 | ],
85 | gui: {
86 | dragReady: false,
87 | dragOffset: { x: 0, y: 0 }
88 | }
89 | };
90 |
91 | // eslint-disable-next-line prefer-const
92 | app = {
93 | start: () => {
94 | $.get(config.db).done((data) => {
95 | vars.listingB64 = JSON.parse(data);
96 | // first check the windows are open, and open them if they aren't
97 | if ($("#player-list").is(":visible") === false) {
98 | log("* Target list must be open");
99 | $("#desktop-list").children("img").click();
100 | }
101 | if ($("#window-shop").is(":visible") === false) {
102 | log("* Black market must be open");
103 | $("#desktop-shop").children("img").click();
104 | $("#desktop-miner").children("img").click();
105 | }
106 | if ($("#window-computer").is(":visible") === false) {
107 | log("* My computer must be open");
108 | $("#desktop-computer").children("img").click();
109 | }
110 | if (config.gui.enabled === true) {
111 | log("* Opening bot window");
112 | if ($("#custom-gui").length > 0) {
113 | $("#custom-gui").show();
114 | } else {
115 | gui.show();
116 | }
117 | } else {
118 | log("* GUI disabled, skipping...");
119 | }
120 | // start the automation
121 | app.automate();
122 | });
123 | },
124 |
125 | restart: () => {
126 | app.stop();
127 | log(". Waiting for restart...");
128 | setTimeout(() => {
129 | log(". Restarting!");
130 | app.automate();
131 | }, config.freq.hack);
132 | },
133 |
134 | stop: () => {
135 | // check and disable all loops
136 | for (const loop in vars.loops) {
137 | if (vars.loops[loop] === null) {
138 | log(`! Can't stop ${loop} loop`);
139 | continue;
140 | }
141 | clearInterval(vars.loops[loop]);
142 | vars.loops[loop] = null;
143 | }
144 | vars.hackProgress = 0;
145 | // reset flags
146 | vars.flags.ocrBlock = false;
147 | vars.flags.progressBlock = false;
148 | log("* Stopped all hacking");
149 | },
150 |
151 | automate: () => {
152 | // does everything to prep for hacking except word guessing
153 | app.attack();
154 | if (vars.loops.miner === null) {
155 | // start the loop for btc monitoring
156 | vars.loops.miner = setInterval(loops.miner, config.freq.mine);
157 | }
158 | if (vars.loops.upgrade === null) {
159 | // start the loop for upgrades
160 | vars.loops.upgrade = setInterval(loops.upgrade, config.freq.upgrade);
161 | }
162 | },
163 |
164 | attack: () => {
165 |
166 | // if the auto target is toggled, choose the target.
167 | if (config.autoTarget) {
168 | // with playerToAttack = 0 choose between the 4 first players from the player list
169 | const rndTarget = getRandomInt(config.playerToAttack, config.playerToAttack + 3);
170 | // playerToAttack is an int, the index of the player list
171 | const targetName = $("#player-list").children("tr").eq(rndTarget)[0].innerText;
172 | log(`. Now attacking ${targetName}`);
173 | // click it, and then hack, and then a random port
174 | $("#player-list").children("tr").eq(rndTarget)[0].click();
175 | $("#window-other-button").click();
176 | }
177 | // if the auto attack port is toggled, choose the port and click
178 | if (config.autoAttack) {
179 | const portNumber = getRandomInt(1, 3);
180 | // do a check for money
181 | const portStyle = $(`#window-other-port${portNumber}`).attr("style");
182 | if (portStyle.indexOf("opacity: 1") === -1) {
183 | // this port costs too much, let's wait a bit
184 | log("* Hack too expensive, waiting");
185 | setTimeout(app.attack, config.freq.broke);
186 | return;
187 | }
188 | $(`#window-other-port${portNumber}`).click();
189 | }
190 | if (vars.loops.word === null) {
191 | vars.loops.word = setInterval(loops.word, config.freq.word);
192 | }
193 | },
194 |
195 | findWord: () => {
196 | const wordLink = $(".tool-type-img").prop("src");
197 | if (!wordLink.endsWith("s0urce.io/client/img/words/template.png")) {
198 | if (vars.listingURL.hasOwnProperty(wordLink) === true) {
199 | const word = vars.listingURL[wordLink];
200 | log(`. Found word (URL): [${word}]`);
201 | app.submit(word);
202 | return;
203 | }
204 | toDataURL(wordLink).then((dataUrl) => {
205 | const hash = getHashCode(dataUrl);
206 | if (vars.listingB64.hasOwnProperty(hash) === true) {
207 | const word = vars.listingB64[hash];
208 | log(`. Found word (B64): [${word}]`);
209 | app.learn(word);
210 | return;
211 | }
212 | if (config.ocr.enabled === true) {
213 | log("* Not seen, trying OCR...");
214 | app.doOCR(config.ocr.url, {
215 | apikey: config.ocr.key,
216 | language: "eng",
217 | url: wordLink
218 | });
219 | } else {
220 | log("* OCR disabled, skipping...");
221 | }
222 | });
223 | } else {
224 | log("* Can't find the word link...");
225 | // if the target is disconnected and autoTarget disabled, re-enable it.
226 | if ($("#cdm-text-container span:last").text() === "Target is disconnected from the Server." && !config.autoTarget) {
227 | $("#custom-autoTarget-button").click();
228 | }
229 | app.restart();
230 | }
231 | },
232 |
233 | learn: (word) => {
234 | const wordLink = $(".tool-type-img").prop("src");
235 | vars.listingURL[wordLink] = word;
236 | app.submit(word);
237 | },
238 |
239 | submit: (word) => {
240 | $("#tool-type-word").val(word);
241 | $("#tool-type-word").submit();
242 | },
243 |
244 | doOCR: (link, payload) => {
245 | vars.flags.ocrBlock = true;
246 | // this is made somewhat generic to allow different ocr vendors
247 | $.post(link, payload).done((data) => {
248 | const word = String(data["ParsedResults"][0]["ParsedText"]).trim().toLowerCase().split(" ").join("");
249 | if (word.length > 2) {
250 | log(`. Got data: [${word}]`);
251 | $("#tool-type-word").val(word);
252 | app.learn(word);
253 | vars.flags.ocrBlock = false;
254 | } else {
255 | log("* OCR failed");
256 | app.restart();
257 | }
258 | });
259 | }
260 | };
261 |
262 | loops = {
263 | word: () => {
264 | // block is true is we're mid-OCR
265 | if (vars.flags.ocrBlock === true) {
266 | return;
267 | }
268 | if ($("#targetmessage-input").is(":visible") === true) {
269 | // we're done!
270 | $("#targetmessage-input").val(config.message);
271 | $("#targetmessage-button-send").click();
272 | app.restart();
273 | return;
274 | }
275 | // if we're waiting on the progress bar to move...
276 | if (vars.flags.progressBlock === true) {
277 | const newHackProgress = parseHackProgress($("#progressbar-firewall-amount").attr("style"));
278 | // check to see if it's new
279 | if (vars.hackProgress === newHackProgress) {
280 | // the bar hasn't moved
281 | log("* Progress bar hasn't moved, waiting");
282 | vars.hackFails++;
283 | if (vars.hackFails >= config.maxHackFails) {
284 | vars.hackFails = 0;
285 | log("* Progress bar is stuck, restarting");
286 | // maybe the URLs have changed
287 | vars.listingURL = {};
288 | app.restart();
289 | }
290 | return;
291 | }
292 | // the bar has moved
293 | vars.hackFails = 0;
294 | vars.hackProgress = newHackProgress;
295 | vars.flags.progressBlock = false;
296 | }
297 | // actually do the word stuff
298 | vars.flags.progressBlock = true;
299 | app.findWord();
300 | },
301 |
302 | miner: () => {
303 | // first, get the status of our miners
304 | for (const miner of vars.minerStatus) {
305 | // set value
306 | miner.value = parseInt($(`#${miner.name}-amount`).text());
307 | // this is available to buy
308 | if ($(`#${miner.name}`).attr("style") === "opacity: 1;") {
309 | // buy more quantum servers and botnets, buy botnets at the same rate as the quantum servers.
310 | if (miner.value >= config.maxQBLevel) {
311 | // we're beyond or at the max QB level, no updates needed
312 | continue;
313 | }
314 | // is this an advanced miner?
315 | const isAdvancedMiner = (miner.name === "shop-quantum-server" || miner.name === "shop-bot-net") ? true : false;
316 | if (miner.value >= config.maxMinerLevel && isAdvancedMiner === false) {
317 | // this isn't an advanced miner and it's beyond the max level, no updates needed
318 | continue;
319 | }
320 | // we should buy this
321 | $(`#${miner.name}`).click();
322 | }
323 | }
324 | },
325 |
326 | upgrade: () => {
327 | // leave if all firewalls are upgraded to max
328 | if (!vars.fireWall[3].needUpgrade)
329 | return;
330 | // get a random firewall
331 | // i refers to the location in the vars.firewall array
332 | const i = getRandomInt(0, 2);
333 | // index refers to 1,2,3, the index in the DOM (use for selectors)
334 | const index = vars.fireWall[i].index;
335 | // if this fireWall is already fully upgraded, get an other random firewall.
336 | if (!vars.fireWall[i].needUpgrade)
337 | vars.loops.upgrade();
338 | vars.balance = parseInt($("#window-my-coinamount").text());
339 | // if the back button is visible, we're on a page, let's back out and hide the firewall warning.
340 | if ($("#window-firewall-pagebutton").is(":visible") === true) {
341 | $("#tutorial-firewall").css("display", "none");
342 | $("#window-firewall-pagebutton").click();
343 | }
344 |
345 | // click on the firewall
346 | log(`. Handling upgrades to firewall ${vars.fireWall[i].name}`);
347 | $(`#window-firewall-part${index}`).click();
348 | // get stats
349 | const stats = [
350 | parseInt($("#shop-max-charges").text()), parseInt($("#shop-strength").text()), parseInt($("#shop-regen").text())
351 | ];
352 | const statLookup = [
353 | "max_charge10", "difficulty", "regen"
354 | ];
355 | const maxStats = [
356 | 30, 4, 10
357 | ];
358 | let maxUpgradeCount = 0;
359 | for (const stat in maxStats) {
360 | if (stats[stat] < maxStats[stat]) {
361 | const statPrice = parseInt($(`#shop-firewall-${statLookup[stat]}-value`).text());
362 | if (statPrice < (vars.balance * config.maxUpgradeCost)) {
363 | log(`. Buying: ${$(".window-shop-element-info b").eq(stat).text()}`);
364 | $(`#shop-firewall-${statLookup[stat]}`).click();
365 | // buy more than one upgrade, but only if they cost less than a third of the bitcoin balance.
366 | // return;
367 | }
368 | } else {
369 | maxUpgradeCount++;
370 | if (maxUpgradeCount === 3) {
371 | vars.fireWall[i].needUpgrade = false;
372 | if (vars.fireWall.every(checkFirewallsUpgrades))
373 | vars.fireWall[3].needUpgrade = false;
374 | }
375 | }
376 | }
377 | // let's go back
378 | if ($("#window-firewall-pagebutton").is(":visible") === true) {
379 | $("#window-firewall-pagebutton").click();
380 | }
381 | }
382 | };
383 |
384 | gui = {
385 | show: () => {
386 | const sizeCSS = `height: ${config.gui.height}; width: ${config.gui.width};`;
387 | const labelMap = {
388 | word: "Word Speed",
389 | mine: "Miner Upgrade",
390 | upgrade: "Firewall Upgrade",
391 | hack: "Hack Wait"
392 | };
393 | const freqInput = (type) => {
394 | return `
395 | ${labelMap[type]}:
396 |
397 | (ms)
398 | `;
399 | };
400 | const botWindowHTML = `
401 |