diff --git a/chatops/scripts/rosbot.coffee b/chatops/scripts/rosbot.coffee index 64faa2f..2dc342f 100644 --- a/chatops/scripts/rosbot.coffee +++ b/chatops/scripts/rosbot.coffee @@ -7,13 +7,127 @@ # Configuration: # See the various handlers in bash/ # +# Usage: +# build +# Detailed line 1 +# Detailed line 2 +# Detailed line 3 +# +# respond +# Detailed line 11 +# Detailed line 22 +# Detailed line 33 +# +# +# Commands: +# hubot build - Builds a .pdf file from in +# hubot convert - Builds a .xml file from in +# hubot invoice - Builds pdf invoice from quote +# hubot quickscope [branch=MASTER] - Converts quickscope into quotation +# hubot startpentest - Bootstraps a pentest +# hubot startquote - Bootstraps a quotation +# hubot validate - Validates a report/quotation +# hubot usage [command] - Displays usage information for command. If no command is specified, supported commands are displayed. +# + # Author: # Peter Mosmans # John Sinteur +# Daniel Attevelt # # This is part of the PenText framework +# -admins = ['admin'] +USAGE_LABEL = '# Usage:' + +Fs = require 'fs' +Path = require 'path' + +admins = ['peter'] + +### + This will initialise the usage information for the chatops command used in the pentext framework + It will parse the file comment sections in search of usage information. See above for an example + + ! This section should always be above the commands section and should always be terminated by two consecutive #s ! + ! Only single word commands are supported ! + + robot gets a new property .usages which can be used later on +### +init_usage = (robot) -> + robot.usages = [] + + load_scripts(robot) + +### + This will load all the scripts in the same folder as this one. It will then proceed + to parsing those scripts in order to extract the rosbot command usage information +### +load_scripts = (robot) -> + if Fs.existsSync(__dirname) + for file in Fs.readdirSync(__dirname).sort() + fullPath = Path.join __dirname, file + robot.logger.info "File: " + fullPath + + body = Fs.readFileSync fullPath, 'utf-8' + parse_scripts(robot, body) + +### + This function will parse the script for usage information. + It will do so by looping through every comment line until it hits the # Usage: label + Then it will start to look for a command tag that is identified by: + # (3 spaces) . The will let the function know it's dealing with a command identifiers. + # (5 spaces) lets the function know it's dealing with info line that should go together with the previously encountered command. + + The sequence is broken by two sequential empty comment lines. e.g.: + # + # + +### +parse_scripts = (robot, body) -> + parsing_usage = false + current_command = null + + # https://regex101.com + cmd_regex = /^#[ ]{3}[\S]+/i + info_regex = /^#[ ]{5}[\S]+/i + + # Loop through each comment line until there is none. + for line in body.split "\n" + break unless line[0] is '#' or line.substr(0, 2) is '//' + + # Determine if we've reached the Usage: tag + if line.indexOf(USAGE_LABEL) != -1 + parsing_usage = true + + if parsing_usage + # Pre-cleaning ... + cleanedLine = line.replace(/^(#|\/\/)\s?/, "").trim() + + # Are we dealing with a command? + if line.match cmd_regex + current_command = cleanedLine + robot.usages[current_command] = [] + + # Are we dealing with an info line ? + if line.match info_regex + if not robot.usages[current_command] + robot.logger.debug "Error parsing chatpos usage info: found info line, but no command line" + else + robot.usages[current_command].push cleanedLine + + # Detects two sequential #'s and quits parsing usage when it does + if cleanedLine.length == 0 + if current_command == null + parsing_usage = false + else + current_command = null + + # Sanity check + robot.logger.info "--- Following usage information parsed ----" + for command, lines of robot.usages + for index, line of lines + robot.logger.info "* #{command} : #{line}" module.exports = (robot) -> run_cmd = (cmd, args, cb ) -> @@ -22,6 +136,13 @@ module.exports = (robot) -> child.stdout.on "data", (buffer) -> cb buffer.toString() child.stderr.on "data", (buffer) -> cb buffer.toString() + init_usage(robot) + + +# *************************************************************************** +# ChatOps command handlers +# *************************************************************************** + robot.respond /build (.*)/i, id:'chatops.build', (msg) -> msg.match[0] = msg.match[0].replace(/^[a-z0-9]+$/i); msg.match.shift(); @@ -93,3 +214,30 @@ module.exports = (robot) -> args = msg.match[0].split(" "); cmd = "bash/handler_validate"; run_cmd cmd, args, (text) -> msg.send text.replace("\n",""); + + + # Handler for the usage command. + # Note that the regex option group (.*) also captures the space after the command + # This allows for the case when there is no command specified, supported commands can be displayed + robot.respond /usage(.*)/i, id:'chatops.usage', (msg) -> + msg.match[0] = msg.match[0].replace(/^[a-z0-9]+$/i); + msg.match.shift(); + args = msg.match[0].trim().split(" "); + command = args[0] + + # If not command is provided, return information on which commands can be handled + if command.trim().length == 0 + msg.send "I can provide usage information for the following commands:" + for command of robot.usages + msg.send command + msg.send "Issue the command: usage for usage information for this command" + return + + # This deals when unsupported commands + if not robot.usages[command] + msg.send "I have no usage information for: " + command + return + + # All odd case have been dealt with, let's get down to business + for index, line of robot.usages[command] + msg.send line \ No newline at end of file