Merge pull request #452 from danielyxie/wget

Wget
This commit is contained in:
danielyxie 2018-09-09 17:34:52 -05:00 committed by GitHub
commit 4b9b3f9002
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 169 additions and 49 deletions

@ -56,6 +56,12 @@
display: flex; display: flex;
} }
#terminal-input-td textarea {
overflow: hidden;
resize:none;
height:auto;
}
#terminal-input-header { #terminal-input-header {
white-space: pre; white-space: pre;
} }

@ -17,9 +17,9 @@ v0.40.2 - 8/27/2018
* There are now, on average, more Synthoid communities in a city * There are now, on average, more Synthoid communities in a city
* If automation is enabled (the feature in Bladeburner console), then switching to another action such as working for a company will now disable the automation * If automation is enabled (the feature in Bladeburner console), then switching to another action such as working for a company will now disable the automation
* Stock Market Changes: * Stock Market Changes:
*Added a watchlist filter feature to the UI that allows you to specify which stocks to show * Added a watchlist filter feature to the UI that allows you to specify which stocks to show
*Added the Four Sigma (4S) Market Data feed, which provides volatility and price forecast information about stocks * Added the Four Sigma (4S) Market Data feed, which provides volatility and price forecast information about stocks
*Added the 4S Market Data TIX API, which lets you access the aforementioned data through Netscript * Added the 4S Market Data TIX API, which lets you access the aforementioned data through Netscript
* There is now a setting for enabling/disabling the popup that appears when you are hospitalized * There is now a setting for enabling/disabling the popup that appears when you are hospitalized
* Bug Fix: Stock market should now be correctly initialized in BitNode-8 (by Kline-) * Bug Fix: Stock market should now be correctly initialized in BitNode-8 (by Kline-)
* Bug Fix: bladeburner.getCurrentAction() should now properly an 'Idle' object rather than null (by Kline-) * Bug Fix: bladeburner.getCurrentAction() should now properly an 'Idle' object rather than null (by Kline-)

@ -412,3 +412,18 @@ Then it could be removed using::
$ unalias "r" $ unalias "r"
It is not necessary to differentiate between global and non-global aliases when using 'unalias' It is not necessary to differentiate between global and non-global aliases when using 'unalias'
wget
^^^^
$ wget [url] [target file]
Retrieves data from a url and downloads it to a file on the current server.
The data can only be downloaded to a script (.script, .ns, .js) or a text file
(.txt). If the target file already exists, it will be overwritten by this command.
Note that will not be possible to download data from many websites because they
do not allow cross-origin origin sharing (CORS). This includes websites such
as gist and pastebin. One notable site it will work on is rawgithub. Example::
$ wget https://raw.githubusercontent.com/danielyxie/bitburner/master/README.md game_readme.txt

@ -101,7 +101,7 @@ let NetscriptFunctions =
"getActionMaxLevel|getActionCurrentLevel|getActionAutolevel|" + "getActionMaxLevel|getActionCurrentLevel|getActionAutolevel|" +
"setActionAutolevel|setActionLevel|" + "setActionAutolevel|setActionLevel|" +
"getRank|getSkillPoints|getSkillLevel|getSkillUpgradeCost|" + "getRank|getSkillPoints|getSkillLevel|getSkillUpgradeCost|" +
"upgradeSkill|getTeamSize|" + "upgradeSkill|getTeamSize|getCity|" +
"setTeamSize|getCityEstimatedPopulation|getCityEstimatedCommunities|" + "setTeamSize|getCityEstimatedPopulation|getCityEstimatedCommunities|" +
"getCityChaos|switchCity|getStamina|joinBladeburnerFaction|getBonusTime"; "getCityChaos|switchCity|getStamina|joinBladeburnerFaction|getBonusTime";

5
package-lock.json generated

