A Scala “Word of the day” shell script

I have a 19" monitor on the counter between my kitchen and living room, and it’s powered by a Raspberry Pi. I use the Linux Phosphor screen saver to show a scrolling “news and stock ticker” on the display, which I’ve programmed to show news from several different sources (Atom and Rss feeds, along with other news and data sources). An old version of the display looks like this:

My Raspberry Pi news ticker display

Today I added a new “Word of the day” feature to the display, and as with all of the other code, I wrote a Scala shell script to generate the output.

My Scala “Word of the day” script

Here’s the Scala shell script I wrote to get and print the word of the day from the merriam-webster.com website:

#!/bin/sh
exec scala -savecompiled -classpath "lib/AtomReaderLibrary-assembly-1.0.jar" "$0" "$@"
!#

import java.net.{URL, URLConnection}
import com.rometools.rome.feed.synd.{SyndEntry, SyndFeed}
import com.rometools.rome.io.{SyndFeedInput, XmlReader}
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import com.alvinalexander.atom.JsoupFormatter
import scala.util.{Try,Success,Failure}
import scala.collection.JavaConverters._

// read the desired feed with ROME
val syndFeed: Try[SyndFeed] = Try {
    val urlConnection = getUrlConnection("https://www.merriam-webster.com/wotd/feed/rss2")
    val xmlReader = new XmlReader(urlConnection)
    val syndFeedInput = new SyndFeedInput
    syndFeedInput.build(xmlReader)
}

syndFeed match {
    case Success(feed) => {
        // convert it to a Scala `Seq`
        val entries: Seq[SyndEntry] = asScalaBuffer(feed.getEntries).toVector

        // the first entry is today's word of the day
        val entry: SyndEntry = entries(0)

        // print the word in a formatted way
        println(getWordFormatted(getWordOfDay(entry)))

        // start getting the description (which is the meaning of the word)
        val descriptionAsHtml = entry.getDescription.getValue

        // clean up the html with Jsoup
        val jsoupDocument: Document = Jsoup.parse(descriptionAsHtml)

        // remove all anchor tags from the document
        jsoupDocument.select("a").remove()

        // use this special formatter so the output is on multiple lines.
        // the default formatter prints everything on one long line.
        val jsoupFormatter = new JsoupFormatter
        val documentAsPlainText = jsoupFormatter.getPlainText(jsoupDocument)
        val finalCleanedDescription = documentAsPlainText.replaceAll("\n\n", "\n").trim
        println(finalCleanedDescription)
    }
    case Failure(e) => {
        println("\nCould not get the word of the day. Reason follows.")
        println(s"${e.getMessage}\n")
    }
}

def getUrlConnection(url: String): URLConnection = {
    val urlConnection = new URL(url).openConnection
    urlConnection.setConnectTimeout(5000)
    urlConnection.setReadTimeout(5000)
    urlConnection
}

def getWordOfDay(entry: SyndEntry): String = entry.getTitle.toUpperCase

def getWordFormatted(s: String): String = {
    s"""
      |
      |Word of the Day
      |---------------
      |Word: $s
    """.stripMargin
}

The code needs some cleanup/refactoring, but basically what it does is call the Meeriam-Webster URL, then extracts the word of the day from its output.

The “Word of the day” output

Here’s what the output from that Scala script looks like on January 28, 2018:

Word of the Day
---------------

Word: POPINJAY

Merriam-Webster's Word of the Day for January 28, 2018 is: 
popinjay  \PAH-pin-jay\  noun

: a strutting  person

Examples:

"Who does that guy think he is?" Amanda asked in regard to the popinjay who 
strolled into the restaurant demanding to be seated instantly.
"[Ryan] Gosling plays the motormouthed popinjay, a tough talker who's actually 
quite skittish about his bloody job." — Sean P. Means,The Salt Lake Tribune, 23 
May 2016

Did you know?

Popinjays and parrots are birds of a feather. Popinjay, from the Middle French 
wordpapegai, is the original name for a parrot in English. The French word, in 
turn, came from the Arabic word for the bird,babghā’. , which English speakers 
adopted later, is probably a modification of the Middle Frenchperroquet, which 
is also the source of the English. In the days of Middle English, parrots were 
rare and exotic, and it was quite a compliment to be called apopinjay after 
such a beautiful bird. But by the 1500s, parrots had become more commonplace, 
and their gaudy plumage and vulgar mimicry helpedpopinjay develop the 
pejorative sense we use today.

Discussion

I’ve written about Scala shell scripts before, so I won’t say too much about this script. A few important notes are:

  • It uses the ROME and Jsoup libraries
  • The JsoupFormatter comes from my AtomRssReaderLibrary project (if I remember right, it’s just a copy of the Java class of the same name you can find on the internet)
  • I use the Scala JavaConverters class to convert a Java List into a Scala Vector
  • As usual, I could condense the code, but I try to write code like this in an “obvious” style that’s easy to support/update/understand in the future
  • I named my Scala shell script GetWordOfTheDay.sh, and I call it with a Linux crontab entry, and redirect its output so the Phosphor screen saver can read it
  • There are a few problems with the output where words that are italicized or included in anchor tags run together, but I haven’t looked into those issues yet

In summary, if you wanted to see how to get the “Word of the day” from an Atom or RSS feed like Merriam-Webster’s, or wanted to see an example of a Scala shell script, I hope this example is helpful.