wip
This commit is contained in:
		
							
								
								
									
										32
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								README.md
									
									
									
									
									
								
							| @@ -13,7 +13,6 @@ These scripts are for playing the [open source](https://github.com/danielyxie/bi | |||||||
| 	- [network-graph.js](#network-graphjs) | 	- [network-graph.js](#network-graphjs) | ||||||
| 	- [rootkit.js](#rootkitjs) | 	- [rootkit.js](#rootkitjs) | ||||||
| 	- [update.js](#updatejs) | 	- [update.js](#updatejs) | ||||||
| 	- [vanguard.js](#vanguardjs) |  | ||||||
|  |  | ||||||
| ## Quick Start | ## Quick Start | ||||||
|  |  | ||||||
| @@ -151,10 +150,12 @@ Usage:	run network-graph.js [OPTIONS] [DEVICE] | |||||||
| 	DEVICE			 Point to start scan from, defaults to current machine | 	DEVICE			 Point to start scan from, defaults to current machine | ||||||
|  |  | ||||||
| Options: | Options: | ||||||
| 	-d --depth		 Depth to scan to, defaults to 3 | 	-d --depth		 Depth to scan to, defaults is 3 | ||||||
| 	-f --filter		 Display devices matching name | 	-f --filter		 Filter to device matching name | ||||||
| 	-r --regex		 Display devices matching pattern | 	-e --regex		 Filter to devices matching pattern | ||||||
| 	-v --verbose		 Displays the required hack level & ports needed to root: (level|port) | 	-r --rooted		 Filter to devices that have been rooted | ||||||
|  | 	-n --not-rooted		 Filter to devices that have not been rooted | ||||||
|  | 	-v --verbose		 Display the required hack level & number of ports to root: (level|port) | ||||||
| 	-h --help		 Display this help message | 	-h --help		 Display this help message | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| @@ -204,24 +205,3 @@ Options: | |||||||
| 	--no-banner		 Hide the banner (Used internally) | 	--no-banner		 Hide the banner (Used internally) | ||||||
| 	-h --help		 Display this help message | 	-h --help		 Display this help message | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| ### [vanguard.js](./scripts/vanguard.js) |  | ||||||
| **RAM:** 1.90 GB |  | ||||||
|  |  | ||||||
| Weaken a device indefinitely. |  | ||||||
| ``` |  | ||||||
| [home ~/scripts]> run /scripts/vanguard.js --help |  | ||||||
| Running script with 1 thread(s), pid 1 and args: ["--help"]. |  | ||||||
| /scripts/vanguard.js:  |  | ||||||
|  |  | ||||||
| Weaken a device indefinitely. |  | ||||||
|  |  | ||||||
| Usage:	run vanguard.js [OPTIONS] [DEVICE] |  | ||||||
| 	run vanguard.js --help |  | ||||||
|  |  | ||||||
| 	DEVICE			 Device to weaken, defaults to the current machine |  | ||||||
|  |  | ||||||
| Options: |  | ||||||
| 	-l --limit		 Limit the number of times to run |  | ||||||
| 	-h --help		 Display this help message |  | ||||||
| ``` |  | ||||||
|   | |||||||
| @@ -1,48 +1,38 @@ | |||||||
| import {ArgError, ArgParser} from './scripts/lib/arg-parser'; | import {ArgError, ArgParser} from '/scripts/lib/arg-parser'; | ||||||
| import {terminal} from './scripts/lib/utils'; | import {pruneTree, scanNetwork, terminal} from '/scripts/lib/utils'; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Connect to a device on a different network. |  * BitBurner autocomplete | ||||||
|  |  * @param data {server: string[], txts: string[], scripts: string[], flags: string[]} - Contextual information | ||||||
|  |  * @returns {string[]} - Pool of autocomplete options | ||||||
|  |  */ | ||||||
|  | export function autocomplete(data) { | ||||||
|  | 	return [...data.servers]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Search the network for a device and connect to it. | ||||||
|  * @param ns {NS} - BitBurner API |  * @param ns {NS} - BitBurner API | ||||||
|  */ |  */ | ||||||
| export function main(ns) { | export function main(ns) { | ||||||
| 	// Setup | 	// Setup | ||||||
| 	ns.disableLog('ALL'); | 	ns.disableLog('ALL'); | ||||||
| 	let args; | 	const argParser = new ArgParser('connect.js', 'Search the network for a device and connect to it.', null, [ | ||||||
| 	const argParser = new ArgParser('connect.js', 'Connect to a device on a different network.', null, [ |  | ||||||
| 		{name: 'device', desc: 'Device to connect to', default: ns.getHostname(), type: 'string'} | 		{name: 'device', desc: 'Device to connect to', default: ns.getHostname(), type: 'string'} | ||||||
| 	]); | 	]); | ||||||
|  |  | ||||||
| 	try { | 	try { | ||||||
| 		args = argParser.parse(ns.args); | 		// Run | ||||||
|  | 		const args = argParser.parse(ns.args); | ||||||
|  | 		const [devices, network] = scanNetwork(ns); | ||||||
|  | 		pruneTree(network, d => d == args['device']); | ||||||
|  | 		let current = network, name; | ||||||
|  | 		while(name = Object.keys(current)[0]) { | ||||||
|  | 			terminal(`connect ${name}`); | ||||||
|  | 			current = current[name]; | ||||||
|  | 		} | ||||||
| 	} catch(err) { | 	} catch(err) { | ||||||
| 		if(err instanceof ArgError) return ns.tprint(argParser.help(err.message)); | 		if(err instanceof ArgError) return ns.tprint(argParser.help(err.message)); | ||||||
| 		throw err; | 		throw err; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * Find path to a device recursively |  | ||||||
| 	 * @param device {string} - Device to locate |  | ||||||
| 	 * @param current {string} - Current device to scan |  | ||||||
| 	 * @param path {string[]} - Path the the current device |  | ||||||
| 	 * @param all {Set} - Stop devices from being scanned |  | ||||||
| 	 * @returns {string[]} - Path to the located device |  | ||||||
| 	 */ |  | ||||||
| 	function find(device, current = 'home', path = [current], blacklist = new Set()) { |  | ||||||
| 		blacklist.add(current); |  | ||||||
| 		const newDevices = ns.scan(current).filter(d => !blacklist.has(d)); |  | ||||||
| 		if(newDevices.length == 0) return []; |  | ||||||
| 		if(newDevices.find(d => d == device)) return [...path, device]; |  | ||||||
| 		return newDevices.map(d => find(device, d, [...path, d], blacklist)).find(p => p && p.length); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// Run |  | ||||||
| 	const path = find(args['device']); |  | ||||||
| 	path.splice(0, 1); // Delete 'home' from from the path |  | ||||||
| 	for(let d of path) { |  | ||||||
| 		terminal(`connect ${d}`); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export function autocomplete(data) { |  | ||||||
| 	return [...data.servers]; |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,4 +1,14 @@ | |||||||
| import {ArgError, ArgParser} from './scripts/lib/arg-parser'; | import {ArgError, ArgParser} from '/scripts/lib/arg-parser'; | ||||||
|  | import {scanNetwork} from '/scripts/lib/utils'; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * BitBurner autocomplete | ||||||
|  |  * @param data {server: string[], txts: string[], scripts: string[], flags: string[]} - Contextual information | ||||||
|  |  * @returns {string[]} - Pool of autocomplete options | ||||||
|  |  */ | ||||||
|  | export function autocomplete(data) { | ||||||
|  | 	return [...data.scripts, '{{TARGET}}']; | ||||||
|  | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Search the network for targets to execute a script against. |  * Search the network for targets to execute a script against. | ||||||
| @@ -13,45 +23,24 @@ export async function main(ns) { | |||||||
| 		{name: 'cpu', desc: 'Number of CPU threads to use with script', flags: ['-c', '--cpu'], default: 1, type: 'num'}, | 		{name: 'cpu', desc: 'Number of CPU threads to use with script', flags: ['-c', '--cpu'], default: 1, type: 'num'}, | ||||||
| 		{name: 'depth', desc: 'Depth to scan to, defaults to 3', flags: ['-d', '--depth'], default: Infinity, type: 'num'}, | 		{name: 'depth', desc: 'Depth to scan to, defaults to 3', flags: ['-d', '--depth'], default: Infinity, type: 'num'}, | ||||||
| 		{name: 'level', desc: 'Exclude targets with higher hack level, defaults to current hack level', flags: ['-l', '--level'], default: ns.getHackingLevel(),  type: 'num'}, | 		{name: 'level', desc: 'Exclude targets with higher hack level, defaults to current hack level', flags: ['-l', '--level'], default: ns.getHackingLevel(),  type: 'num'}, | ||||||
| 		{name: 'ports', desc: 'Exclute targets with too many closed ports', flags: ['-p', '--ports'], optional: true, default: Infinity, type: 'num'}, | 		{name: 'ports', desc: 'Exclute targets with too many closed ports', flags: ['-p', '--ports'], default: Infinity, type: 'num'}, | ||||||
|  | 		{name: 'silent', desc: 'Surpress program output', flags: ['-s', '--silent'], type: 'bool'} | ||||||
| 	], true); | 	], true); | ||||||
| 	let args; |  | ||||||
| 	try { | 	try { | ||||||
| 		args = argParser.parse(ns.args); |  | ||||||
| 	} catch(err) { |  | ||||||
| 		if(err instanceof ArgError) return ns.tprint(argParser.help(err.message)); |  | ||||||
| 		throw err; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * Recursively search network & build a tree |  | ||||||
| 	 * @param host {string} - Point to scan from |  | ||||||
| 	 * @param depth {number} - Current scanning depth |  | ||||||
| 	 * @param blacklist {String[]} - Devices already discovered |  | ||||||
| 	 * @returns Dicionary of discovered devices |  | ||||||
| 	 */ |  | ||||||
| 	function scan(target = 'home', depth = 1, found = new Set()) { |  | ||||||
| 		if(found.size == 0) found.add(target); |  | ||||||
| 		ns.scan(target).filter(t => !found.has(t)).forEach(t => { |  | ||||||
| 			found.add(t); |  | ||||||
| 			scan(t, depth + 1, found); |  | ||||||
| 		}); |  | ||||||
| 		found.delete('home'); |  | ||||||
| 		return Array.from(found); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 		// Run | 		// Run | ||||||
| 	const targets = scan(); | 		const args = argParser.parse(ns.args); | ||||||
|  | 		const [devices, network] = scanNetwork(ns); | ||||||
| 		let complete = 0, failed = 0, skipped = 0; | 		let complete = 0, failed = 0, skipped = 0; | ||||||
| 	for(let target of targets) { | 		for(let device of devices) { | ||||||
| 		if(target == 'home') continue; | 			// Skip invalid devices | ||||||
|  | 			if(device == 'home' || args['level'] < ns.getServerRequiredHackingLevel(device) || args['ports'] < ns.getServerNumPortsRequired(device)) { | ||||||
| 		if(args['level'] < ns.getServerRequiredHackingLevel(target) || args['ports'] < ns.getServerNumPortsRequired(target)) { |  | ||||||
| 				skipped++; | 				skipped++; | ||||||
| 				continue; | 				continue; | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 		const scriptArgs = args['args'].map(arg => arg == '{{TARGET}}' ? target : arg); | 			// Start script | ||||||
|  | 			const scriptArgs = args['args'].map(arg => arg.toUpperCase() == '{{TARGET}}' ? device : arg); | ||||||
| 			const pid = ns.run(args['script'], args['cpu'], ...scriptArgs); | 			const pid = ns.run(args['script'], args['cpu'], ...scriptArgs); | ||||||
| 			if(pid == 0) { | 			if(pid == 0) { | ||||||
| 				failed++; | 				failed++; | ||||||
| @@ -59,14 +48,21 @@ export async function main(ns) { | |||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			// Wait for script to finish | 			// Wait for script to finish | ||||||
|         do { await ns.sleep(1000); } | 			while(ns.scriptRunning(args['script'], 'home')) | ||||||
|         while(ns.scriptRunning(args['script'], 'home')); | 				await ns.sleep(1000); | ||||||
| 			complete++; | 			complete++; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		// Output report | 		// Output report | ||||||
|  | 		if(!args['silent']) { | ||||||
| 			ns.tprint('==================================================='); | 			ns.tprint('==================================================='); | ||||||
| 	ns.tprint(`Crawler Report: ${targets.length} Device${targets.length > 1 ? 's' : ''}`); | 			ns.tprint(`Crawler Report: ${devices.length} Device${devices.length > 1 ? 's' : ''}`); | ||||||
| 			ns.tprint('==================================================='); | 			ns.tprint('==================================================='); | ||||||
| 			ns.tprint(`Complete: ${complete}\tFailed: ${failed}\tSkipped: ${skipped}`); | 			ns.tprint(`Complete: ${complete}\tFailed: ${failed}\tSkipped: ${skipped}`); | ||||||
| 			ns.tprint(''); | 			ns.tprint(''); | ||||||
| 		} | 		} | ||||||
|  | 	} catch(err) { | ||||||
|  | 		if(err instanceof ArgError) return ns.tprint(argParser.help(err.message)); | ||||||
|  | 		throw err; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
| @@ -7,37 +7,17 @@ import {ArgError, ArgParser} from './scripts/lib/arg-parser'; | |||||||
| export async function main(ns) { | export async function main(ns) { | ||||||
| 	// Setup | 	// Setup | ||||||
| 	ns.disableLog('ALL'); | 	ns.disableLog('ALL'); | ||||||
| 	const historyLength = 17; |  | ||||||
| 	const messageHistory = Array(historyLength).fill(''); |  | ||||||
| 	let args, nodeCount = ns.hacknet.numNodes(); |  | ||||||
| 	const argParser = new ArgParser('hacknet-manager.js', 'Buy, upgrade & manage Hacknet nodes automatically. Tail for live updates.', null, [ | 	const argParser = new ArgParser('hacknet-manager.js', 'Buy, upgrade & manage Hacknet nodes automatically. Tail for live updates.', null, [ | ||||||
| 		{name: 'limit', desc: 'Limit the number of nodes the manager will buy, defaults to 8', optional: true, default: 8, type: 'num'}, | 		{name: 'limit', desc: 'Limit the number of nodes the manager will buy, defaults to 8', optional: true, default: 8, type: 'num'}, | ||||||
| 		{name: 'balance', desc: 'Prevent spending bellow point', flags: ['-b', '--balance'], type: 'num'} | 		{name: 'balance', desc: 'Prevent spending bellow point', flags: ['-b', '--balance'], type: 'num'}, | ||||||
|  | 		{name: 'sleep', desc: 'Amount of time to wait between purchases, defaults to 1 (second)', flags: ['-s', '--sleep'], default: 1, type: 'num'} | ||||||
|     ]); |     ]); | ||||||
|  |  | ||||||
| 	try { | 	try { | ||||||
| 		args = argParser.parse(ns.args); |  | ||||||
| 	} catch(err) { |  | ||||||
| 		if(err instanceof ArgError) return ns.tprint(argParser.help(err.message)); |  | ||||||
| 		throw err; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * Print header with logs |  | ||||||
| 	 * @param message - message to append to logs |  | ||||||
| 	 */ |  | ||||||
| 	function log(message) { |  | ||||||
| 		ns.clearLog(); |  | ||||||
| 		ns.print('==================================================='); |  | ||||||
| 		ns.print(`Hacknet Manager: ${nodeCount}/${args['limit']} Nodes`); |  | ||||||
| 		ns.print('==================================================='); |  | ||||||
| 		if(message != null) messageHistory.push(message); |  | ||||||
| 		messageHistory.splice(0, messageHistory.length - historyLength); |  | ||||||
| 		messageHistory.forEach(m => ns.print(m)); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 		// Run | 		// Run | ||||||
| 	log(); | 		const args = argParser.parse(ns.args); | ||||||
|  | 		let nodeCount = ns.hacknet.numNodes(); | ||||||
|  | 		const logger = new Logger(ns, () => [() => `Hacknet Manager: ${nodeCount}/${args['limit']}`]); | ||||||
| 		while(true) { | 		while(true) { | ||||||
| 			const balance = ns.getServerMoneyAvailable('home'); | 			const balance = ns.getServerMoneyAvailable('home'); | ||||||
|  |  | ||||||
| @@ -45,7 +25,7 @@ export async function main(ns) { | |||||||
| 			if(nodeCount < args['limit'] && balance - ns.hacknet.getPurchaseNodeCost() >= args['balance']) { | 			if(nodeCount < args['limit'] && balance - ns.hacknet.getPurchaseNodeCost() >= args['balance']) { | ||||||
| 				nodeCount++; | 				nodeCount++; | ||||||
| 				ns.hacknet.purchaseNode(); | 				ns.hacknet.purchaseNode(); | ||||||
| 			log(`Buying Node ${nodeCount}`); | 				logger.log(`Buying Node ${nodeCount}`); | ||||||
| 			} else { | 			} else { | ||||||
| 				// Create an ordered list of nodes by their cheapest upgrade | 				// Create an ordered list of nodes by their cheapest upgrade | ||||||
| 				const nodes = Array(nodeCount).fill(null) | 				const nodes = Array(nodeCount).fill(null) | ||||||
| @@ -92,12 +72,16 @@ export async function main(ns) { | |||||||
| 				// Apply the cheapest upgrade | 				// Apply the cheapest upgrade | ||||||
| 				if(nodes.length && balance - nodes[0].bestUpgrade.cost >= args['balance']) { | 				if(nodes.length && balance - nodes[0].bestUpgrade.cost >= args['balance']) { | ||||||
| 					const cost = Math.round(nodes[0].bestUpgrade.cost * 100) / 100; | 					const cost = Math.round(nodes[0].bestUpgrade.cost * 100) / 100; | ||||||
| 				log(`Node ${nodes[0].index} - ${nodes[0].bestUpgrade.name} ${nodes[0][nodes[0].bestUpgrade.name] + 1} - $${cost}`); | 					logger.log(`Node ${nodes[0].index} - ${nodes[0].bestUpgrade.name} ${nodes[0][nodes[0].bestUpgrade.name] + 1} - $${cost}`); | ||||||
| 					nodes[0].bestUpgrade.purchase(); | 					nodes[0].bestUpgrade.purchase(); | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			// Check again in 1s | 			// Check again in 1s | ||||||
| 		await ns.sleep(1000); | 			await ns.sleep(args['sleep'] * 1000); | ||||||
|  | 		} | ||||||
|  | 	} catch(err) { | ||||||
|  | 		if(err instanceof ArgError) return ns.tprint(argParser.help(err.message)); | ||||||
|  | 		throw err; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -12,6 +12,7 @@ export class Logger { | |||||||
| 		this.fns = lineFns; | 		this.fns = lineFns; | ||||||
| 		this.historyLen -= fns.length * 2; | 		this.historyLen -= fns.length * 2; | ||||||
| 		this.history = Array(this.historyLen).fill(''); | 		this.history = Array(this.historyLen).fill(''); | ||||||
|  | 		this.log(); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|   | |||||||
| @@ -1,7 +1,31 @@ | |||||||
|  | /** | ||||||
|  |  * Copy a file & scan it for dependencies copying them as well. | ||||||
|  |  * @param ns {NS} - BitBurner API | ||||||
|  |  * @param src {string} - File to scan & copy | ||||||
|  |  * @param device {string} - Device to copy files to | ||||||
|  |  * @returns {string[]} - Array of coppied files | ||||||
|  |  */ | ||||||
|  | export async function copyWithDependencies(ns, src, device) { | ||||||
|  | 	const queue = [src], found = [src]; | ||||||
|  | 	while(queue.length) { | ||||||
|  | 		const file = queue.splice(0, 1)[0]; | ||||||
|  | 		const imports = new RegExp(/from ["']\.?(\/.+)["']/g); | ||||||
|  | 		const script = await ns.read(file); | ||||||
|  | 		let match; | ||||||
|  | 		while((match = imports.exec(script)) != null) { | ||||||
|  | 			const path = `${match[1]}.js`; | ||||||
|  | 			queue.push(path); | ||||||
|  | 			found.push(path); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	await ns.scp(found, device); | ||||||
|  | 	return found; | ||||||
|  | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Print a download bar to the terminal. |  * Print a download bar to the terminal. | ||||||
|  * @params ns {NS} - Bitburner API |  * @param ns {NS} - BitBurner API | ||||||
|  * @params file - Filename to display with progress bar |  * @param file - Filename to display with progress bar | ||||||
|  */ |  */ | ||||||
| export async function downloadPrint(ns, file) { | export async function downloadPrint(ns, file) { | ||||||
| 	const speed = ~~(Math.random() * 100) / 10; | 	const speed = ~~(Math.random() * 100) / 10; | ||||||
| @@ -9,12 +33,50 @@ export async function downloadPrint(ns, file) { | |||||||
| 	await slowPrint(ns, `${file}${spacing}[==================>] 100%\t(${speed} MB/s)`); | 	await slowPrint(ns, `${file}${spacing}[==================>] 100%\t(${speed} MB/s)`); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | export function flatten(object) { | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * **Impure:** Prune tree down to keys which pass function | ||||||
|  |  * @param tree {object} - Tree to search | ||||||
|  |  * @param fn {(key: string) => boolean} - Function to test each key with | ||||||
|  |  * @returns {boolean} - True if a match was found | ||||||
|  |  */ | ||||||
|  | export function pruneTree(tree, fn) { | ||||||
|  | 	return !!Object.keys(tree).map(k => { | ||||||
|  | 		let matches = fn(k), children = Object.keys(k), childrenMatch = false; | ||||||
|  | 		if(children.length) childrenMatch = pruneTree(tree[k], fn); | ||||||
|  | 		if(!childrenMatch && !matches) delete tree[k]; | ||||||
|  | 		return childrenMatch || matches; | ||||||
|  | 	}).find(el => el); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Scan the network of a given device. | ||||||
|  |  * @param ns {NS} - BitBurner API | ||||||
|  |  * @param device {string} - Device network that will be scanned | ||||||
|  |  * @param maxDepth - Depth to scan to | ||||||
|  |  * @returns {[string[], Object]} - A tuple including an array of discovered devices & a tree of the network | ||||||
|  |  */ | ||||||
|  | export function scanNetwork(ns, device = ns.getHostname(), maxDepth = Infinity) { | ||||||
|  | 	let discovered = [device]; | ||||||
|  | 	function scan (device, depth = 1) { | ||||||
|  | 		if(depth > maxDepth) return {}; | ||||||
|  | 		const localTargets = ns.scan(device).filter(newDevice => !discovered.includes(newDevice)); | ||||||
|  | 		discovered = [...discovered, ...localTargets]; | ||||||
|  | 		return localTargets.reduce((acc, device) => ({...acc, [device]: scan(device, depth + 1)}), {}); | ||||||
|  | 	} | ||||||
|  | 	const network = scan(device); | ||||||
|  | 	return [discovered.slice(1), network]; | ||||||
|  | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Print text to the terminal & then delay for a random amount of time to emulate execution time. |  * Print text to the terminal & then delay for a random amount of time to emulate execution time. | ||||||
|  * @params ns {NS} - Bitburner API |  * @param ns {NS} - BitBurner API | ||||||
|  * @params message {string} - Text to display |  * @param message {string} - Text to display | ||||||
|  * @params min {number} - minimum amount of time to wait after printing text |  * @param min {number} - minimum amount of time to wait after printing text | ||||||
|  * @params max {number} - maximum amount of time to wait after printing text |  * @param max {number} - maximum amount of time to wait after printing text | ||||||
|  */ |  */ | ||||||
| export async function slowPrint(ns, message, min = 0.5, max = 1.5) { | export async function slowPrint(ns, message, min = 0.5, max = 1.5) { | ||||||
| 	const time = ~~(Math.random() * (max * 1000 - min * 1000)) + min * 1000; | 	const time = ~~(Math.random() * (max * 1000 - min * 1000)) + min * 1000; | ||||||
| @@ -24,7 +86,7 @@ export async function slowPrint(ns, message, min = 0.5, max = 1.5) { | |||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Write a command to the terminal. |  * Write a command to the terminal. | ||||||
|  * @params command {string} - Command that will be run |  * @param command {string} - Command that will be run | ||||||
|  * @returns {string} - Response |  * @returns {string} - Response | ||||||
|  */ |  */ | ||||||
| export async function terminal(command) { | export async function terminal(command) { | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ export async function main(ns) { | |||||||
| 	const historyLength = 15; | 	const historyLength = 15; | ||||||
| 	const messageHistory = Array(historyLength).fill(''); | 	const messageHistory = Array(historyLength).fill(''); | ||||||
| 	let maxBalance, balance, minSecurity, security; | 	let maxBalance, balance, minSecurity, security; | ||||||
| 	const argParser = new ArgParser('miner.js', 'Weaken, Grow, Hack loop to "mine" machine for money.', null, [ | 	const argParser = new ArgParser('miner.js', 'Weaken, Grow, Hack loop to "mine" machine for money. Tail for live updates', null, [ | ||||||
| 		{name: 'device', desc: 'Device to mine, defaults to current machine', optional: true, default: ns.getHostname(), type: 'string'} | 		{name: 'device', desc: 'Device to mine, defaults to current machine', optional: true, default: ns.getHostname(), type: 'string'} | ||||||
| 	]); | 	]); | ||||||
| 	let args; | 	let args; | ||||||
|   | |||||||
| @@ -1,14 +1,30 @@ | |||||||
| import {ArgError, ArgParser} from './scripts/lib/arg-parser'; | import {ArgError, ArgParser} from '/scripts/lib/arg-parser'; | ||||||
|  | import {pruneTree, scanNetwork} from '/scripts/lib/utils'; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * BitBurner autocomplete | ||||||
|  |  * @param data {server: string[], txts: string[], scripts: string[], flags: string[]} - Contextual information | ||||||
|  |  * @returns {string[]} - Pool of autocomplete options | ||||||
|  |  */ | ||||||
|  | export function autocomplete(data) { | ||||||
|  | 	return [...data.servers]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Scan the network for devices and display as an ASCII tree. | ||||||
|  |  * @param ns {NS} - BitBurner API | ||||||
|  |  */ | ||||||
| export async function main(ns) { | export async function main(ns) { | ||||||
| 	// Setup | 	// Setup | ||||||
| 	ns.disableLog('ALL'); | 	ns.disableLog('ALL'); | ||||||
| 	const argParser = new ArgParser('network-graph.js', 'Scan the network for devices and display as an ASCII tree:\n home\n  ├─ n00dles (ROOTED)\n  |   └─ max-hardware (80|1)\n  |       └─ neo-net (50|1)\n  ├─ foodnstuff (ROOTED)\n  └─ sigma-cosmetics (ROOTED)', null, [ | 	const argParser = new ArgParser('network-graph.js', 'Scan the network for devices and display as an ASCII tree:\n home\n  ├─ n00dles (ROOTED)\n  |   └─ max-hardware (80|1)\n  |       └─ neo-net (50|1)\n  ├─ foodnstuff (ROOTED)\n  └─ sigma-cosmetics (ROOTED)', null, [ | ||||||
| 		{name: 'device', desc: 'Point to start scan from, defaults to current machine', optional: true, default: ns.getHostname(), type: 'string'}, | 		{name: 'device', desc: 'Point to start scan from, defaults to current machine', optional: true, default: ns.getHostname(), type: 'string'}, | ||||||
| 		{name: 'depth', desc: 'Depth to scan to, defaults to 3', flags: ['-d', '--depth'], default: Infinity, type: 'num'}, | 		{name: 'depth', desc: 'Depth to scan to, defaults is 3', flags: ['-d', '--depth'], default: Infinity, type: 'num'}, | ||||||
|         {name: 'filter', desc: 'Display devices matching name', flags: ['-f', '--filter'], type: 'string'}, | 		{name: 'filter', desc: 'Filter to device matching name', flags: ['-f', '--filter'], type: 'string'}, | ||||||
| 		{name: 'regex', desc: 'Display devices matching pattern', flags: ['-r', '--regex'], type: 'string'}, | 		{name: 'regex', desc: 'Filter to devices matching pattern', flags: ['-e', '--regex'], type: 'string'}, | ||||||
| 		{name: 'verbose', desc: 'Displays the required hack level & ports needed to root: (level|port)', flags: ['-v', '--verbose'], type: 'bool'}, | 		{name: 'rooted', desc: 'Filter to devices that have been rooted', flags: ['-r', '--rooted'], type: 'bool'}, | ||||||
|  | 		{name: 'notRooted', desc: 'Filter to devices that have not been rooted', flags: ['-n', '--not-rooted'], type: 'bool'}, | ||||||
|  | 		{name: 'verbose', desc: 'Display the required hack level & number of ports to root: (level|port)', flags: ['-v', '--verbose'], type: 'bool'}, | ||||||
| 	]); | 	]); | ||||||
| 	let args; | 	let args; | ||||||
| 	try { | 	try { | ||||||
| @@ -18,81 +34,31 @@ export async function main(ns) { | |||||||
| 		throw err; | 		throw err; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * Prune tree down to devices that match name or pattern. |  | ||||||
| 	 * @param tree {object} - Tree to search |  | ||||||
| 	 * @param find {string} - Device name or pattern to search for |  | ||||||
| 	 * @param regex {boolean} - True to use regex, false for raw check |  | ||||||
| 	 * @returns {object} - Pruned tree |  | ||||||
| 	 */ |  | ||||||
| 	function filter(tree, find, regex = false) { |  | ||||||
| 		const found = new Set(); |  | ||||||
| 		function buildWhitelist(tree, find, path = []) { |  | ||||||
| 			const keys = Object.keys(tree); |  | ||||||
| 			if(!keys.length) return; |  | ||||||
| 			Object.keys(tree).forEach(n => { |  | ||||||
| 				const matches = regex ? new RegExp(find).test(n) : n == find; |  | ||||||
| 				if(n == 'n00dles') console.log(n, find, matches); |  | ||||||
| 				if(matches) { |  | ||||||
| 					found.add(n); |  | ||||||
| 					path.forEach(p => found.add(p)); |  | ||||||
| 				} |  | ||||||
| 				buildWhitelist(tree[n], find, [...path, n]); |  | ||||||
| 			}) |  | ||||||
| 		} |  | ||||||
| 		function prune(tree, whitelist) { |  | ||||||
| 			Object.keys(tree).forEach(n => { |  | ||||||
| 				if(Object.keys(tree[n]).length) prune(tree[n], whitelist); |  | ||||||
| 				if(!whitelist.includes(n)) delete tree[n]; |  | ||||||
| 			}); |  | ||||||
| 		} |  | ||||||
| 		buildWhitelist(tree, find); |  | ||||||
| 		prune(tree, Array.from(found)); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * Recursively search network & build a tree |  | ||||||
| 	 * @param host {string} - Point to scan from |  | ||||||
| 	 * @param depth {number} - Current scanning depth |  | ||||||
| 	 * @param blacklist {String[]} - Devices already discovered |  | ||||||
| 	 * @returns Dicionary of discovered devices |  | ||||||
| 	 */ |  | ||||||
| 	function scan(host, depth = 1, blacklist = [host]) { |  | ||||||
| 		if(depth > args['depth']) return {}; |  | ||||||
| 		const localTargets = ns.scan(host).filter(target => !blacklist.includes(target)); |  | ||||||
| 		blacklist = [...blacklist, ...localTargets]; |  | ||||||
| 		return localTargets.reduce((acc, target) => { |  | ||||||
| 			const info = ns.getServer(target); |  | ||||||
| 			const verb = args['verbose'] ? ` (${info.hasAdminRights ? 'ROOTED' : `${info.requiredHackingSkill}|${info.numOpenPortsRequired}`})` : ''; |  | ||||||
| 			const name = `${target}${verb}`; |  | ||||||
| 			acc[name] = scan(target, depth + 1, blacklist); |  | ||||||
| 			return acc; |  | ||||||
| 		}, {}); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	/** | 	/** | ||||||
| 	 * Iterate tree & print to screen | 	 * Iterate tree & print to screen | ||||||
| 	 * @param tree {object} - Tree to parse | 	 * @param tree {Object} - Tree to parse | ||||||
|  | 	 * @param stats {Object} - Pool of stats to pull extra information from | ||||||
| 	 * @param spacer {string} - Spacer text for tree formatting | 	 * @param spacer {string} - Spacer text for tree formatting | ||||||
| 	 */ | 	 */ | ||||||
| 	function render(tree, spacer = ' ') { | 	function render(tree, stats, spacer = ' ') { | ||||||
| 		Object.keys(tree).forEach((key, i, arr) => { | 		Object.keys(tree).forEach((device, i, arr) => { | ||||||
|  | 			const deviceStats = stats ? stats[device] : null; | ||||||
|  | 			const stat = deviceStats ? ` (${deviceStats.hasAdminRights ? 'ROOTED' : `${deviceStats.requiredHackingSkill}|${deviceStats.numOpenPortsRequired}`})` : ''; | ||||||
| 			const last = i == arr.length - 1; | 			const last = i == arr.length - 1; | ||||||
| 			const branch = last ? '└─ ' : '├─ '; | 			const branch = last ? '└─ ' : '├─ '; | ||||||
| 			ns.tprint(`${spacer}${branch}${key}`); | 			ns.tprint(spacer + branch + device + stat); | ||||||
| 			render(tree[key], spacer + (last ? '    ' : '|   ')); | 			render(tree[device], stats, spacer + (last ? '    ' : '|   ')); | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Run | 	// Run | ||||||
|  | 	const [devices, network] = scanNetwork(ns, args['device'], args['depth']); | ||||||
|  | 	const stats = devices.reduce((acc, d) => ({...acc, [d]: ns.getServer(d)}), {}); | ||||||
|  | 	if(args['regex']) pruneTree(network, d => RegExp(args['regex']).test(d)); // Regex flag | ||||||
|  | 	else if(args['filter']) pruneTree(network, d => d == args['filter']); // Filter flag | ||||||
|  | 	if(args['rooted']) pruneTree(network, d => stats[d].hasAdminRights); // Rooted flag | ||||||
|  | 	else if(args['notRooted']) pruneTree(network, d => !stats[d].hasAdminRights); // Not rooted flag | ||||||
| 	ns.tprint(args['device']); | 	ns.tprint(args['device']); | ||||||
| 	const found = scan(args['device']); | 	render(network, args['verbose'] ? stats : null); | ||||||
| 	if(args['regex']) filter(found, args['regex'], true); |  | ||||||
| 	else if(args['filter']) filter(found, args['filter']); |  | ||||||
| 	render(found); |  | ||||||
| 	ns.tprint(''); | 	ns.tprint(''); | ||||||
| } | } | ||||||
|  |  | ||||||
| export function autocomplete(data) { |  | ||||||
| 	return [...data.servers]; |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -136,8 +136,7 @@ export async function main(ns) { | |||||||
| 		'hacknet-manager.js', | 		'hacknet-manager.js', | ||||||
|         'miner.js', |         'miner.js', | ||||||
| 		'network-graph.js', | 		'network-graph.js', | ||||||
|         'rootkit.js', |         'rootkit.js' | ||||||
| 		'vanguard.js' |  | ||||||
|     ]; |     ]; | ||||||
| 	let args; | 	let args; | ||||||
| 	try { | 	try { | ||||||
|   | |||||||
| @@ -1,54 +0,0 @@ | |||||||
| import {ArgError, ArgParser} from './scripts/lib/arg-parser'; |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Weaken a device indefinitely. |  | ||||||
|  * @params ns {NS} - BitBurner API |  | ||||||
|  */ |  | ||||||
| export async function main(ns) { |  | ||||||
| 	// Setup |  | ||||||
| 	ns.disableLog('ALL'); |  | ||||||
| 	let args, counter = 0, orgSecurity, security; |  | ||||||
| 	const historyLength = 17; |  | ||||||
| 	const messageHistory = Array(historyLength).fill(''); |  | ||||||
| 	const argParser = new ArgParser('vanguard.js', 'Weaken a device indefinitely.', null, [ |  | ||||||
| 		{name: 'device', desc: 'Device to weaken, defaults to the current machine', optional: true, default: ns.getHostname(), type: 'string'}, |  | ||||||
| 		{name: 'limit', desc: 'Limit the number of times to run', flags: ['-l', '--limit'], default: Infinity, type: 'num'} |  | ||||||
| 	]); |  | ||||||
| 	try { |  | ||||||
| 		args = argParser.parse(ns.args); |  | ||||||
| 		orgSecurity = security = ns.getServerSecurityLevel(args['device']); |  | ||||||
| 	} catch(err) { |  | ||||||
| 		if(err instanceof ArgError) return ns.tprint(argParser.help(err.message)); |  | ||||||
| 		throw err; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * Print header with logs |  | ||||||
| 	 * @param message - message to append to logs |  | ||||||
| 	 */ |  | ||||||
| 	function log(message) { |  | ||||||
| 		ns.clearLog(); |  | ||||||
| 		ns.print('==================================================='); |  | ||||||
| 		ns.print(`Vanguard: ${args['device']}`); |  | ||||||
| 		ns.print('==================================================='); |  | ||||||
| 		ns.print(`Security: ${security}/${orgSecurity}`); |  | ||||||
| 		ns.print('==================================================='); |  | ||||||
| 		if(message != null) messageHistory.push(message); |  | ||||||
| 		messageHistory.splice(0, messageHistory.length - historyLength); |  | ||||||
| 		messageHistory.forEach(m => ns.print(m)); |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	// Run |  | ||||||
| 	log(); |  | ||||||
| 	do { |  | ||||||
| 		security = ns.getServerSecurityLevel(args['device']); |  | ||||||
| 		log(`Attacking...`); |  | ||||||
| 		log(await ns.weaken(args['device'])); |  | ||||||
| 		counter++; |  | ||||||
| 	} while (counter < args['limit']); |  | ||||||
| 	ns.print('Complete!'); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export function autocomplete(data) { |  | ||||||
| 	return [...data.servers]; |  | ||||||
| } |  | ||||||
		Reference in New Issue
	
	Block a user