-
Notifications
You must be signed in to change notification settings - Fork 27
/
Copy pathmain.js
540 lines (517 loc) · 16.7 KB
/
main.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
// eslint-disable-next-line prefer-const, one-var
let config, vars, app, loops, gui;
// eslint-disable-next-line prefer-const
config = {
// the message you send to others when you hack them
message: "papa bless, it's everyday bro /r/javascript",
autoTarget: true,
autoAttack: true,
// the base64 database url
db: "https://raw.githubusercontent.com/snollygolly/sourceio-automation/master/db.json",
// all things timing related
freq: {
// how often to guess
word: 1500,
// how often to attempt to upgrade mining tools
mine: 3000,
// how often to attempt to upgrade firewalls
upgrade: 4500,
// how long to wait before attempting to rehack, not enough money for hack
broke: 6000,
// how long to wait before restarting the hacking loop
hack: 3500
},
// 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).
playerToAttack: 0,
// how many hacks to try (and fail) before restarting
maxHackFails: 5,
// how high to upgrade all of your miner types except quantum-servers and botnets.
maxMinerLevel: 20,
// 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.
maxQBLevel: 50,
// the max BTC the bot will spend per upgrade. (current BTC * maxUpgradeCost).
maxUpgradeCost: .33,
// all the gui settings
gui: {
enabled: true,
width: "320px",
height: "412px"
},
// all the ocr settings, disabled by default
ocr: {
enabled: false,
url: "http://api.ocr.space/parse/image",
key: "XXX"
}
};
// eslint-disable-next-line prefer-const
vars = {
// the object that contains a mapping of image urls to words (built over time)
listingURL: {},
// the object that contains b64 hashes to words (loaded on start)
listingB64: {},
// how much BT you have
balance: 0,
flags: {
// we're waiting for OCR to complete
ocrBlock: false,
// we're waiting for the bar to move in response to our word
progressBlock: false
},
loops: {
word: null,
upgrade: null,
miner: null
},
hackProgress: 0,
hackFailures: 0,
// the different types of miners and their current rank
minerStatus: [
{ name: "shop-basic-miner", value: 0 },
{ name: "shop-advanced-miner", value: 0 },
{ name: "shop-mining-drill", value: 0 },
{ name: "shop-data-center", value: 0 },
{ name: "shop-bot-net", value: 0 },
{ name: "shop-quantum-server", value: 0 }
],
fireWall: [
{ name: "A", index: 1, needUpgrade: true },
{ name: "B", index: 2, needUpgrade: true },
{ name: "C", index: 3, needUpgrade: true },
{ name: "ALL", needUpgrade: true }
],
gui: {
dragReady: false,
dragOffset: { x: 0, y: 0 }
}
};
// eslint-disable-next-line prefer-const
app = {
start: () => {
$.get(config.db).done((data) => {
vars.listingB64 = JSON.parse(data);
// first check the windows are open, and open them if they aren't
if ($("#player-list").is(":visible") === false) {
log("* Target list must be open");
$("#desktop-list").children("img").click();
}
if ($("#window-shop").is(":visible") === false) {
log("* Black market must be open");
$("#desktop-shop").children("img").click();
$("#desktop-miner").children("img").click();
}
if ($("#window-computer").is(":visible") === false) {
log("* My computer must be open");
$("#desktop-computer").children("img").click();
}
if (config.gui.enabled === true) {
log("* Opening bot window");
if ($("#custom-gui").length > 0) {
$("#custom-gui").show();
} else {
gui.show();
}
} else {
log("* GUI disabled, skipping...");
}
// start the automation
app.automate();
});
},
restart: () => {
app.stop();
log(". Waiting for restart...");
setTimeout(() => {
log(". Restarting!");
app.automate();
}, config.freq.hack);
},
stop: () => {
// check and disable all loops
for (const loop in vars.loops) {
if (vars.loops[loop] === null) {
log(`! Can't stop ${loop} loop`);
continue;
}
clearInterval(vars.loops[loop]);
vars.loops[loop] = null;
}
vars.hackProgress = 0;
// reset flags
vars.flags.ocrBlock = false;
vars.flags.progressBlock = false;
log("* Stopped all hacking");
},
automate: () => {
// does everything to prep for hacking except word guessing
app.attack();
if (vars.loops.miner === null) {
// start the loop for btc monitoring
vars.loops.miner = setInterval(loops.miner, config.freq.mine);
}
if (vars.loops.upgrade === null) {
// start the loop for upgrades
vars.loops.upgrade = setInterval(loops.upgrade, config.freq.upgrade);
}
},
attack: () => {
// if the auto target is toggled, choose the target.
if (config.autoTarget) {
// with playerToAttack = 0 choose between the 4 first players from the player list
const rndTarget = getRandomInt(config.playerToAttack, config.playerToAttack + 3);
// playerToAttack is an int, the index of the player list
const targetName = $("#player-list").children("tr").eq(rndTarget)[0].innerText;
log(`. Now attacking ${targetName}`);
// click it, and then hack, and then a random port
$("#player-list").children("tr").eq(rndTarget)[0].click();
$("#window-other-button").click();
}
// if the auto attack port is toggled, choose the port and click
if (config.autoAttack) {
const portNumber = getRandomInt(1, 3);
// do a check for money
const portStyle = $(`#window-other-port${portNumber}`).attr("style");
if (portStyle.indexOf("opacity: 1") === -1) {
// this port costs too much, let's wait a bit
log("* Hack too expensive, waiting");
setTimeout(app.attack, config.freq.broke);
return;
}
$(`#window-other-port${portNumber}`).click();
}
if (vars.loops.word === null) {
vars.loops.word = setInterval(loops.word, config.freq.word);
}
},
findWord: () => {
const wordLink = $(".tool-type-img").prop("src");
if (!wordLink.endsWith("s0urce.io/client/img/words/template.png")) {
if (vars.listingURL.hasOwnProperty(wordLink) === true) {
const word = vars.listingURL[wordLink];
log(`. Found word (URL): [${word}]`);
app.submit(word);
return;
}
toDataURL(wordLink).then((dataUrl) => {
const hash = getHashCode(dataUrl);
if (vars.listingB64.hasOwnProperty(hash) === true) {
const word = vars.listingB64[hash];
log(`. Found word (B64): [${word}]`);
app.learn(word);
return;
}
if (config.ocr.enabled === true) {
log("* Not seen, trying OCR...");
app.doOCR(config.ocr.url, {
apikey: config.ocr.key,
language: "eng",
url: wordLink
});
} else {
log("* OCR disabled, skipping...");
}
});
} else {
log("* Can't find the word link...");
// if the target is disconnected and autoTarget disabled, re-enable it.
if ($("#cdm-text-container span:last").text() === "Target is disconnected from the Server." && !config.autoTarget) {
$("#custom-autoTarget-button").click();
}
app.restart();
}
},
learn: (word) => {
const wordLink = $(".tool-type-img").prop("src");
vars.listingURL[wordLink] = word;
app.submit(word);
},
submit: (word) => {
$("#tool-type-word").val(word);
$("#tool-type-word").submit();
},
doOCR: (link, payload) => {
vars.flags.ocrBlock = true;
// this is made somewhat generic to allow different ocr vendors
$.post(link, payload).done((data) => {
const word = String(data["ParsedResults"][0]["ParsedText"]).trim().toLowerCase().split(" ").join("");
if (word.length > 2) {
log(`. Got data: [${word}]`);
$("#tool-type-word").val(word);
app.learn(word);
vars.flags.ocrBlock = false;
} else {
log("* OCR failed");
app.restart();
}
});
}
};
loops = {
word: () => {
// block is true is we're mid-OCR
if (vars.flags.ocrBlock === true) {
return;
}
if ($("#targetmessage-input").is(":visible") === true) {
// we're done!
$("#targetmessage-input").val(config.message);
$("#targetmessage-button-send").click();
app.restart();
return;
}
// if we're waiting on the progress bar to move...
if (vars.flags.progressBlock === true) {
const newHackProgress = parseHackProgress($("#progressbar-firewall-amount").attr("style"));
// check to see if it's new
if (vars.hackProgress === newHackProgress) {
// the bar hasn't moved
log("* Progress bar hasn't moved, waiting");
vars.hackFails++;
if (vars.hackFails >= config.maxHackFails) {
vars.hackFails = 0;
log("* Progress bar is stuck, restarting");
// maybe the URLs have changed
vars.listingURL = {};
app.restart();
}
return;
}
// the bar has moved
vars.hackFails = 0;
vars.hackProgress = newHackProgress;
vars.flags.progressBlock = false;
}
// actually do the word stuff
vars.flags.progressBlock = true;
app.findWord();
},
miner: () => {
// first, get the status of our miners
for (const miner of vars.minerStatus) {
// set value
miner.value = parseInt($(`#${miner.name}-amount`).text());
// this is available to buy
if ($(`#${miner.name}`).attr("style") === "opacity: 1;") {
// buy more quantum servers and botnets, buy botnets at the same rate as the quantum servers.
if (miner.value >= config.maxQBLevel) {
// we're beyond or at the max QB level, no updates needed
continue;
}
// is this an advanced miner?
const isAdvancedMiner = (miner.name === "shop-quantum-server" || miner.name === "shop-bot-net") ? true : false;
if (miner.value >= config.maxMinerLevel && isAdvancedMiner === false) {
// this isn't an advanced miner and it's beyond the max level, no updates needed
continue;
}
// we should buy this
$(`#${miner.name}`).click();
}
}
},
upgrade: () => {
// leave if all firewalls are upgraded to max
if (!vars.fireWall[3].needUpgrade)
return;
// get a random firewall
// i refers to the location in the vars.firewall array
const i = getRandomInt(0, 2);
// index refers to 1,2,3, the index in the DOM (use for selectors)
const index = vars.fireWall[i].index;
// if this fireWall is already fully upgraded, get an other random firewall.
if (!vars.fireWall[i].needUpgrade)
vars.loops.upgrade();
vars.balance = parseInt($("#window-my-coinamount").text());
// if the back button is visible, we're on a page, let's back out and hide the firewall warning.
if ($("#window-firewall-pagebutton").is(":visible") === true) {
$("#tutorial-firewall").css("display", "none");
$("#window-firewall-pagebutton").click();
}
// click on the firewall
log(`. Handling upgrades to firewall ${vars.fireWall[i].name}`);
$(`#window-firewall-part${index}`).click();
// get stats
const stats = [
parseInt($("#shop-max-charges").text()), parseInt($("#shop-strength").text()), parseInt($("#shop-regen").text())
];
const statLookup = [
"max_charge10", "difficulty", "regen"
];
const maxStats = [
30, 4, 10
];
let maxUpgradeCount = 0;
for (const stat in maxStats) {
if (stats[stat] < maxStats[stat]) {
const statPrice = parseInt($(`#shop-firewall-${statLookup[stat]}-value`).text());
if (statPrice < (vars.balance * config.maxUpgradeCost)) {
log(`. Buying: ${$(".window-shop-element-info b").eq(stat).text()}`);
$(`#shop-firewall-${statLookup[stat]}`).click();
// buy more than one upgrade, but only if they cost less than a third of the bitcoin balance.
// return;
}
} else {
maxUpgradeCount++;
if (maxUpgradeCount === 3) {
vars.fireWall[i].needUpgrade = false;
if (vars.fireWall.every(checkFirewallsUpgrades))
vars.fireWall[3].needUpgrade = false;
}
}
}
// let's go back
if ($("#window-firewall-pagebutton").is(":visible") === true) {
$("#window-firewall-pagebutton").click();
}
}
};
gui = {
show: () => {
const sizeCSS = `height: ${config.gui.height}; width: ${config.gui.width};`;
const labelMap = {
word: "Word Speed",
mine: "Miner Upgrade",
upgrade: "Firewall Upgrade",
hack: "Hack Wait"
};
const freqInput = (type) => {
return `<span style="font-size:15px">
${labelMap[type]}:
<input type="text" class="custom-gui-freq input-form" style="width:50px;margin:0px 0px 15px 5px;border:" value="${config.freq[type]}" data-type="${type}">
<span>(ms)</span><br>
</span>`;
};
const botWindowHTML = `
<div id="custom-gui" class="window" style="border-color: rgb(62, 76, 95); color: rgb(191, 207, 210); ${sizeCSS} z-index: 10; top: 11.5%; left: 83%;">
<div id="custom-gui-bot-title" class="window-title" style="background-color: rgb(62, 76, 95);">
Source.io Bot
<span class="window-close-style">
<img class="window-close-img" src="http://s0urce.io/client/img/icon-close.png">
</span>
</div>
<div class="window-content" style="${sizeCSS}">
<div id="custom-restart-button" class="button" style="display: block; margin-bottom: 15px">
Restart Bot
</div>
<div id="custom-stop-button" class="button" style="display: block; margin-bottom: 15px">
Stop Bot
</div>
<div id="custom-autoTarget-button" class="button" style="display: block; margin-bottom: 15px">
Target Auto
</div>
<div id="custom-autoAttack-button" class="button" style="display: block; margin-bottom: 15px">
Port Attack Auto
</div>
<span>Message to victim:</span>
<br>
<input type="text" class="custom-gui-msg input-form" style="width:250px;height:30px;border:;background:lightgrey;color:black" value="${config.message}" >
<br><br>
${freqInput("word")}
${freqInput("mine")}
${freqInput("upgrade")}
${freqInput("hack")}
<div id="custom-github-button" class="button" style="display: block;">
This script is on Github!
</div>
</div>
</div>`;
$(".window-wrapper").append(botWindowHTML);
// color the toggle buttons
$("#custom-autoTarget-button").css("color", config.autoTarget ? "green" : "red");
$("#custom-autoAttack-button").css("color", config.autoAttack ? "green" : "red");
// bind functions to the gui's buttons
$("#custom-gui-bot-title > span.window-close-style").on("click", () => {
$("#custom-gui").hide();
});
$("#custom-restart-button").on("click", () => {
app.restart();
});
$("#custom-stop-button").on("click", () => {
app.stop();
});
$("#custom-autoTarget-button").on("click", () => {
config.autoTarget = !config.autoTarget;
$("#custom-autoTarget-button").css("color", config.autoTarget ? "green" : "red");
});
$("#custom-autoAttack-button").on("click", () => {
config.autoAttack = !config.autoAttack;
$("#custom-autoAttack-button").css("color", config.autoAttack ? "green" : "red");
});
$("#custom-github-button").on("click", () => {
window.open("https://github.com/snollygolly/sourceio-automation");
});
$(".custom-gui-freq").on("keypress", (e) => {
if (e.keyCode !== 13) {
return;
}
const type = $(e.target).attr("data-type");
if (!config.freq[type]) {
// invalid input, disregard i guess?
return;
}
config.freq[type] = $(e.target).val();
log(`* Frequency for '${type}' set to ${config.freq[type]}`);
});
$(".custom-gui-msg").on("keypress", (e) => {
if (e.keyCode !== 13) {
return;
}
config.message = $(e.target).val();
log(`* Message for set to : ${config.message}`);
});
// make the bot window draggable
const botWindow = ("#custom-gui");
$(document).on("mousedown", botWindow, (e) => {
vars.gui.dragReady = true;
vars.gui.dragOffset.x = e.pageX - $(botWindow).position().left;
vars.gui.dragOffset.y = e.pageY - $(botWindow).position().top;
});
$(document).on("mouseup", botWindow, () => {
vars.gui.dragReady = false;
});
$(document).on("mousemove", (e) => {
if (vars.gui.dragReady) {
$(botWindow).css("top", `${e.pageY - vars.gui.dragOffset.y}px`);
$(botWindow).css("left", `${e.pageX - vars.gui.dragOffset.x}px`);
}
});
}
};
function checkFirewallsUpgrades(FW, index) {
if (index === 3)
return true;
return FW.needUpgrade === false;
}
function parseHackProgress(progress) {
// remove the %;
const newProgress = progress.slice(0, -2);
const newProgressParts = newProgress.split("width: ");
return parseInt(newProgressParts.pop());
}
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function getHashCode(data) {
let hash = 0;
if (data.length === 0) {
return hash;
}
for (let i = 0; i < data.length; i++) {
const c = data.charCodeAt(i);
hash = ((hash << 5) - hash) + c;
hash &= hash;
}
return hash.toString();
}
function toDataURL(url) {
return fetch(url)
.then(response => response.blob())
.then(blob => new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result);
reader.onerror = reject;
reader.readAsDataURL(blob);
}));
}
function log(message) {
console.log(`:: ${message}`);
}