diff --git a/sharedPlugins/inventoryImports/interfacingFunctions.js b/sharedPlugins/inventoryImports/interfacingFunctions.js index 0713735f..0e24a6f9 100644 --- a/sharedPlugins/inventoryImports/interfacingFunctions.js +++ b/sharedPlugins/inventoryImports/interfacingFunctions.js @@ -48,25 +48,14 @@ function handleInventory(json, config){ let collectStacks = function(stack){ itemsToConfirm--; if(stack){ - if(confirmedItems[stack.name]){ + if(confirmedItems[stack.name] && confirmedItems[stack.name] > 0){ confirmedItems[stack.name] += stack.count; } else { confirmedItems[stack.name] = stack.count; } } if(itemsToConfirm == 0 && Object.keys(confirmedItems).length > 0){ - // make lua table - // we gotta construct a string because I can't find any nice library for this - let itemTable = "{"; - Object.keys(confirmedItems).forEach(function(name){ - let count = confirmedItems[name]; - itemTable += '["'+name+'"]='+count+','; - }); - itemTable += "}"; - let outputFile = pluginConfig.scriptOutputFileSubscription; - // a="'+players[i]+'" - // b='+itemTable+' - console.log('/silent-command local a="'+playerNames[i]+'"local b='+itemTable+'local c={}if game.players[a]and game.players[a].connected then local d='+"'"+'{"exports":{"'+"'"+'..a..'+"'"+'":['+"'"+'local e=false;for f,g in pairs(b)do local h=game.players[a].insert{name=f,count=g}if e then d=d..'+"'"+','+"'"+'else e=true end;d=d..'+"'"+'{"name":"'+"'"+'..f..'+"'"+'","count":'+"'"+'..g-h..'+"'"+'}'+"'"+'end;d=d..'+"'"+']}}'+"'"+'game.write_file("'+outputFile+'",d)end'); + console.log(insertItemsFromObject(confirmedItems, playerNames[i])); } } // subtract items already in inventory @@ -118,6 +107,27 @@ function handleInventory(json, config){ } } else return false; // inventory is falsey or a string } +function insertItemsFromObject(confirmedItems, playerName){ + // expeced input + // {["iron-plate"]:100, coal:20} + // make lua table + // we gotta construct a string because I can't find any nice library for this + if(playerName && confirmedItems){ + if(typeof playerName != "string") { + throw "playerName is: '"+typeof playerName+"' instead of string!"; + } + let itemTable = "{"; + Object.keys(confirmedItems).forEach(function(name){ + let count = confirmedItems[name]; + itemTable += '["'+name+'"]='+count+','; + }); + itemTable += "}"; + let outputFile = pluginConfig.scriptOutputFileSubscription; + // a="'+players[i]+'" + // b='+itemTable+' + return '/silent-command local a="'+playerName+'"local b='+itemTable+'local c={}if game.players[a]and game.players[a].connected then local d='+"'"+'{"exports":{"'+"'"+'..a..'+"'"+'":['+"'"+'local e=false;for f,g in pairs(b)do local h=game.players[a].insert{name=f,count=g}if e then d=d..'+"'"+','+"'"+'else e=true end;d=d..'+"'"+'{"name":"'+"'"+'..f..'+"'"+'","count":'+"'"+'..g-h..'+"'"+'}'+"'"+'end;d=d..'+"'"+']}}'+"'"+'game.write_file("'+outputFile+'",d)end'; + } +} function pollInventories(outputFile){ // console.log("/silent-command "+'local a=game.players["Danielv123"].get_inventory(defines.inventory.player_main).get_contents()local b=game.players["Danielv123"].get_quickbar().get_contents()local c={}local d="{inventory:{"for e,f in pairs(a)do d=d.."['+"'"+'"..e.."'+"'"+']:"..f..","end;for e,f in pairs(b)do d=d.."['+"'"+'"..e.."'+"'"+']:"..f..","end;d=d.."},requestSlots:{"for e,f in pairs(c)do d=d.."['+"'"+'"..e.."'+"'"+']:"..f..","end;d=d.."}}"game.write_file("'+pluginConfig.scriptOutputFileSubscription+'",d)'); // console.log("Running inventory LUA") @@ -136,4 +146,5 @@ module.exports = { handleInventory: handleInventory, parseJsString: parseJsString, pollInventories: pollInventories, + insertItemsFromObject: insertItemsFromObject, } \ No newline at end of file diff --git a/sharedPlugins/inventoryImports/interfacingFunctions.spec.js b/sharedPlugins/inventoryImports/interfacingFunctions.spec.js index c005fe41..d6f24c2f 100644 --- a/sharedPlugins/inventoryImports/interfacingFunctions.spec.js +++ b/sharedPlugins/inventoryImports/interfacingFunctions.spec.js @@ -2,10 +2,11 @@ const assert = require("assert"); const child_process = require("child_process"); const functions = require("./interfacingFunctions"); const luamin = require("luamin"); +const isFactorioCommand = require("./isFactorioCommand") describe("inventoryImports/interfacingFunctions.js", function(){ - describe("handleInventory(inventory)", function(){ - it("does things to an object", function(){ + describe("handleInventory(inventory, config)", function(){ + /*it("does things to an object", function(){ let inventory = {'1':{inventory: {"iron-plate":63,'raw-wood': 4},requestSlots:{'raw-wood': 50,'fast-transport-belt': 50,'express-underground-belt': 50}}}; assert.throws(function(){ functions.handleInventory(inventory); @@ -14,24 +15,42 @@ describe("inventoryImports/interfacingFunctions.js", function(){ let error = ""+err // expected error is here /* istanbul ignore else */ - if(error.includes("TypeError: Cannot read property 'masterIP' of undefined")) return true + /*if(error.includes("TypeError: Cannot read property 'masterIP' of undefined")) return true }, "threw unexpected error"); - }); + });*/ it("returns false if given invalid input", function(){ assert(functions.handleInventory("this is a string") === false, "when given strings it should return false and do nothing"); }); + /*it("Sends post requests", function(){ + + var scope = nock('localhost:8080') + .post('/api/remove', { + username: 'pgte', + email: 'pedro.teixeira@gmail.com' + }) + .reply(201, { + name: "copper-plate", + count: 100 + }); + + + });*/ }); - describe("pollInventories()", function(){ + describe("pollInventories(outputfile)", function(){ it("Returns a big factorio command as string", function(){ let x = functions.pollInventories("output.txt"); assert(typeof x == "string", "pollInventories() should always return a string"); - assert(x.includes("/silent-command "), "This is not a command factorio will accept, please prepend /c"); + assert(x.includes("/silent-command ")||x.includes("/c "), "This is not a command factorio will accept, please prepend /c"); assert(x.length > 20, "LUA string seems to short, this ain't good"); }); it("Returns valid LUA", function(){ let x = functions.pollInventories("output.txt"); + + // remove string added to make factorio interpret as command x = x.replace("/silent-command ","").replace("/c ",""); + let y = false; + // luamin throws when it recieves something that is invalid LUA assert.doesNotThrow(function(){ y = luamin.minify(x); }, "Invalid LUA supplied"); @@ -39,6 +58,37 @@ describe("inventoryImports/interfacingFunctions.js", function(){ assert(y.length > 20, "Minified LUA too short, something is up"); }); }); + describe("insertItemsFromObject(object)", function(){ + let testObject = {"copper-plate":100, "iron-ore":1337} + let playerName = "Danielv123" + it("Returns undefined if any of the parameters are missing", function(){ + let x = functions.insertItemsFromObject(); + assert(x === undefined); + + let y = functions.insertItemsFromObject(testObject); + assert(y === undefined); + + let z = functions.insertItemsFromObject(undefined, playerName); + assert(z === undefined); + }); + it("Throws if player name is not a string", function(){ + assert.throws(function(){ + let x = functions.insertItemsFromObject(testObject, testObject); + }, "playerName is: 'object' instead of string!"); + }); + it("Returns a big factorio command as string", function(){ + let x = functions.insertItemsFromObject(testObject, playerName); + + assert(typeof x == "string", "LUA returned should be a string"); + assert(x.length > 20, "LUA strings are quite long"); + assert(x.includes("/silent-command ")||x.includes("/c "), "This is not a command factorio will accept, please prepend /c"); + }); + it("The returned string is a valid factorio command", function(){ + let x = functions.insertItemsFromObject(testObject, playerName); + + assert(isFactorioCommand(x) == true); + }); + }); describe("parseJsString(string) => object", function(){ it("converts a string with JS object notation to object", function(){ let x = functions.parseJsString("{hello:'thisAString!', yes:function(){return 'JS string!'}, not:'json'}"); @@ -57,5 +107,5 @@ describe("inventoryImports/interfacingFunctions.js", function(){ } }, "should throw when ; are passed because those are the simplest form of xss"); }); - }) + }); }); \ No newline at end of file diff --git a/sharedPlugins/inventoryImports/isFactorioCommand.js b/sharedPlugins/inventoryImports/isFactorioCommand.js new file mode 100644 index 00000000..239bbc59 --- /dev/null +++ b/sharedPlugins/inventoryImports/isFactorioCommand.js @@ -0,0 +1,23 @@ +const luamin = require("luamin"); +function isFactorioCommand(command){ + if(typeof command != "string"){ + return false; + } else if(!command.includes("/silent-command ") && !command.includes("/c ")){ + return false; + } else if(command[0] != "/"){ + return false; + } else { + // check that what follows /c is valid LUA + let x = command.replace("/silent-command ", "").replace("/c ",""); + let y + try{ + y = luamin.minify(x); + } catch (e) { + return false; + } + + // by this point it is surely a LUA command + return true; + } +} +module.exports = isFactorioCommand; \ No newline at end of file diff --git a/sharedPlugins/inventoryImports/isFactorioCommand.spec.js b/sharedPlugins/inventoryImports/isFactorioCommand.spec.js new file mode 100644 index 00000000..bc3ac232 --- /dev/null +++ b/sharedPlugins/inventoryImports/isFactorioCommand.spec.js @@ -0,0 +1,18 @@ +const isFactorioCommand = require("./isFactorioCommand"); +const assert = require("assert"); + +describe("inventoryImports/isFactorioCommand.js()", function(){ + it("Returns false if input is not a factorio command", function(){ + assert(isFactorioCommand({}) == false, "This is not even a string so should return false"); + assert(isFactorioCommand("Howdy boys!") == false, "This is not a command so should return false"); + assert(isFactorioCommand("/c this ain't a command because its invalid LUA") == false, "This is not valid LUA so it should return false"); + assert(isFactorioCommand("Trying to trick the system /c game.print('Hello world!')") == false, "this is still not a command"); + + }); + it("Returns true if it is a valid command", function(){ + assert(isFactorioCommand("/c game.print('hello world')"), "This is a valid command, should output true not false") + }); + it("Fails to understand that leading spaces are valid", function(){ + assert(isFactorioCommand(" /c game.print('Hello world!')") == false, "This should be true but often returns false unintentionally. If you see this you probably fixed the bug so change this test, OK?") + }); +}); \ No newline at end of file