Adding command groups

Getting started

What if we wanted to prefix all commands with something? We can use a command group for that.

A CommandGroup is the same as a Command except it contains a list of commands and/or command groups. The group will only match if its matcher and any of the children inside of it also match. You should know how this works from the understanding gears guide.

Creating the command group

Let's make our command group and make it match on "!", like this:

const group = new CommandGroup()
  .match(matchPrefixes("!"))
  .setCommands(sumCommand, multiplyCommand)

And then, let's replace our bot's commands:

const bot = new Bot({ adapter, commands: [group] })

Now, run your bot again and try a command like !sum 8 8 and it should return 16

Using middleware in command groups

We can improve our code even more, by using middleware in the command group. Wouldn't it be nice if we could just return the message instead of logging it manually? Since middleware is async, we can await next() to wait for the next middleware to be executed. When you do this, you also get the response returned from the promise.

So let's define some middleware that does exactly that:

const loggingMiddleware = async (context, next) => {
  const response = await next()

  console.log(`Result: ${response}`)
  return response
  // Always return the response,
  // otherwise the response event on the bot
  // will not be usable
}

Now let's change the rest of our code to reflect this:

const { matchPrefixes } = require("@enitoni/gears")
const {
  Bot,
  Adapter,
  Command,
  CommandGroup,
} = require("@enitoni/gears-readline")

const numberMiddleware = (context, next) => {
  const { content, state } = context

  const numbers = content.split(" ").map(n => Number(n))

  if (numbers.some(n => isNaN(n))) {
    console.log("Please specify a list of numbers separated by space.")
    return
  }

  state.numbers = numbers
  return next()
}

const loggingMiddleware = async (context, next) => {
  const response = await next()

  console.log(`Result: ${response}`)
  return response
  // Always return the response,
  // otherwise the response event on the bot
  // will not be usable
}

const sumCommand = new Command()
  .match(matchPrefixes("sum "))
  .use(numberMiddleware)
  .use(context => {
    const { numbers } = context.state
    return numbers.reduce((a, b) => a + b)
  })

const multiplyCommand = new Command()
  .match(matchPrefixes("multiply "))
  .use(numberMiddleware)
  .use(context => {
    const { numbers } = context.state
    return numbers.reduce((a, b) => a * b)
  })

const group = new CommandGroup()
  .match(matchPrefixes("!"))
  .use(loggingMiddleware)
  .setCommands(sumCommand, multiplyCommand)

const adapter = new Adapter({})
const bot = new Bot({ adapter, commands: [group] })

bot.start().then(() => {
  console.log("Hello world!")
})

Run your code and type !multiply 1 9 2 9 and you should get Result: 162 back in return.

Using middleware
Introduction to services