justinp.io projects about contact

Dismal Tony is a conversational, virtual assistant I've been working on, that includes elements of Natural Language understanding, Web Integration, Text parsing and processing, and unique facets of the Ruby programming language. Writing code for Tony taught me a lot about Ruby, and programming in general. It's a project I'm very proud of, and one that I use myself.

Tony is designed to have mini-programs known as Directives written, which have known Criteria associated with them. This forms an internal structure of actions that can be taken when these directives are triggered. That's a very long winded way of saying that writing code for Tony allows him to understand new topics and actions based on what the words mean, and how to respond to them and reply.

module DismalTony::Directives
  class DrinkMixDirective < DismalTony::Directive
    include DismalTony::DirectiveHelpers::DataRepresentationHelpers

    set_name :drinkmix
    set_group :fun

    expect_frags :drink

    use_parsing_strategies do |use|
      use << DismalTony::ParsingStrategies::ComprehendSyntaxStrategy

    add_criteria do |qry|
      qry << must { |q| q =~ /drink/i } 
      qry << must { |q| q.verb&.any_of?(/want/i, /like/i, /mix/i, /make/i, /pick/i, /choose/i) }

    def run
      frags[:drink] = ['Glass of Water', 'Old Fashioned', 'Zombie', 'Mai Tai', 'Rum & Coke', 'Gin & Tonic', 'Pepsi Crystal'].sample
      moj = %w[martini pineapple think tropicaldrink beer cheers toast champagne].sample
      DismalTony::HandledResponse.finish("~e:#{moj} Okay, #{query.user['nickname']}. Have a #{frags[:drink]}!")

Tony takes advantage of any number of modular Parsing Strategies, including AWS Comprehend, and a Google Tensorflow Parsey McParseface Parser. This allows the Directive to define its match logic requirements using powerful sentence structure analysis methods, conveniently inside a Ruby data structure for performing complex NLU matching simply.

    add_criteria do |qry|
      qry << uniquely { |q| q.contains?(/weather/i, /\brain/i) }
      qry << must(&:location?)
      qry << could { |q| q.key_phrases.any? { |ph| ph.text.match?(/\bweather\b/i) } }

This allows really simple means of handling complex tasks by breaking them into logical, lexical structures. Assisting the Virtual Agent in learning the language allows the programmer to understand their query better, and is a dually didactic process. Built in to the system are a few MatchLogic keyword methods which allow you to easier specify when a query should match the directive. This way, a couple match criteria can yield a huge variety of acceptable phrasings.

    # Highest level of presence detection, used for key words and phrases,
    # and other imperatives.
    class Uniquely < MatchLogic
      def initialize(pre)
        # :nodoc:
        @priority = :uniquely
        @success_incr = 7

      def on_failure
        raise MatchLogicFailure

After executing the instructions, Tony will reply with a conversational output, featuring an emoji and a response string. These are defined within the Directive, and are thus context sensitive and dynamically settable within the code to use any information available to Tony.

   def run
      if /how are you/.match?(query)
        DismalTony::HandledResponse.finish("~e:thumbsup I'm doing well!")
        moj = random_emoji(
        resp = "#{synonym_for('hello').capitalize}"
        resp << ', ' << query.user[:nickname] if rand(4) <= 2
        resp << ['!', '.'].sample
        DismalTony::HandledResponse.finish("~e:#{moj} #{resp}")

You can include Tony in existing Ruby work for a quick text notification when long running processes are complete, but Tony is best utilized within a persistent web accessible application. A simple Rails application designed to receive and send Twilio messages allows the various directives to be utilized as a mobile service. Customize the conversation paths and directive executions, and Tony becomes the friendly face of info queries, backend operations, and custom scripts.