├── .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 [![Build Status](https://travis-ci.org/snollygolly/sourceio-automation.svg?branch=master)](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 |
402 |
403 | Source.io Bot 404 | 405 | 406 | 407 |
408 |
409 |
410 | Restart Bot 411 |
412 |
413 | Stop Bot 414 |
415 |
416 | Target Auto 417 |
418 |
419 | Port Attack Auto 420 |
421 | Message to victim: 422 |
423 | 424 |

425 | ${freqInput("word")} 426 | ${freqInput("mine")} 427 | ${freqInput("upgrade")} 428 | ${freqInput("hack")} 429 |
430 | This script is on Github! 431 |
432 |
433 |
`; 434 | $(".window-wrapper").append(botWindowHTML); 435 | // color the toggle buttons 436 | $("#custom-autoTarget-button").css("color", config.autoTarget ? "green" : "red"); 437 | $("#custom-autoAttack-button").css("color", config.autoAttack ? "green" : "red"); 438 | // bind functions to the gui's buttons 439 | $("#custom-gui-bot-title > span.window-close-style").on("click", () => { 440 | $("#custom-gui").hide(); 441 | }); 442 | $("#custom-restart-button").on("click", () => { 443 | app.restart(); 444 | }); 445 | $("#custom-stop-button").on("click", () => { 446 | app.stop(); 447 | }); 448 | $("#custom-autoTarget-button").on("click", () => { 449 | config.autoTarget = !config.autoTarget; 450 | $("#custom-autoTarget-button").css("color", config.autoTarget ? "green" : "red"); 451 | }); 452 | $("#custom-autoAttack-button").on("click", () => { 453 | config.autoAttack = !config.autoAttack; 454 | $("#custom-autoAttack-button").css("color", config.autoAttack ? "green" : "red"); 455 | }); 456 | $("#custom-github-button").on("click", () => { 457 | window.open("https://github.com/snollygolly/sourceio-automation"); 458 | }); 459 | $(".custom-gui-freq").on("keypress", (e) => { 460 | if (e.keyCode !== 13) { 461 | return; 462 | } 463 | const type = $(e.target).attr("data-type"); 464 | if (!config.freq[type]) { 465 | // invalid input, disregard i guess? 466 | return; 467 | } 468 | config.freq[type] = $(e.target).val(); 469 | log(`* Frequency for '${type}' set to ${config.freq[type]}`); 470 | }); 471 | $(".custom-gui-msg").on("keypress", (e) => { 472 | if (e.keyCode !== 13) { 473 | return; 474 | } 475 | config.message = $(e.target).val(); 476 | log(`* Message for set to : ${config.message}`); 477 | }); 478 | // make the bot window draggable 479 | const botWindow = ("#custom-gui"); 480 | $(document).on("mousedown", botWindow, (e) => { 481 | vars.gui.dragReady = true; 482 | vars.gui.dragOffset.x = e.pageX - $(botWindow).position().left; 483 | vars.gui.dragOffset.y = e.pageY - $(botWindow).position().top; 484 | }); 485 | $(document).on("mouseup", botWindow, () => { 486 | vars.gui.dragReady = false; 487 | }); 488 | $(document).on("mousemove", (e) => { 489 | if (vars.gui.dragReady) { 490 | $(botWindow).css("top", `${e.pageY - vars.gui.dragOffset.y}px`); 491 | $(botWindow).css("left", `${e.pageX - vars.gui.dragOffset.x}px`); 492 | } 493 | }); 494 | } 495 | }; 496 | 497 | function checkFirewallsUpgrades(FW, index) { 498 | if (index === 3) 499 | return true; 500 | return FW.needUpgrade === false; 501 | } 502 | 503 | function parseHackProgress(progress) { 504 | // remove the %; 505 | const newProgress = progress.slice(0, -2); 506 | const newProgressParts = newProgress.split("width: "); 507 | return parseInt(newProgressParts.pop()); 508 | } 509 | 510 | function getRandomInt(min, max) { 511 | return Math.floor(Math.random() * (max - min + 1)) + min; 512 | } 513 | 514 | function getHashCode(data) { 515 | let hash = 0; 516 | if (data.length === 0) { 517 | return hash; 518 | } 519 | for (let i = 0; i < data.length; i++) { 520 | const c = data.charCodeAt(i); 521 | hash = ((hash << 5) - hash) + c; 522 | hash &= hash; 523 | } 524 | return hash.toString(); 525 | } 526 | 527 | function toDataURL(url) { 528 | return fetch(url) 529 | .then(response => response.blob()) 530 | .then(blob => new Promise((resolve, reject) => { 531 | const reader = new FileReader(); 532 | reader.onloadend = () => resolve(reader.result); 533 | reader.onerror = reject; 534 | reader.readAsDataURL(blob); 535 | })); 536 | } 537 | 538 | function log(message) { 539 | console.log(`:: ${message}`); 540 | } 541 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sourceio-automation", 3 | "version": "2.0.0", 4 | "description": "A bot to play s0urce.io for you", 5 | "main": "main.js", 6 | "scripts": { 7 | "test": "./node_modules/.bin/eslint ." 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/snollygolly/sourceio-automation.git" 12 | }, 13 | "author": "@snollygolly", 14 | "license": "MIT", 15 | "bugs": { 16 | "url": "https://github.com/snollygolly/sourceio-automation/issues" 17 | }, 18 | "homepage": "https://github.com/snollygolly/sourceio-automation#readme", 19 | "dependencies": { 20 | "eslint": "^4.2.0" 21 | } 22 | } 23 | --------------------------------------------------------------------------------