diff --git a/README.md b/README.md index 6ef2214..df7f901 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,8 @@ run scripts/update.js # Start the node manager with 8 nodes run scripts/node-manager.js 8 -# Chain the crawler, auto-pwner & miner to hack everything within 3 hops -run scripts/crawler.js 3 /scripts/auto-pwn.js {{TARGET}} /scripts/miner.js +# Chain the crawler, auto-pwner & miner to hack everything on the network +run scripts/crawler.js /scripts/auto-pwn.js {{TARGET}} /scripts/miner.js ``` ## Scripts @@ -24,7 +24,7 @@ run scripts/crawler.js 3 /scripts/auto-pwn.js {{TARGET}} /scripts/miner.js Automatically gain root on a target machine. Optionaly after being rooted, a file can be coppied & executed. ``` [home ~/]> run scripts/auto-pwn.js --help -Running script with 1 thread(s), pid 176 and args: ["--help"]. +Running script with 1 thread(s), pid 161 and args: ["--help"]. /scripts/auto-pwn.js: Automatically gain root on a target machine. Optionaly after being rooted, a file can be coppied & executed. @@ -37,18 +37,37 @@ Usage: run auto-pwn.js [TARGET] [SCRIPT] [ARGS]... ARGS Aditional arguments for SCRIPT. Forward the target with "{{TARGET}}" Options: + -t --threads=num Set number of threads for script -h --help Display help message ``` ### [bruteforce.js](./scripts/bruteforce.js) (WIP) Attacks target until security falls bellow threshold. Useful for throwing extra compute power & cracking a specific computer. -It's recommended you use any extra compute power on your home computer/servers to break strong servers & speed up the process. - ### [crawler.js](./scripts/crawler.js) (WIP) -Scans the network to a desired depth & runs the specified script against targets. +**RAM:** 3.05 GB -It's recommended you use this in combination with `auto-pwn.js`. +Search the network for targets to execute a script against. +``` +[home ~/]> run scripts/crawler.js --help +Running script with 1 thread(s), pid 163 and args: ["--help"]. +/scripts/crawler.js: + +Search the network for targets to execute a script against. + +Usage: run crawler.js [OPTIONS] SCRIPT [ARGS]... + run crawler.js --help + + SCRIPT Script to copy & execute + ARGS Aditional arguments for SCRIPT. Forward the target with "{{TARGET}}" + +Options: + -d --depth=num Number of network hops. Defaults to 3 + -l --level=num Exclude targets with a high hacking level. Defaults to hack level, 0 to disable + -p --ports=num Exclute targets with too many closed ports + -t --threads=num Set number of threads for script + -h --help Display help message +``` ### [miner.js](./scripts/miner.js) **RAM:** 2.35 GB @@ -56,20 +75,17 @@ It's recommended you use this in combination with `auto-pwn.js`. Weaken, spoof & hack the target in a loop for money. ``` [home ~/]> run scripts/miner.js --help - -Running script with 1 thread(s), pid 41 and args: ["--help"]. - +Running script with 1 thread(s), pid 165 and args: ["--help"]. /scripts/miner.js: Weaken, spoof & hack the target in a loop for money. -Usage: run miner.js [OPTIONS] [TARGET] +Usage: run miner.js [TARGET] run miner.js --help TARGET Target to mine. Defaults to localhost Options: - -t --threads Speed up script with more CPU power -h --help Display help message ``` @@ -79,10 +95,10 @@ Options: Buy, upgrade & manage Hacknet nodes automatically. ``` [home ~/]> run scripts/node-manager.js --help -Running script with 1 thread(s), pid 128 and args: ["--help"]. +Running script with 1 thread(s), pid 166 and args: ["--help"]. /scripts/node-manager.js: -Buy, upgrade & manage Hacknet nodes automatically. +Buy, upgrade & manage Hacknet nodes automatically. Tail for live updates. Usage: run node-manager.js [OPTIONS] LIMIT run node-manager.js --balance 1E6 4 @@ -100,21 +116,15 @@ Options: Automatically download the latest versions of all scripts using wget. ``` -[home ~/]> run scripts/update.js -Running script with 1 thread(s), pid 18 and args: []. -/scripts/update.js: Updating self: -/scripts/update.js: update.js [==================>] 100% (14.9 MB/s) +[home ~/]> run scripts/update.js --help +Running script with 1 thread(s), pid 167 and args: ["--help"]. /scripts/update.js: -/scripts/update.js: Restarting... -/scripts/update.js: Downloading scripts: -/scripts/update.js: -/scripts/update.js: lib/arg-parser.js [==================>] 100% (13.4 MB/s) -/scripts/update.js: auto-pwn.js [==================>] 100% (15.8 MB/s) -/scripts/update.js: bruteforce.js [==================>] 100% (18 MB/s) -/scripts/update.js: crawler.js [==================>] 100% (23.5 MB/s) -/scripts/update.js: miner.js [==================>] 100% (18.4 MB/s) -/scripts/update.js: node-manager.js [==================>] 100% (22 MB/s) -/scripts/update.js: update.js [==================>] 100% (22.7 MB/s) -/scripts/update.js: -/scripts/update.js: ✅ Done! + +Automatically download the latest versions of all scripts using wget. + +Usage: run update.js + run update.js --help + +Options: + -h --help Display help message ``` diff --git a/scripts/auto-pwn.js b/scripts/auto-pwn.js index 6500d55..ef9f751 100644 --- a/scripts/auto-pwn.js +++ b/scripts/auto-pwn.js @@ -1,4 +1,71 @@ -import {ArgParser} from './scripts/lib/arg-parser'; +class ArgParser { + /** + * Create a unix-like argument parser to extract flags from the argument list. Can also create help messages. + * @param opts - {examples: string[], arguments: {key: string, alias: string, type: string, optional: boolean, desc: string}[], desc: string} + */ + constructor(opts) { + this.examples = opts.examples ?? []; + this.arguments = opts.args ?? []; + this.description = opts.desc; + } + + /** + * Parse the list for arguments & create a dictionary. + * @param args {any[]} - Array of arguments + * @returns Dictionary of matched flags + unmatched args under 'extra' + */ + parse(args) { + const req = this.arguments.filter(a => !a.optional && !a.skip); + const queue = [...args], parsed = {}, extra = []; + for(let i = 0; i < queue.length; i++) { + if(queue[i][0] != '-') { + extra.push(queue[i]); + continue; + } + let value = null, parse = queue[i].slice(queue[i][1] == '-' ? 2 : 1); + if(parse.indexOf('=')) { + const split = parse.split('='); + parse = split[0]; + value = split[1]; + } + let arg = this.arguments.find(a => a.key == parse) ?? this.arguments.find(a => a.alias == parse); + if(!arg) { + extra.push(queue[i]); + continue; + } + if(!value) { + value = arg.type == 'bool' ? true : queue[i + 1]; + if(arg.type != 'bool') i++; + } + parsed[arg.key] = value; + } + req.forEach((a, i) => parsed[a.key] = extra[i]); + extra.splice(0, req.length); + return {...parsed, extra}; + } + + /** + * Create a help message of the expected paramters & usage. + * @param msg {String} - Optional message to display with help + */ + help(msg) { + let message = '\n\n'; + message += msg ? msg : this.description; + if(this.examples.length) message += '\n\nUsage:\t' + this.examples.join('\n\t'); + const required = this.arguments.filter(a => !a.optional); + if(required.length) message += '\n\n\t' + required.map(a => { + const padding = 3 - ~~(a.key.length / 8); + return `${a.key}${Array(padding).fill('\t').join('')} ${a.desc}`; + }).join('\n\t'); + const optional = this.arguments.filter(a => a.optional); + if(optional.length) message += '\n\nOptions:\n\t' + optional.map(a => { + const flgs = `${a.alias ? `-${a.alias} ` : ''}--${a.key}${a.type && a.type != 'bool' ? `=${a.type}` : ''}`; + const padding = 3 - ~~(flgs.length / 8); + return `${flgs}${Array(padding).fill('\t').join('')} ${a.desc}`; + }).join('\n\t'); + return `${message}\n\n`; + } +} /** * Pwn a target server will availible tools. Can also copy & execute a script after pwning. @@ -27,8 +94,9 @@ export async function main(ns) { args: [ {key: 'TARGET', desc: 'Target machine to root. Defaults to localhost'}, {key: 'SCRIPT', desc: 'Script to copy & execute'}, - {key: 'ARGS', desc: 'Aditional arguments for SCRIPT. Forward the target with "{{TARGET}}"'}, - {key: 'help', alias: 'h', optional: true, desc: 'Display help message'}, + {key: 'ARGS', skip: true, desc: 'Aditional arguments for SCRIPT. Forward the target with "{{TARGET}}"'}, + {key: 'threads', alias: 't', type: 'num', optional: true, desc: 'Set number of threads for script'}, + {key: 'help', alias: 'h', type: 'bool', optional: true, desc: 'Display help message'}, ] }); const args = argParser.parse(ns.args); @@ -36,25 +104,28 @@ export async function main(ns) { // Setup const target = args['TARGET'] && args['TARGET'] != 'localhost' ? args['TARGET'] : ns.getHostname(); + const threads = args['threads'] || !args['SCRIPT'] ? 1 : ~~(ns.getServerMaxRam(target) / ns.getScriptRam(args['SCRIPT'], 'home')); // Banner ns.tprint('==================================================='); ns.tprint(`🧑‍💻 Pwning: ${target}`); await printWithDelay('==================================================='); - // Gain root try { + // Open as many ports as possible ns.brutessh(target); await printWithDelay(`Attacking (SSH) ⚔️ ${target}:22`, 3, 5); ns.ftpcrack(target); await printWithDelay(`Attacking (FTP) ⚔️ ${target}:24`, 3, 5); } catch { } finally { + // Attempt to root try { ns.nuke(target) await printWithDelay(`💀 Root Granted 💀`); } catch { await printWithDelay(`⚠️ Failed to Root ⚠️`); + ns.exit(); } } @@ -69,13 +140,11 @@ export async function main(ns) { // Run scripts ns.tprint(''); ns.tprint('Executing:'); - const threads = Math.floor(ns.getServerMaxRam(target) / 2.3); ns.scriptKill(args['SCRIPT'], target); await printWithDelay(`ssh -c "run ${args['SCRIPT']} ${args['extra'].join(' ')} -t ${threads}" root@${target}`); const pid = ns.exec(args['SCRIPT'], target, threads, ...args['extra'].map(a => a == '{{TARGET}}' ? target : a)); - if(!pid) return ns.tprint('⚠️ Failed to start ⚠️'); ns.tprint(''); - ns.tprint('✅ Complete!'); + ns.tprint(pid != null ? '✅ Done!' : '⚠️ Failed to start ⚠️'); ns.tprint(''); } } diff --git a/scripts/crawler.js b/scripts/crawler.js index 7db9b54..6c6d5f5 100644 --- a/scripts/crawler.js +++ b/scripts/crawler.js @@ -1,21 +1,122 @@ -export async function main(ns) { - function usage(message) { - ns.tprint(`${!message ? '' : `${message}\n\n`}Usage:\nrun hack-all.js [...scripts]\n\n\tdepth - Follows network recersively\n\tscript1 - Path to script to run\n\tscripts - Additional scripts to run`); - } +class ArgParser { + /** + * Create a unix-like argument parser to extract flags from the argument list. Can also create help messages. + * @param opts - {examples: string[], arguments: {key: string, alias: string, type: string, optional: boolean, desc: string}[], desc: string} + */ + constructor(opts) { + this.examples = opts.examples ?? []; + this.arguments = opts.args ?? []; + this.description = opts.desc; + } - ns.disableLog('ALL'); - if(ns.args[0] == null) return usage('Missing depth'); - if(ns.args.length < 2) return usage('Missing script(s)'); + /** + * Parse the list for arguments & create a dictionary. + * @param args {any[]} - Array of arguments + * @returns Dictionary of matched flags + unmatched args under 'extra' + */ + parse(args) { + const req = this.arguments.filter(a => !a.optional && !a.skip); + const queue = [...args], parsed = {}, extra = []; + for(let i = 0; i < queue.length; i++) { + if(queue[i][0] != '-') { + extra.push(queue[i]); + continue; + } + let value = null, parse = queue[i].slice(queue[i][1] == '-' ? 2 : 1); + if(parse.indexOf('=')) { + const split = parse.split('='); + parse = split[0]; + value = split[1]; + } + let arg = this.arguments.find(a => a.key == parse) ?? this.arguments.find(a => a.alias == parse); + if(!arg) { + extra.push(queue[i]); + continue; + } + if(!value) { + value = arg.type == 'bool' ? true : queue[i + 1]; + if(arg.type != 'bool') i++; + } + parsed[arg.key] = value; + } + req.forEach((a, i) => parsed[a.key] = extra[i]); + extra.splice(0, req.length); + return {...parsed, extra}; + } - let targets = ns.scan().map(h => [h, 1]); - for(let i = 0; i < targets.length; i++) { - if(targets[i][1] < ns.args[0]) ns.scan(targets[i][0]).forEach(h => { - if(h != 'home') targets.push([h, targets[i][1] + 1]) - }); - if(ns.getServerRequiredHackingLevel(targets[i][0]) > ns.getHackingLevel()) continue; - if(ns.getServerNumPortsRequired(targets[i][0]) > 0) continue; - ns.run('hack.js', 1, targets[i][0], ...ns.args.slice(1)); - do { await ns.sleep(1000); } - while(ns.scriptRunning('hack.js', 'home')); - } + /** + * Create a help message of the expected paramters & usage. + * @param msg {String} - Optional message to display with help + */ + help(msg) { + let message = '\n\n'; + message += msg ? msg : this.description; + if(this.examples.length) message += '\n\nUsage:\t' + this.examples.join('\n\t'); + const required = this.arguments.filter(a => !a.optional); + if(required.length) message += '\n\n\t' + required.map(a => { + const padding = 3 - ~~(a.key.length / 8); + return `${a.key}${Array(padding).fill('\t').join('')} ${a.desc}`; + }).join('\n\t'); + const optional = this.arguments.filter(a => a.optional); + if(optional.length) message += '\n\nOptions:\n\t' + optional.map(a => { + const flgs = `${a.alias ? `-${a.alias} ` : ''}--${a.key}${a.type && a.type != 'bool' ? `=${a.type}` : ''}`; + const padding = 3 - ~~(flgs.length / 8); + return `${flgs}${Array(padding).fill('\t').join('')} ${a.desc}`; + }).join('\n\t'); + return `${message}\n\n`; + } +} + +export async function main(ns) { + ns.disableLog('ALL'); + + // Initilize script arguments + const argParser = new ArgParser({ + desc: 'Search the network for targets to execute a script against.', + examples: [ + 'run crawler.js [OPTIONS] SCRIPT [ARGS]...', + 'run crawler.js --help', + ], + args: [ + {key: 'SCRIPT', desc: 'Script to copy & execute'}, + {key: 'ARGS', skip: true, desc: 'Aditional arguments for SCRIPT. Forward the target with "{{TARGET}}"'}, + {key: 'depth', alias: 'd', type: 'num', optional: true, desc: 'Number of network hops. Defaults to 3'}, + {key: 'level', alias: 'l', type: 'num', optional: true, desc: 'Exclude targets with a high hacking level. Defaults to hack level, 0 to disable'}, + {key: 'ports', alias: 'p', type: 'num', optional: true, desc: 'Exclute targets with too many closed ports'}, + {key: 'threads', alias: 't', type: 'num', optional: true, desc: 'Set number of threads for script'}, + {key: 'help', alias: 'h', type: 'bool', optional: true, desc: 'Display help message'}, + ] + }); + const args = argParser.parse(ns.args); + if(args['help']) return ns.tprint(argParser.help()); + if(!args['SCRIPT']) return ns.tprint(argParser.help('Missing SCRIPT')); + + // Setup + const depth = args['depth'] || 3; + const level = args['level'] || ns.getHackingLevel(); + const ports = args['ports'] || Infinity; + const threads = args['threads'] || 1; + + // Recursively search for targets + const targets = ns.scan().map(h => [h, 1]); + for(let i = 0; i < targets.length; i++) { + if(targets[i][1] < depth) ns.scan(targets[i][0]).forEach(h => { + if(h != 'home' && !targets.find(t => t[0] == h)) targets.push([h, targets[i][1] + 1]); + }); + } + + // Execute script on each target + for(const target of targets) { + // Check target + if(level && level < ns.getServerRequiredHackingLevel(target[0]) || + (ports && ports < ns.getServerNumPortsRequired(target[0]))) return; + + // Start script + ns.run(args['SCRIPT'], threads, ...args['extra'].map(a => a == '{{TARGET}}' ? target[0] : a)); + + // Wait for script to finish + // do { await ns.sleep(1000); } + // while(ns.scriptRunning(args['SCRIPT'], 'home')); + await ns.sleep(10000); + } } diff --git a/scripts/lib/arg-parser.js b/scripts/lib/arg-parser.js deleted file mode 100644 index 47c2793..0000000 --- a/scripts/lib/arg-parser.js +++ /dev/null @@ -1,62 +0,0 @@ -export class ArgParser { - /** - * Create a unix-like argument parser to extract flags from the argument list. Can also create help messages. - * @param opts - {examples: string[], arguments: {key: string, alias: string, type: string, optional: boolean, desc: string}[], desc: string} - */ - constructor(opts) { - this.examples = opts.examples ?? []; - this.arguments = opts.args ?? []; - this.description = opts.desc; - } - - /** - * Parse the list for arguments & create a dictionary. - * @param args {any[]} - Array of arguments - * @returns Dictionary of matched flags + unmatched args under 'extra' - */ - parse(args) { - const req = this.arguments.filter(a => !a.optional); - const queue = [...args]; - const parsed = {}; - for(let i = 0; i < queue.length; i++) { - if(queue[i][0] != '-') continue; - let value = null, parse = queue[i].slice(queue[i][1] == '-' ? 2 : 1); - if(parse.indexOf('=')) { - const split = parse.split('='); - parse = split[0]; - value = split[1]; - } - let arg = this.arguments.find(a => a.key == parse) ?? this.arguments.find(a => a.alias == parse); - if(!arg) continue; - if(!value) value = arg.type && arg.type != 'bool' ? queue.splice(i + 1, 1)[0] : true; - parsed[arg.key] = value; - queue.splice(i, 1); - } - req.forEach((a, i) => parsed[a.key] = queue[i]); - queue.splice(0, req.length); - parsed.extra = queue; - return parsed; - } - - /** - * Create a help message of the expected paramters & usage. - * @param msg {String} - Optional message to display with help - */ - help(msg) { - let message = '\n\n'; - message += msg ? msg : this.description; - if(this.examples.length) message += '\n\nUsage:\t' + this.examples.join('\n\t'); - const required = this.arguments.filter(a => !a.optional); - if(required.length) message += '\n\n\t' + required.map(a => { - const padding = 3 - ~~(a.key.length / 8); - return `${a.key}${Array(padding).fill('\t').join('')} ${a.desc}`; - }).join('\n\t'); - const optional = this.arguments.filter(a => a.optional); - if(optional.length) message += '\n\nOptions:\n\t' + optional.map(a => { - const flgs = `${a.alias ? `-${a.alias} ` : ''}--${a.key}${a.type && a.type != 'bool' ? `=${a.type}` : ''}`; - const padding = 3 - ~~(flgs.length / 8); - return `${flgs}${Array(padding).fill('\t').join('')} ${a.desc}`; - }).join('\n\t'); - return `${message}\n\n`; - } -} diff --git a/scripts/miner.js b/scripts/miner.js index 5a5cd77..5bd1049 100644 --- a/scripts/miner.js +++ b/scripts/miner.js @@ -1,4 +1,71 @@ -import {ArgParser} from './scripts/lib/arg-parser'; +class ArgParser { + /** + * Create a unix-like argument parser to extract flags from the argument list. Can also create help messages. + * @param opts - {examples: string[], arguments: {key: string, alias: string, type: string, optional: boolean, desc: string}[], desc: string} + */ + constructor(opts) { + this.examples = opts.examples ?? []; + this.arguments = opts.args ?? []; + this.description = opts.desc; + } + + /** + * Parse the list for arguments & create a dictionary. + * @param args {any[]} - Array of arguments + * @returns Dictionary of matched flags + unmatched args under 'extra' + */ + parse(args) { + const req = this.arguments.filter(a => !a.optional && !a.skip); + const queue = [...args], parsed = {}, extra = []; + for(let i = 0; i < queue.length; i++) { + if(queue[i][0] != '-') { + extra.push(queue[i]); + continue; + } + let value = null, parse = queue[i].slice(queue[i][1] == '-' ? 2 : 1); + if(parse.indexOf('=')) { + const split = parse.split('='); + parse = split[0]; + value = split[1]; + } + let arg = this.arguments.find(a => a.key == parse) ?? this.arguments.find(a => a.alias == parse); + if(!arg) { + extra.push(queue[i]); + continue; + } + if(!value) { + value = arg.type == 'bool' ? true : queue[i + 1]; + if(arg.type != 'bool') i++; + } + parsed[arg.key] = value; + } + req.forEach((a, i) => parsed[a.key] = extra[i]); + extra.splice(0, req.length); + return {...parsed, extra}; + } + + /** + * Create a help message of the expected paramters & usage. + * @param msg {String} - Optional message to display with help + */ + help(msg) { + let message = '\n\n'; + message += msg ? msg : this.description; + if(this.examples.length) message += '\n\nUsage:\t' + this.examples.join('\n\t'); + const required = this.arguments.filter(a => !a.optional); + if(required.length) message += '\n\n\t' + required.map(a => { + const padding = 3 - ~~(a.key.length / 8); + return `${a.key}${Array(padding).fill('\t').join('')} ${a.desc}`; + }).join('\n\t'); + const optional = this.arguments.filter(a => a.optional); + if(optional.length) message += '\n\nOptions:\n\t' + optional.map(a => { + const flgs = `${a.alias ? `-${a.alias} ` : ''}--${a.key}${a.type && a.type != 'bool' ? `=${a.type}` : ''}`; + const padding = 3 - ~~(flgs.length / 8); + return `${flgs}${Array(padding).fill('\t').join('')} ${a.desc}`; + }).join('\n\t'); + return `${message}\n\n`; + } +} /** * Hack a server for it's money. @@ -27,13 +94,12 @@ export async function main(ns) { const argParser = new ArgParser({ desc: 'Weaken, spoof & hack the target in a loop for money.', examples: [ - 'run miner.js [OPTIONS] [TARGET]', + 'run miner.js [TARGET]', 'run miner.js --help', ], args: [ {key: 'TARGET', desc: 'Target to mine. Defaults to localhost'}, - {key: 'threads', alias: 't', optional: true, desc: 'Speed up script with more CPU power'}, - {key: 'help', alias: 'h', optional: true, desc: 'Display help message'}, + {key: 'help', alias: 'h', type: 'bool', optional: true, desc: 'Display help message'}, ] }); const args = argParser.parse(ns.args); @@ -42,7 +108,6 @@ export async function main(ns) { // Setup const historyLength = 15; const messageHistory = Array(historyLength).fill(''); - const threads = args['threads'] || 1; const target = args['TARGET'] && args['TARGET'] != 'localhost' ? args['TARGET'] : ns.getHostname(); const minSecurity = ns.getServerMinSecurityLevel(target) + 2; let orgBalance, balance, security; @@ -55,17 +120,17 @@ export async function main(ns) { if(orgBalance == null) orgBalance = balance; // Pick step - if(security > minSecurity) { + if(security > minSecurity) { // Weaken log('Attacking Security...'); - const w = await ns.weaken(target, {threads}); + const w = await ns.weaken(target); log(`Security: -${w}`); - } else if(balance <= orgBalance) { + } else if(balance <= orgBalance) { // Grow log('Spoofing Balance...'); - const g = await ns.grow(target, {threads}); + const g = await ns.grow(target); log(`Balance: +$${Math.round((g * balance - balance) * 100) / 100}`); - } else { + } else { // Hack log('Hacking Account...'); - const h = await ns.hack(target, {threads}); + const h = await ns.hack(target); log(`Balance: -$${h}`); } } diff --git a/scripts/node-manager.js b/scripts/node-manager.js index d8953e8..d5e2da1 100644 --- a/scripts/node-manager.js +++ b/scripts/node-manager.js @@ -1,4 +1,71 @@ -import {ArgParser} from './scripts/lib/arg-parser'; +class ArgParser { + /** + * Create a unix-like argument parser to extract flags from the argument list. Can also create help messages. + * @param opts - {examples: string[], arguments: {key: string, alias: string, type: string, optional: boolean, desc: string}[], desc: string} + */ + constructor(opts) { + this.examples = opts.examples ?? []; + this.arguments = opts.args ?? []; + this.description = opts.desc; + } + + /** + * Parse the list for arguments & create a dictionary. + * @param args {any[]} - Array of arguments + * @returns Dictionary of matched flags + unmatched args under 'extra' + */ + parse(args) { + const req = this.arguments.filter(a => !a.optional && !a.skip); + const queue = [...args], parsed = {}, extra = []; + for(let i = 0; i < queue.length; i++) { + if(queue[i][0] != '-') { + extra.push(queue[i]); + continue; + } + let value = null, parse = queue[i].slice(queue[i][1] == '-' ? 2 : 1); + if(parse.indexOf('=')) { + const split = parse.split('='); + parse = split[0]; + value = split[1]; + } + let arg = this.arguments.find(a => a.key == parse) ?? this.arguments.find(a => a.alias == parse); + if(!arg) { + extra.push(queue[i]); + continue; + } + if(!value) { + value = arg.type == 'bool' ? true : queue[i + 1]; + if(arg.type != 'bool') i++; + } + parsed[arg.key] = value; + } + req.forEach((a, i) => parsed[a.key] = extra[i]); + extra.splice(0, req.length); + return {...parsed, extra}; + } + + /** + * Create a help message of the expected paramters & usage. + * @param msg {String} - Optional message to display with help + */ + help(msg) { + let message = '\n\n'; + message += msg ? msg : this.description; + if(this.examples.length) message += '\n\nUsage:\t' + this.examples.join('\n\t'); + const required = this.arguments.filter(a => !a.optional); + if(required.length) message += '\n\n\t' + required.map(a => { + const padding = 3 - ~~(a.key.length / 8); + return `${a.key}${Array(padding).fill('\t').join('')} ${a.desc}`; + }).join('\n\t'); + const optional = this.arguments.filter(a => a.optional); + if(optional.length) message += '\n\nOptions:\n\t' + optional.map(a => { + const flgs = `${a.alias ? `-${a.alias} ` : ''}--${a.key}${a.type && a.type != 'bool' ? `=${a.type}` : ''}`; + const padding = 3 - ~~(flgs.length / 8); + return `${flgs}${Array(padding).fill('\t').join('')} ${a.desc}`; + }).join('\n\t'); + return `${message}\n\n`; + } +} /** * Manages hacknet nodes, purchasing nodes to reach the desired amount. @@ -32,7 +99,7 @@ export async function main(ns) { args: [ {key: 'LIMIT', desc: 'Limit the number of nodes the manager will buy'}, {key: 'balance', alias: 'b', type: 'num', optional: true, desc: 'Prevent spending bellow this point'}, - {key: 'help', alias: 'h', optional: true, desc: 'Display help message'}, + {key: 'help', alias: 'h', type: 'bool', optional: true, desc: 'Display help message'}, ] }); const args = argParser.parse(ns.args); @@ -48,7 +115,7 @@ export async function main(ns) { const messageHistory = Array(historyLength).fill(''); const limit = args['LIMIT']; const savings = args['balance'] ?? 0 - const nodeCount = ns.hacknet.numNodes(); + let nodeCount = ns.hacknet.numNodes(); log(); while(true) { diff --git a/scripts/update.js b/scripts/update.js index c012766..86dae91 100644 --- a/scripts/update.js +++ b/scripts/update.js @@ -1,3 +1,72 @@ +class ArgParser { + /** + * Create a unix-like argument parser to extract flags from the argument list. Can also create help messages. + * @param opts - {examples: string[], arguments: {key: string, alias: string, type: string, optional: boolean, desc: string}[], desc: string} + */ + constructor(opts) { + this.examples = opts.examples ?? []; + this.arguments = opts.args ?? []; + this.description = opts.desc; + } + + /** + * Parse the list for arguments & create a dictionary. + * @param args {any[]} - Array of arguments + * @returns Dictionary of matched flags + unmatched args under 'extra' + */ + parse(args) { + const req = this.arguments.filter(a => !a.optional && !a.skip); + const queue = [...args], parsed = {}, extra = []; + for(let i = 0; i < queue.length; i++) { + if(queue[i][0] != '-') { + extra.push(queue[i]); + continue; + } + let value = null, parse = queue[i].slice(queue[i][1] == '-' ? 2 : 1); + if(parse.indexOf('=')) { + const split = parse.split('='); + parse = split[0]; + value = split[1]; + } + let arg = this.arguments.find(a => a.key == parse) ?? this.arguments.find(a => a.alias == parse); + if(!arg) { + extra.push(queue[i]); + continue; + } + if(!value) { + value = arg.type == 'bool' ? true : queue[i + 1]; + if(arg.type != 'bool') i++; + } + parsed[arg.key] = value; + } + req.forEach((a, i) => parsed[a.key] = extra[i]); + extra.splice(0, req.length); + return {...parsed, extra}; + } + + /** + * Create a help message of the expected paramters & usage. + * @param msg {String} - Optional message to display with help + */ + help(msg) { + let message = '\n\n'; + message += msg ? msg : this.description; + if(this.examples.length) message += '\n\nUsage:\t' + this.examples.join('\n\t'); + const required = this.arguments.filter(a => !a.optional); + if(required.length) message += '\n\n\t' + required.map(a => { + const padding = 3 - ~~(a.key.length / 8); + return `${a.key}${Array(padding).fill('\t').join('')} ${a.desc}`; + }).join('\n\t'); + const optional = this.arguments.filter(a => a.optional); + if(optional.length) message += '\n\nOptions:\n\t' + optional.map(a => { + const flgs = `${a.alias ? `-${a.alias} ` : ''}--${a.key}${a.type && a.type != 'bool' ? `=${a.type}` : ''}`; + const padding = 3 - ~~(flgs.length / 8); + return `${flgs}${Array(padding).fill('\t').join('')} ${a.desc}`; + }).join('\n\t'); + return `${message}\n\n`; + } +} + /** * Automatically download all the scripts in the repository. */ @@ -14,11 +83,24 @@ export async function main(ns) { ns.tprint(`${file} ${file.length <= 10 ? '\t' : ''}\t [==================>] 100% \t (${speed} MB/s)`); } + // Initilize script arguments + const argParser = new ArgParser({ + desc: 'Automatically download the latest versions of all scripts using wget.', + examples: [ + 'run update.js', + 'run update.js --help', + ], + args: [ + {key: 'help', alias: 'h', type: 'bool', optional: true, desc: 'Display help message'}, + ] + }); + const args = argParser.parse(ns.args); + if(args['help']) return ns.tprint(argParser.help()); + // Setup const src = 'https://gitlab.zakscode.com/ztimson/BitBurner/-/raw/develop/scripts/'; const dest = '/scripts/'; const fileList = [ - 'lib/arg-parser.js', 'auto-pwn.js', 'bruteforce.js', 'crawler.js',