Spell Check Script

The spell-check script (scripts/spell-check.js) contains a single handler for responding to the phrase:

hubot how do you spell <word>

The script starts aspell (a spell checking program) in a child process, sends it the <word> that we’re spell checking, and interpretes the results for the user.

There are a couple of things you’ll want to know about aspell before we talk about how your callback should function:

  1. aspell is a command line utility. It’s not a JavaScript library or anything like that.
  2. You’ll start aspell as a child process of your JavaScript program.
  • You’ll send input over stdin
  • You’ll receive output over stdout
  • We don’t really need stderr.
  1. When we start aspell, we need to pass it the -a argument.

  2. The output of aspell -a is easy to parse. We’re concerned with the following parts of the ispell documentation

    OK:        *
    Miss:      & <original> <count> <offset>: <miss>, <miss>, ..., <guess>, ...
    

    When we feed a word to aspell -a, we see output like this:

    • If the word is spelled right, it’s just a *:

      $ echo "banana" | aspell -a
      @(#) International Ispell Version 3.1.20 (but really Aspell 0.60.7-20110707)
      *
      
    • If the word is spelled wrong, we see an &, the word, some numbers, and a list of suggestions:

      $ echo "bananna" | aspell -a
      @(#) International Ispell Version 3.1.20 (but really Aspell 0.60.7-20110707)
      & bananna 5 0: banana, bandanna, bananas, banning, banana's
      

In summary, we’re starting aspell -a in a child process and sending it a single word over stdin. We then collect the output from stdout and reply to the user with the results.

How it Works

Your callback for how do you spell (.+) should do the following:

  1. Check to see if word contains more than one word (i.e., check if it contains any whitespace). If it does, the Hubot should reply to the user with:

    I can only offer suggestions for single words.
    

    and return immediately.

  2. Assuming we’re working with a single word, spawn aspell -a in a child process using spawn(). Save the return value as a variable called spellChecker.

  3. Set up an event listener for the data event of spellChecker.stdout. Your callback will be run whenever there’s new data written to stdout. The new data is passed as a parameter to your callback.

    • We want to aggregate all of the data that’s sent over stdout in a single string.:

      let output = '';
      spellChecker.stdout.on('data', (data) => {
        output += data.toString();
      });
      
    • Don’t forget to use .toString() or you’ll run into problems later.
  4. Set up an event listener for the close event of spellChecker. Your callback will be run whenever spellChecker finishes running. The return code will be passed as a parameter to your callback.

    • If the return code is not zero, an error occurred in the child process. In this case, the Hubot should reply with:

      aspell didn't work!
      

      and return immediately.

    • Assuming the return code is zero, we need to split the data received over stdout (output), so that all we have is the second line of output.

      • Remember that the first line is junk (@(#) International Ispell Version...)

      • The Hubot’s reply depends on the second line of output;

        • If the word was spelled correctly (*), the Hubot should reply with:

          <word> is spelled correctly
          
        • If the word was spelled incorrectly (&), the Hubot should reply with a list of suggestions:

          What about: a, comma, separated, list, of, suggestions
          
        • If aspell -a didn’t print a * or a & on the second line, the Hubot should reply with:

          aspell returned something weird!
          
  5. At this point, aspell -a has been started with spawn(), we have a data event listener collecting output from stdout, and we have a close event listener waiting for aspell -a to finish. Now we need to actually send the word to aspell -a, so that it can check the spelling.

    • Use spellChecker.stdin.write() to write the word to stdin of our spellChecker.
    • Call spellChecker.stdin.end() to signal that we won’t be sending any more data over spellchecker.stdin.