@ -608,6 +608,11 @@
"postcss-value-parser": "3.3.0" "postcss-value-parser": "3.3.0"
} }
}, },
"autosize": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/autosize/-/autosize-4.0.2.tgz",
"integrity": "sha512-jnSyH2d+qdfPGpWlcuhGiHmqBJ6g3X+8T+iRwFrHPLVcdoGJE/x6Qicm6aDHfTsbgZKxyV8UU/YB2p4cjKDRRA=="
},
"aws-sign2": { "aws-sign2": {
"version": "0.6.0", "version": "0.6.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz",

@ -11,6 +11,7 @@
"ajv": "^5.1.5", "ajv": "^5.1.5",
"ajv-keywords": "^2.0.0", "ajv-keywords": "^2.0.0",
"async": "^2.6.1", "async": "^2.6.1",
"autosize": "^4.0.2",
"bluebird": "^3.5.1", "bluebird": "^3.5.1",
"brace": "^0.11.1", "brace": "^0.11.1",
"decimal.js": "7.2.3", "decimal.js": "7.2.3",

@ -511,6 +511,8 @@ let CONSTANTS = {
* Updated documentation to reflect the fact that Netscript port handles (getPortHandle()) only works in NetscriptJS (2.0), NOT Netscript 1.0<br> * Updated documentation to reflect the fact that Netscript port handles (getPortHandle()) only works in NetscriptJS (2.0), NOT Netscript 1.0<br>
* Added tryWrite() Netscript function<br> * Added tryWrite() Netscript function<br>
* When working (for a company/faction), experience is gained immediately/continuously rather than all at once when the work is finished<br> * When working (for a company/faction), experience is gained immediately/continuously rather than all at once when the work is finished<br>
* Added a setting in .fconf for enabling line-wrap in the Terminal input<br>
* Added 'wget' Terminal command<br>
* Improved the introductory tutorial` * Improved the introductory tutorial`
} }

@ -1,8 +1,9 @@
import {parse, Node} from "../utils/acorn"; import {parse, Node} from "../utils/acorn";
var FconfSettings = { var FconfSettings = {
ENABLE_BASH_HOTKEYS: false, ENABLE_BASH_HOTKEYS: false,
ENABLE_TIMESTAMPS: false, ENABLE_TIMESTAMPS: false,
WRAP_INPUT: false,
} }
var FconfComments = { var FconfComments = {
@ -15,6 +16,11 @@ var FconfComments = {
"http://bitburner.readthedocs.io/en/latest/shortcuts.html", "http://bitburner.readthedocs.io/en/latest/shortcuts.html",
ENABLE_TIMESTAMPS: "Terminal commands and log entries will be timestamped. The timestamp\n" + ENABLE_TIMESTAMPS: "Terminal commands and log entries will be timestamped. The timestamp\n" +
"will have the format: M/D h:m", "will have the format: M/D h:m",
WRAP_INPUT: "Wrap Terminal Input. If this is enabled, then when a Terminal command is\n" +
"too long and overflows, then it will wrap to the next line instead of\n" +
"side-scrolling\n\n" +
"Note that after you enable/disable this, you'll have to run a command\n" +
"before its effect takes place.",
} }
//Parse Fconf settings from the config text //Parse Fconf settings from the config text
@ -74,6 +80,7 @@ function parseFconfSetting(setting, value) {
switch(setting) { switch(setting) {
case "ENABLE_BASH_HOTKEYS": case "ENABLE_BASH_HOTKEYS":
case "ENABLE_TIMESTAMPS": case "ENABLE_TIMESTAMPS":
case "WRAP_INPUT":
var value = value.toLowerCase(); var value = value.toLowerCase();
if (value === "1" || value === "true" || value === "y") { if (value === "1" || value === "true" || value === "y") {
value = true; value = true;

@ -32,7 +32,8 @@ export const TerminalHelpText: string =
"tail [script] [args...] Displays dynamic logs for the specified script<br>" + "tail [script] [args...] Displays dynamic logs for the specified script<br>" +
"theme [preset] | bg txt hlgt Change the color scheme of the UI<br>" + "theme [preset] | bg txt hlgt Change the color scheme of the UI<br>" +
"top Displays all running scripts and their RAM usage<br>" + "top Displays all running scripts and their RAM usage<br>" +
'unalias "[alias name]" Deletes the specified alias<br>'; 'unalias "[alias name]" Deletes the specified alias<br>' +
'wget [url] [target file] Retrieves code/text from a web server<br>';
interface IMap<T> { interface IMap<T> {
[key: string]: T; [key: string]: T;
@ -214,5 +215,11 @@ export const HelpTexts: IMap<string> = {
"Then it could be removed using:<br><br>" + "Then it could be removed using:<br><br>" +
'unalias "r"<br><br>' + 'unalias "r"<br><br>' +
"It is not necessary to differentiate between global and non-global aliases when using 'unalias'", "It is not necessary to differentiate between global and non-global aliases when using 'unalias'",
wget: "wget [url] [target file]<br>" +
"Retrieves data from a URL and downloads it to a file on the current server. The data can only " +
"be downloaded to a script (.script, .ns, .js) or a text file (.txt). If the file already exists, " +
"it will be overwritten by this command.<br><br>" +
"Note that it will not be possible to download data from many websites because they do not allow " +
"cross-origin resource sharing (CORS). Example:<br><br>" +
"wget https://raw.githubusercontent.com/danielyxie/bitburner/master/README.md game_readme.txt",
}; };

@ -451,7 +451,7 @@ function loadImportedGame(saveObj, saveString) {
var popupId = "import-game-restart-game-notice"; var popupId = "import-game-restart-game-notice";
var txt = createElement("p", { var txt = createElement("p", {
innerText:"Imported game! I would suggest saving the game and then reloading the page " + innerText:"Imported game! You need to SAVE the game and then RELOAD the page " +
"to make sure everything runs smoothly" "to make sure everything runs smoothly"
}); });
var gotitBtn = createElement("a", { var gotitBtn = createElement("a", {

@ -50,6 +50,7 @@ import {yesNoBoxCreate,
import {post, hackProgressBarPost, import {post, hackProgressBarPost,
hackProgressPost} from "./ui/postToTerminal"; hackProgressPost} from "./ui/postToTerminal";
import autosize from 'autosize';
import * as JSZip from 'jszip'; import * as JSZip from 'jszip';
import * as FileSaver from 'file-saver'; import * as FileSaver from 'file-saver';
@ -66,8 +67,8 @@ $(document).keydown(function(event) {
if (event.keyCode === KEY.ENTER) { if (event.keyCode === KEY.ENTER) {
event.preventDefault(); //Prevent newline from being entered in Script Editor event.preventDefault(); //Prevent newline from being entered in Script Editor
var command = $('input[class=terminal-input]').val(); var command = terminalInput.value;
post( post(
"<span class='prompt'>[" + "<span class='prompt'>[" +
(FconfSettings.ENABLE_TIMESTAMPS ? getTimestamp() + " " : "") + (FconfSettings.ENABLE_TIMESTAMPS ? getTimestamp() + " " : "") +
Player.getCurrentServer().hostname + Player.getCurrentServer().hostname +
@ -524,9 +525,18 @@ let Terminal = {
commandHistoryIndex: 0, commandHistoryIndex: 0,
resetTerminalInput: function() { resetTerminalInput: function() {
document.getElementById("terminal-input-td").innerHTML = if (FconfSettings.WRAP_INPUT) {
"<div id='terminal-input-header' class='prompt'>[" + Player.getCurrentServer().hostname + " ~]" + "$ </div>" + document.getElementById("terminal-input-td").innerHTML =
'<input type="text" id="terminal-input-text-box" class="terminal-input" tabindex="1"/>'; "<div id='terminal-input-header' class='prompt'>[" + Player.getCurrentServer().hostname + " ~]" + "$ </div>" +
'<textarea type="text" id="terminal-input-text-box" class="terminal-input" tabindex="1"/>';
//Auto re-size the line element as it wraps
autosize(document.getElementById("terminal-input-text-box"));
} else {
document.getElementById("terminal-input-td").innerHTML =
"<div id='terminal-input-header' class='prompt'>[" + Player.getCurrentServer().hostname + " ~]" + "$ </div>" +
'<input type="text" id="terminal-input-text-box" class="terminal-input" tabindex="1"/>';
}
var hdr = document.getElementById("terminal-input-header"); var hdr = document.getElementById("terminal-input-header");
hdr.style.display = "inline"; hdr.style.display = "inline";
}, },
@ -762,7 +772,56 @@ let Terminal = {
$('input[class=terminal-input]').prop('disabled', false); $('input[class=terminal-input]').prop('disabled', false);
}, },
executeCommand: function(command) { writeToScriptFile : function(server, fn, code) {
var ret = {success: false, overwritten: false};
if (!isScriptFilename(fn) || !(server instanceof Server)) { return ret; }
//Check if the script already exists, and overwrite it if it does
for (let i = 0; i < server.scripts.length; ++i) {
if (fn === server.scripts[i].filename) {
let script = server.scripts[i];
script.code = code;
script.updateRamUsage();
script.module = "";
ret.overwritten = true;
ret.success = true;
return ret;
}
}
//Otherwise, create a new script
var newScript = new Script();
newScript.filename = fn;
newScript.code = code;
newScript.updateRamUsage();
newScript.server = server.ip;
server.scripts.push(newScript);
ret.success = true;
return ret;
},
writeToTextFile : function(server, fn, txt) {
var ret = {success: false, overwritten: false};
if (!fn.endsWith("txt") || !(server instanceof Server)) { return ret; }
//Check if the text file already exists, and overwrite if it does
for (let i = 0; i < server.textFiles.length; ++i) {
if (server.textFiles[i].fn === fn) {
ret.overwritten = true;
server.textFiles[i].text = txt;
ret.success = true;
return ret;
}
}
//Otherwise create a new text file
var newFile = new TextFile(fn, txt);
server.textFiles.push(newFile);
ret.success = true;
return ret;
},
executeCommand : function(command) {
command = command.trim(); command = command.trim();
//Replace all extra whitespace in command with a single space //Replace all extra whitespace in command with a single space
command = command.replace(/\s\s+/g, ' '); command = command.replace(/\s\s+/g, ' ');
@ -1384,8 +1443,7 @@ let Terminal = {
} }
} }
destServer.messages.push(scriptname); destServer.messages.push(scriptname);
post(scriptname + " copied over to " + destServer.hostname); return post(scriptname + " copied over to " + destServer.hostname);
return;
} }
//Scp for txt files //Scp for txt files
@ -1401,18 +1459,15 @@ let Terminal = {
if (!found) {return post("Error: no such file exists!");} if (!found) {return post("Error: no such file exists!");}
for (var i = 0; i < destServer.textFiles.length; ++i) { let tRes = Terminal.writeToTextFile(destServer, txtFile.fn, txtFile.text);
if (destServer.textFiles[i].fn === scriptname) { if (!tRes.success) {
//Overwrite return post("Error: scp failed");
destServer.textFiles[i].text = txtFile.text;
post("WARNING: " + scriptname + " already exists on " + destServer.hostname +
"and will be overwriten");
return post(scriptname + " copied over to " + destServer.hostname);
}
} }
var newFile = new TextFile(txtFile.fn, txtFile.text); if (tRes.overwritten) {
destServer.textFiles.push(newFile); post(`WARNING: ${scriptname} already exists on ${destServer.hostname} and will be overwriten`);
return post(scriptname + " copied over to " + destServer.hostname); return post(`${scriptname} overwritten on ${destServer.hostname}`);
}
return post(`${scriptname} copied over to ${destServer.hostname}`);
} }
//Get the current script //Get the current script
@ -1428,27 +1483,16 @@ let Terminal = {
return; return;
} }
//Overwrite script if it exists let sRes = Terminal.writeToScriptFile(destServer, scriptname, sourceScript.code);
for (var i = 0; i < destServer.scripts.length; ++i) { if (!sRes.success) {
if (scriptname == destServer.scripts[i].filename) { return post(`Error: scp failed`);
post("WARNING: " + scriptname + " already exists on " + destServer.hostname + " and will be overwritten");
var oldScript = destServer.scripts[i];
oldScript.code = sourceScript.code;
oldScript.ramUsage = sourceScript.ramUsage;
oldScript.module = "";
post(scriptname + " overwriten on " + destServer.hostname);
return;
}
} }
if (sRes.overwritten) {
var newScript = new Script(); post(`WARNING: ${scriptname} already exists on ${destServer.hostname} and will be overwritten`);
newScript.filename = scriptname; return post(`${scriptname} overwritten on ${destServer.hostname}`);
newScript.code = sourceScript.code; }
newScript.ramUsage = sourceScript.ramUsage; post(`${scriptname} copied over to ${destServer.hostname}`);
newScript.destServer = ip; break;
destServer.scripts.push(newScript);
post(scriptname + " copied over to " + destServer.hostname);
break;
case "sudov": case "sudov":
if (commandArray.length != 1) { if (commandArray.length != 1) {
post("Incorrect number of arguments. Usage: sudov"); return; post("Incorrect number of arguments. Usage: sudov"); return;
@ -1571,6 +1615,38 @@ let Terminal = {
post("No such alias exists"); post("No such alias exists");
} }
} }
break;
case "wget":
if (commandArray.length !== 2) {
return post("Incorrect usage of wget command. Usage: wget [url] [target file]");
}
var args = commandArray[1].split(" ");
if (args.length !== 2) {
return post("Incorrect usage of wget command. Usage: wget [url] [target file]");
}
let url = args[0];
let target = args[1];
if (!isScriptFilename(target) && !target.endsWith(".txt")) {
return post(`wget failed: Invalid target file. Target file must be script or text file`);
}
$.get(url, function(data) {
let res;
if (isScriptFilename(target)) {
res = Terminal.writeToScriptFile(s, target, data);
} else {
res = Terminal.writeToTextFile(s, target, data);
}
if (!res.success) {
return post("wget failed");
}
if (res.overwritten) {
return post(`wget successfully retrieved content and overwrote ${target}`);
}
return post(`wget successfully retrieved content to new file ${target}`);
}, 'text').fail(function(e) {
return post("wget failed: " + JSON.stringify(e));
})
break; break;
default: default:
post("Command not found"); post("Command not found");
@ -2048,6 +2124,7 @@ let Terminal = {
} }
} }
post("ERROR: No such script"); post("ERROR: No such script");
} }
}; };

@ -1,4 +1,4 @@
import { getElementById } from "../../utils/uiHelpers/getElementById"; import { getElementById } from "../../utils/uiHelpers/getElementById";
/** /**
* Adds some output to the terminal. * Adds some output to the terminal.