Advertisement
KnightMiner

[CC] voice

Feb 9th, 2016
443
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 15.54 KB | None | 0 0
  1. -- voice version 1.1
  2. -- list of args
  3. local startupArgs = { ... }
  4. if #startupArgs < 1 or #startupArgs > 2 then
  5.   print "Usage: voice <name> [mute]"
  6.   return
  7. end
  8.  
  9. -- whether we should mute turtle output
  10. local mute = "partial"
  11. if startupArgs[2] == "false" then
  12.   mute = false
  13. elseif startupArgs[2] == "true" then
  14.   mute = true
  15. end
  16.  
  17. -- loading config data and defaults
  18. local config = {}
  19. if fs.exists( "voice.cfg" ) then
  20.   local configFile = fs.open( "voice.cfg", "r" )
  21.   config = textutils.unserialise( configFile.readAll() ) or {}
  22.   configFile.close()
  23. end
  24.  
  25. -- commands to disallow
  26. local blacklist = config.blacklist or {
  27.   equipLeft = true, -- use "equip"
  28.   equipRight = true, -- don't lose the chatbox, we need that
  29.   wrap = true, -- useless
  30.   find = true, -- useless
  31. }
  32. -- and programs
  33. local progBlacklist = config.progBlacklist or {
  34.   -- will cause errors
  35.   startup = true,
  36.   voice = true,
  37.   equip = true, -- use equip command
  38.  
  39.   -- locks the controller out
  40.   edit = true,
  41.   exit = true,
  42.   lua = true,
  43.   shell = true,
  44.   shutdown = true,  
  45. }
  46. local scriptPath = config.scriptPath or "scripts/"
  47.  
  48. -- backward/forward compatibility
  49. local unpack = table.unpack or unpack
  50.  
  51. -- only take input from a predetermined player
  52. local name = startupArgs[1]
  53. local label = os.getComputerLabel() or "Turtle"
  54.  
  55. -- chat commands
  56. local chatbox = peripheral.wrap( "right" )
  57. local say, tell
  58.  
  59. -- define data based on which chatbox we are using, or if we are using a modem
  60. local mode, eventType, opened
  61. local messages = {}
  62. local pType = peripheral.getType( "right" )
  63.  
  64. if pType == "chatbox" then
  65.   mode = "moar"
  66.   eventType = "chatbox_command"
  67.   chatbox.setLabel( label )
  68.   function say( message )
  69.     chatbox.say( message )
  70.     sleep( 0.6 )
  71.   end
  72.   function tell( message )
  73.     chatbox.tell( name, message )
  74.     sleep( 0.6 )
  75.   end
  76.  
  77. elseif pType == "chatBox" then
  78.   mode = "plus"
  79.   eventType = "command"
  80.   function say( message )
  81.     chatbox.say( message, 60000000, true, label )
  82.     sleep( 0.6 )
  83.   end
  84.   function tell( message )
  85.     chatbox.tell( name, message, 60000000, true, label )
  86.     sleep( 0.6 )
  87.   end
  88.  
  89. elseif pType == "modem" then
  90.   name = tonumber( name )
  91.   mode = "modem"
  92.   eventType = "rednet_message"
  93.   if not rednet.isOpen( "right" ) then
  94.     opened = true
  95.     rednet.open( "right" )
  96.   end
  97.   function tell( message )
  98.     table.insert( messages, message )
  99.   end
  100.  
  101. else
  102.   print "Unable to find chatbox"
  103.   return
  104. end
  105.  
  106. -- list of mute overrides, or commands that return even when muted
  107. -- basically any command that just returns a result
  108. local unmuted = config.unmuted or {
  109.   --turtle
  110.   compare = true,
  111.   compareDown = true,
  112.   compareUp = true,
  113.   compareTo = true,
  114.   detect = true,
  115.   detectDown = true,
  116.   detectUp = true,
  117.   equip = true,
  118.   getFuelLevel = true,
  119.   getFuelLimit = true,
  120.   getItemCount = true,
  121.   getItemDetail = true,
  122.   getItemSpace = true,
  123.   getSelectedSlot = true,
  124.   inspect = true,
  125.   inspectDown = true,
  126.   inspectUp = true,
  127.  
  128.   -- redstone
  129.   getSides = true,
  130.   getInput = true,
  131.   getOutput = true,
  132.   getAnalogInput = true,
  133.   getAnalogOutput = true,
  134.   getBndledInput = true,
  135.   getBunledOutput = true,
  136.   testBundledOutput = true,
  137.  
  138.   -- peripheral
  139.   isPresent = true,
  140.   getType = true,
  141.   getMethods = true,
  142.   getNames = true,
  143.  
  144.   -- MoarPeripherals
  145.   getDensity = true, -- Density Scanning Turtle
  146.   getNaturalLightLevel = true, -- Outdoorsy Turtle
  147.   getFacing = true, -- Dizzy Turtle/Peripherals++ Navigational Turtle
  148.   --chatbox
  149.   getLabel = true,
  150.   getReadRange = true,
  151.   getMaxReadRange = true,
  152.   getSayRange = true,
  153.   getMaxSayRange = true,
  154.   getTellRange = true,
  155.   getMaxTellRange = true,
  156.  
  157.   -- Peripherals++
  158.   getEntity = true, --Ridable Turtle
  159.   getLiquid = true, --Thirsty Turtle
  160.   getXP = true, --XP Turtle
  161.   --Barrel Turtle
  162.   getUnlocalizedName = true,
  163.   getLocalizedName = true,
  164.   getItemID = true,
  165.   getAmount = true,
  166.   getOreDictEntries = true,
  167.   --Environment Scanner
  168.   isRaining = true,
  169.   getBiome = true,
  170.   getTemperature = true,
  171.   isSnow = true,
  172.   --Equivalence Checking Turtle
  173.   getEntries = true,
  174.   doItemsMatch = true,
  175.   --Gardening Turtle
  176.   getGrowth = true,
  177.   getGrowthUp = true,
  178.   getGrowthDown = true,
  179.   --Player Sensor
  180.   getNearbyPlayers = true,
  181.   getAllPlayers = true,
  182.   --Sign Reading Turtle
  183.   read = true,
  184.   readUp = true,
  185.   readDown = true,
  186.  
  187.   -- chat commands
  188.   script = true,
  189.   delete = true,
  190.   program = true,
  191.  
  192.   -- error reporting
  193.   Usage = true,
  194.   ERROR = true
  195. }
  196.  
  197. -- override print
  198. local consolePrint = print
  199. print = function( ... )
  200.   if not mute then
  201.     local strings = { ... }
  202.     for _, v in ipairs( strings ) do
  203.       tell( v )
  204.     end
  205.   end
  206.   return consolePrint( ... )
  207. end
  208.  
  209. -- print a bunch of messages
  210. function resultsPrint( com, results )
  211.   local oldMute = mute
  212.   if oldMute == "partial" and unmuted[com] then
  213.     mute = false
  214.   end
  215.   if type( results ) == "table" then
  216.     for i, v in ipairs( results ) do
  217.       if v ~= nil then
  218.         local comText = com .. ( #results == 1 and "" or " " .. i ) .. ": "
  219.         if type( v ) == "table" then
  220.           for k, w in pairs( v ) do
  221.             print( comText .. k .. " = '" .. tostring(w) .. "'" )
  222.           end
  223.         else
  224.           print( comText .. tostring(v) )
  225.         end
  226.       end
  227.     end
  228.   else
  229.     print( com .. ": " .. tostring(results) )
  230.   end
  231.   mute = oldMute
  232. end
  233.  
  234. -- write an error
  235. function resultsError( text )
  236.   return resultsPrint( "ERROR", text )
  237. end
  238.  
  239. -- split the input string into space separate parameters, for a few override functions
  240. function processArgs( input )
  241.   local out = {}
  242.   for match in string.gmatch( input, '%S+') do
  243.     table.insert( out, match )
  244.   end
  245.   return out
  246. end
  247.  
  248. -- process the input string into the command table
  249. -- format: { { { command, { arg, ... } }, ... }, number }
  250. function processCommand( input )
  251.   local out = {}
  252.   for match in string.gmatch( input, '%S+') do
  253.    
  254.     -- first, split out the number
  255.     -- format - command:number
  256.     local command, number
  257.     if match:find( ":" ) then
  258.       command = match:gsub( ":.*$", "" )
  259.       number = tonumber(( match:gsub( "^.*:", "" ) ))
  260.     else
  261.       command = match
  262.       number = 1
  263.     end
  264.    
  265.     -- then, split apart our commands
  266.     -- format - command+command2
  267.     local commands = {}
  268.     for match2 in command:gmatch( "([^+]+)" ) do
  269.       table.insert( commands, match2 )
  270.     end
  271.    
  272.     -- next, find the args
  273.     -- format - command(arg,arg2)
  274.     local results = {}
  275.     for _, v in ipairs( commands ) do
  276.       if v:find( "%(" ) then
  277.         local command = v:gsub( "%(.*%)$", "" )
  278.         local arg = v:gsub( "^.*%((.*)%)$", "%1" )
  279.         local argList = {}
  280.         for match in arg:gmatch( "([^,]+)" ) do
  281.           -- numbers
  282.           if tonumber( match ) then
  283.             table.insert( argList, tonumber( match ) )
  284.           -- boolean true
  285.           elseif match == "true" then
  286.             table.insert( argList, true )
  287.           -- boolean false
  288.           elseif match == "false" then
  289.             table.insert( argList, false )
  290.           -- string
  291.           else
  292.             -- character escapes for strings
  293.             match = match
  294.               :gsub( "%%%%", "%%X" )  -- %%: percent sign escape
  295.               :gsub( "([^%%])%-", "%1 " ) -- -: space
  296.               :gsub( "%%%-", "-" )    -- %-: underscore
  297.               :gsub( "%%P", "+" )     -- %P: plus sign
  298.               :gsub( "%%C", "," )     -- %C: comma
  299.               :gsub( "%%L", "(" )     -- %L: left bracket
  300.               :gsub( "%%R", ")" )     -- %R: right bracket
  301.               :gsub( "%%N", ":" )     -- %N: colon
  302.               :gsub( "%%T", "true" )  -- %T: standalone "true"
  303.               :gsub( "%%F", "false" ) -- %F: standalone "false"
  304.               :gsub( "%%X", "%%" )    -- %X: alternate percent sign escape
  305.             table.insert( argList, match )
  306.           end
  307.         end
  308.         table.insert( results, { command, argList } )
  309.       else
  310.         table.insert( results, { v, {} } )
  311.       end
  312.     end
  313.     table.insert( out, { results, number } )
  314.   end
  315.   return out
  316. end
  317.  
  318. -- peripheral commands
  319. local tool = peripheral.wrap( "left" )
  320.  
  321. -- prevent function errors from crashing the program
  322. function callFunction( com, func, ... )
  323.   local results = { pcall( func, ... ) }
  324.   if results[1] then
  325.     table.remove( results, 1 )
  326.     resultsPrint( com, results )
  327.   else
  328.     resultsError( com .. ": " .. results[2]:gsub( "^pcall: ", "" ) )
  329.   end
  330. end
  331.  
  332. -- function to run commands, used for running scripts as well
  333. local runCommand -- recursion of sorts, this function can call itself
  334. local usedScripts = {} -- to prevent a script from running itself
  335. function runCommand( command )
  336.   local commands = processCommand( command )    
  337.   for _, v in ipairs( commands ) do
  338.     local num = v[2]
  339.     if type( v[1] ) == "table" and type( num ) == "number" then
  340.       for i = 1, v[2] do
  341.         for _, comData in ipairs( v[1] ) do
  342.           local com = comData[1]
  343.           local args = comData[2]
  344.           if blacklist[com] then
  345.             resultsError( "Function '" .. com .. "' is blacklisted" )
  346.          
  347.           -- mute control
  348.           elseif com == "mute" then
  349.             if args[1] == true then
  350.               mute = true
  351.             elseif args[1] == false then
  352.               mute = false
  353.             else
  354.               mute = "partial"
  355.             end
  356.          
  357.           -- shorthand for mute(false)
  358.           elseif com == "unmute" then
  359.             mute = false
  360.          
  361.           -- turtle scripts, TODO: script writing command
  362.           elseif com == "script" then
  363.             if args[1] then
  364.               local name = scriptPath .. args[1] .. ".tz"
  365.               if fs.exists( name ) then
  366.                 if usedScripts[args[1]] then
  367.                   resultsError( "script: A script cannot run itself" )
  368.                 else
  369.                   usedScripts[args[1]] = true
  370.                   local file = fs.open( name, "r" )
  371.                   local script = file.readAll()
  372.                   file.close()
  373.              
  374.                   -- don't allow using the script command in scripts, as a script should not eteranlly loop
  375.                   resultsPrint( "script", "Running script '" .. args[1] .. "'" )
  376.                   runCommand( script, true )
  377.                   usedScripts[args[1]] = nil
  378.                 end
  379.               else
  380.                 resultsError( "script: Script does not exist" )
  381.               end
  382.             else
  383.               resultsError( "script: Must specifiy a script" )
  384.             end
  385.            
  386.           -- run a program from the turtle
  387.           elseif com == "program" then
  388.             if args[1] then
  389.               if progBlacklist[args[1]] then
  390.                 resultsError( "program: Program is blacklisted" )
  391.               else
  392.                 resultsPrint( "program", "Running program '" .. args[1] .. "'" )
  393.                 shell.run( table.concat( args, " " ) )
  394.               end
  395.             else
  396.               resultsError( "program: Must specifiy a program" )
  397.             end
  398.            
  399.           -- delay
  400.           elseif com == "sleep" then
  401.             if type( args[1] ) == "number" then
  402.               resultsPrint( com, { sleep( args[1] ) } )
  403.             else
  404.               resultsError( "sleep: Number expected, got " .. type( args[1] ) )
  405.             end
  406.            
  407.           -- swap tools and reload peripheral
  408.           elseif com == "equip" then
  409.             turtle.equipLeft()
  410.             local pType = peripheral.getType( "left" ) or "tool/empty"
  411.             resultsPrint( com, { pType } )
  412.             tool = peripheral.wrap( "left" )
  413.            
  414.           -- and lastly chatbox commands
  415.           elseif com == "say" and mode ~= "modem" then
  416.             if args[1] then
  417.               say( args[1] )
  418.             end
  419.            
  420.           -- call a peripheral, either a turtle one or a block
  421.           elseif peripheral[com] then
  422.             callFunction( com, peripheral[com], unpack( args ) )
  423.            
  424.           -- if none of the others, first try a turtle command
  425.           elseif turtle[com] then
  426.             callFunction( com, turtle[com], unpack( args ) )
  427.            
  428.           -- then a redstone command
  429.           elseif redstone[com] then
  430.             callFunction( com, redstone[com], unpack( args ) )
  431.          
  432.           -- then a peripheral command
  433.           elseif tool and tool[com] then
  434.             callFunction( com, tool[com], unpack( args ) )
  435.            
  436.           else
  437.             resultsError( "No such command '" .. com .. "'" )
  438.           end
  439.         end
  440.       end
  441.      
  442.     else
  443.       resultsError( "Broken command syntax" )
  444.     end
  445.   end
  446. end
  447.  
  448. consolePrint( "Running 'voice' by KnightMiner" )
  449. consolePrint( "Press [end] to exit" )
  450.  
  451. -- main loop
  452. while true do
  453.   local event = { os.pullEvent() }
  454.   if event[1] == "key" and event[2] == keys["end"] then
  455.     break
  456.   elseif event[1] == eventType then
  457.     -- make sure we have the right protocol for a turtle
  458.     local protocol = true
  459.     if mode == "modem" then
  460.       protocol = ( event[4] == "voice-command" )
  461.     -- dump the side data when using a MoarPeripherals chatbox, Peripherals++ does not have that
  462.     elseif mode == "moar" then
  463.       table.remove( event, 2 )
  464.     end
  465.    
  466.     -- only run on the predetermined player
  467.     if event[2] == name and protocol then
  468.       -- Peripherals++ separates commands by a space automatically, but does not trim the space near the \
  469.       -- so just rejoin and manually split it later
  470.       local command = event[3]
  471.       if mode == "plus" then
  472.         command = table.concat( command, ' ' ):gsub( '^\\ *', '' )
  473.       end
  474.      
  475.       -- split the command into space separated stuff
  476.       local args = processArgs( command )
  477.      
  478.       if args[1] == "script" then
  479.         if args[2] then
  480.           local name = scriptPath .. args[2] .. ".tz"
  481.           if args[3] then
  482.             if fs.exists( name ) then
  483.               resultsError( "Script '" .. args[2] .. "' already exists" )
  484.             else
  485.               local file = fs.open( name, "w" )
  486.               file.write( table.concat( args, "\n", 3 ) )
  487.               file.close()
  488.               resultsPrint( "script", "Saved script as '" .. args[2] .. "'" )
  489.             end
  490.           else
  491.             if fs.exists( name ) then
  492.               local file = fs.open( name, "r" )
  493.               local script = file.readAll()
  494.               file.close()
  495.               resultsPrint( "script", script:gsub( "%s+", " " ) )
  496.             else
  497.               resultsError( "Script '" .. args[2] .. "' does not exist" )
  498.             end
  499.           end
  500.         else
  501.           resultsPrint( "Usage", "script <name> <command> ..." )
  502.         end
  503.          
  504.       elseif args[1] == "delete" then
  505.         if args[2] then
  506.           local name = scriptPath .. args[2] .. ".tz"
  507.           if fs.exists( name ) then
  508.             fs.delete( name )
  509.             resultsPrint( "delete", "Deleted script '" .. args[2] .. "'" )
  510.           else
  511.             resultsError( "Script '" .. args[2] .. "' does not exist" )
  512.           end
  513.         else
  514.           resultsPrint( "Usage", "delete <name>" )
  515.         end        
  516.       else
  517.         runCommand( command )
  518.       end
  519.      
  520.       -- send the messages to the control
  521.       if mode == "modem" then
  522.         sleep( 0.1 ) -- just in case it executes instantly
  523.         rednet.send( name, messages, "voice-messages" )
  524.         messages = {}
  525.       end
  526.     end  
  527.   end
  528. end
  529.  
  530. -- shutdown tasks
  531. if opened then
  532.   rednet.close( "right" )
  533. end
  534. print = consolePrint
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement