ralyxa-lambda 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
 - data/.gitignore +3 -0
 - data/.rspec +3 -0
 - data/.rubocop.yml +16 -0
 - data/.ruby-version +1 -0
 - data/.tool-versions +1 -0
 - data/.travis.yml +12 -0
 - data/CODE_OF_CONDUCT.md +74 -0
 - data/CONTRIBUTING.md +19 -0
 - data/Gemfile +3 -0
 - data/LICENSE.txt +21 -0
 - data/README.md +294 -0
 - data/Rakefile +6 -0
 - data/bin/console +14 -0
 - data/bin/setup +8 -0
 - data/lib/ralyxa.rb +36 -0
 - data/lib/ralyxa/configuration.rb +18 -0
 - data/lib/ralyxa/errors.rb +4 -0
 - data/lib/ralyxa/handler.rb +47 -0
 - data/lib/ralyxa/register_intents.rb +45 -0
 - data/lib/ralyxa/request_entities/request.rb +63 -0
 - data/lib/ralyxa/request_entities/user.rb +25 -0
 - data/lib/ralyxa/response_builder.rb +65 -0
 - data/lib/ralyxa/response_entities/card.rb +89 -0
 - data/lib/ralyxa/response_entities/directives.rb +9 -0
 - data/lib/ralyxa/response_entities/directives/audio.rb +11 -0
 - data/lib/ralyxa/response_entities/directives/audio/audio_item.rb +23 -0
 - data/lib/ralyxa/response_entities/directives/audio/stream.rb +37 -0
 - data/lib/ralyxa/response_entities/directives/audio_player.rb +46 -0
 - data/lib/ralyxa/response_entities/directives/audio_player/clear_queue.rb +27 -0
 - data/lib/ralyxa/response_entities/directives/audio_player/play.rb +32 -0
 - data/lib/ralyxa/response_entities/directives/audio_player/stop.rb +19 -0
 - data/lib/ralyxa/response_entities/output_speech.rb +35 -0
 - data/lib/ralyxa/response_entities/reprompt.rb +20 -0
 - data/lib/ralyxa/response_entities/response.rb +54 -0
 - data/lib/ralyxa/skill.rb +53 -0
 - data/lib/ralyxa/version.rb +3 -0
 - data/pkg/ralyxa-1.0.0.gem +0 -0
 - data/ralyxa.gemspec +32 -0
 - metadata +195 -0
 
    
        data/Rakefile
    ADDED
    
    
    
        data/bin/console
    ADDED
    
    | 
         @@ -0,0 +1,14 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #!/usr/bin/env ruby
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'bundler/setup'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'ralyxa'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            # You can add fixtures and/or initialization code here to make experimenting
         
     | 
| 
      
 7 
     | 
    
         
            +
            # with your gem easier. You can also use a different console, if you like.
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            # (If you use this, don't forget to add pry to your Gemfile!)
         
     | 
| 
      
 10 
     | 
    
         
            +
            # require "pry"
         
     | 
| 
      
 11 
     | 
    
         
            +
            # Pry.start
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            require 'irb'
         
     | 
| 
      
 14 
     | 
    
         
            +
            IRB.start(__FILE__)
         
     | 
    
        data/bin/setup
    ADDED
    
    
    
        data/lib/ralyxa.rb
    ADDED
    
    | 
         @@ -0,0 +1,36 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'ralyxa/version'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'ralyxa/configuration'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'ralyxa/register_intents'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'ralyxa/skill'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            module Ralyxa
         
     | 
| 
      
 7 
     | 
    
         
            +
              class << self
         
     | 
| 
      
 8 
     | 
    
         
            +
                attr_accessor :configuration
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                def configure
         
     | 
| 
      
 11 
     | 
    
         
            +
                  yield configuration if block_given?
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                def method_missing(m, *args, &block)
         
     | 
| 
      
 15 
     | 
    
         
            +
                  if configuration.respond_to?(m)
         
     | 
| 
      
 16 
     | 
    
         
            +
                    configuration.send(m, *args, &block)
         
     | 
| 
      
 17 
     | 
    
         
            +
                  else
         
     | 
| 
      
 18 
     | 
    
         
            +
                    super
         
     | 
| 
      
 19 
     | 
    
         
            +
                  end
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                def respond_to_missing?(m, include_private = false)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  configuration.respond_to?(m) || super
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                private
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                def setup_configuration
         
     | 
| 
      
 29 
     | 
    
         
            +
                  @configuration = Ralyxa::Configuration.new
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
              end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
              setup_configuration
         
     | 
| 
      
 34 
     | 
    
         
            +
            end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
            Ralyxa::RegisterIntents.run
         
     | 
| 
         @@ -0,0 +1,18 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Ralyxa
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Configuration
         
     | 
| 
      
 3 
     | 
    
         
            +
                attr_accessor :validate_requests, :require_secure_urls
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                def initialize
         
     | 
| 
      
 6 
     | 
    
         
            +
                  @validate_requests = true
         
     | 
| 
      
 7 
     | 
    
         
            +
                  @require_secure_urls = true
         
     | 
| 
      
 8 
     | 
    
         
            +
                end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                def validate_requests?
         
     | 
| 
      
 11 
     | 
    
         
            +
                  validate_requests
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                def require_secure_urls?
         
     | 
| 
      
 15 
     | 
    
         
            +
                  require_secure_urls
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,47 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require_relative './response_builder'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require_relative './response_entities/card'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            # Handler Base Class. Each Intent Handler inherits from this, and overwrites the #handle method.
         
     | 
| 
      
 5 
     | 
    
         
            +
            module Ralyxa
         
     | 
| 
      
 6 
     | 
    
         
            +
              class Handler
         
     | 
| 
      
 7 
     | 
    
         
            +
                def initialize(request)
         
     | 
| 
      
 8 
     | 
    
         
            +
                  @request = request
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                def handle
         
     | 
| 
      
 12 
     | 
    
         
            +
                  raise NotImplementedError
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                def respond(response_text = '', response_details = {}, response_builder = Ralyxa::ResponseBuilder)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  options = response_details
         
     | 
| 
      
 17 
     | 
    
         
            +
                  options[:response_text] = response_text if response_text
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  response_builder.build(options)
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                def tell(response_text = '', response_details = {})
         
     | 
| 
      
 23 
     | 
    
         
            +
                  respond(response_text, response_details.merge(end_session: true))
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                def card(title, body, image_url = nil, card_class = Ralyxa::ResponseEntities::Card)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  card_class.as_hash(title, body, image_url)
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                def audio_player
         
     | 
| 
      
 31 
     | 
    
         
            +
                  Ralyxa::ResponseEntities::Directives::AudioPlayer
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                def link_account_card(card_class = Ralyxa::ResponseEntities::Card)
         
     | 
| 
      
 35 
     | 
    
         
            +
                  card_class.link_account
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                def log(level, message)
         
     | 
| 
      
 39 
     | 
    
         
            +
                  puts "[#{Time.new}] [#{@request.user_id}] #{level} - #{message}"
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                alias ask respond
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                attr_reader :request
         
     | 
| 
      
 45 
     | 
    
         
            +
                private :request, :respond, :tell, :ask
         
     | 
| 
      
 46 
     | 
    
         
            +
              end
         
     | 
| 
      
 47 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,45 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require_relative './skill'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Ralyxa
         
     | 
| 
      
 4 
     | 
    
         
            +
              class RegisterIntents
         
     | 
| 
      
 5 
     | 
    
         
            +
                DEFAULT_INTENTS_DIRECTORY = './intents'.freeze
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                def initialize(intents_directory, alexa_skill_class)
         
     | 
| 
      
 8 
     | 
    
         
            +
                  @intents_directory = intents_directory
         
     | 
| 
      
 9 
     | 
    
         
            +
                  @alexa_skill_class = alexa_skill_class
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                def self.run(intents_directory = DEFAULT_INTENTS_DIRECTORY, alexa_skill_class = Ralyxa::Skill)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  new(intents_directory, alexa_skill_class).run
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                def run
         
     | 
| 
      
 17 
     | 
    
         
            +
                  warn NO_INTENT_DECLARATIONS_FOUND if intent_declarations.empty?
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  intent_declarations.each do |intent_declaration|
         
     | 
| 
      
 20 
     | 
    
         
            +
                    alexa_skill_class.class_eval intent_declaration
         
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                private
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                attr_reader :intents_directory, :alexa_skill_class
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                def intent_declarations
         
     | 
| 
      
 29 
     | 
    
         
            +
                  @intent_declarations ||=
         
     | 
| 
      
 30 
     | 
    
         
            +
                    Dir.glob("#{intents_directory}/*.rb")
         
     | 
| 
      
 31 
     | 
    
         
            +
                       .map { |relative_path| File.expand_path(relative_path) }
         
     | 
| 
      
 32 
     | 
    
         
            +
                       .map { |intent_declaration_path| File.open(intent_declaration_path).read }
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                heredoc = <<~HEREDOC
         
     | 
| 
      
 36 
     | 
    
         
            +
                  \e[33m
         
     | 
| 
      
 37 
     | 
    
         
            +
                  WARNING: You haven't defined any intent declarations.
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                  Please define intent declarations inside a directory called 'intents',
         
     | 
| 
      
 40 
     | 
    
         
            +
                  on the same directory level as the runfile for your server application.
         
     | 
| 
      
 41 
     | 
    
         
            +
                  \e[0m
         
     | 
| 
      
 42 
     | 
    
         
            +
                HEREDOC
         
     | 
| 
      
 43 
     | 
    
         
            +
                NO_INTENT_DECLARATIONS_FOUND = heredoc.freeze
         
     | 
| 
      
 44 
     | 
    
         
            +
              end
         
     | 
| 
      
 45 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,63 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'json'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'forwardable'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'alexa_verifier'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require_relative './user'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            module Ralyxa
         
     | 
| 
      
 7 
     | 
    
         
            +
              module RequestEntities
         
     | 
| 
      
 8 
     | 
    
         
            +
                class Request
         
     | 
| 
      
 9 
     | 
    
         
            +
                  extend Forwardable
         
     | 
| 
      
 10 
     | 
    
         
            +
                  INTENT_REQUEST_TYPE = 'IntentRequest'.freeze
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  def_delegator :@user, :id, :user_id
         
     | 
| 
      
 13 
     | 
    
         
            +
                  def_delegator :@user, :access_token, :user_access_token
         
     | 
| 
      
 14 
     | 
    
         
            +
                  def_delegator :@user, :access_token_exists?, :user_access_token_exists?
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  attr_reader :request
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  def initialize(original_request, user_class = Ralyxa::RequestEntities::User)
         
     | 
| 
      
 19 
     | 
    
         
            +
                    validate_request(original_request) if Ralyxa.configuration.validate_requests?
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                    @request = JSON.parse(original_request.body.read)
         
     | 
| 
      
 22 
     | 
    
         
            +
                    attempt_to_rewind_request_body(original_request)
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                    @user = user_class.build(@request)
         
     | 
| 
      
 25 
     | 
    
         
            +
                  end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                  def intent_name
         
     | 
| 
      
 28 
     | 
    
         
            +
                    return @request['request']['type'] unless intent_request?
         
     | 
| 
      
 29 
     | 
    
         
            +
                    @request['request']['intent']['name']
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                  def slot_value(slot_name)
         
     | 
| 
      
 33 
     | 
    
         
            +
                    @request['request']['intent']['slots'][slot_name]['value']
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                  def new_session?
         
     | 
| 
      
 37 
     | 
    
         
            +
                    @request['session']['new']
         
     | 
| 
      
 38 
     | 
    
         
            +
                  end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                  def session_attributes
         
     | 
| 
      
 41 
     | 
    
         
            +
                    @request['session']['attributes']
         
     | 
| 
      
 42 
     | 
    
         
            +
                  end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                  def session_attribute(attribute_name)
         
     | 
| 
      
 45 
     | 
    
         
            +
                    session_attributes[attribute_name]
         
     | 
| 
      
 46 
     | 
    
         
            +
                  end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                  private
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                  def intent_request?
         
     | 
| 
      
 51 
     | 
    
         
            +
                    @request['request']['type'] == INTENT_REQUEST_TYPE
         
     | 
| 
      
 52 
     | 
    
         
            +
                  end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                  def validate_request(request)
         
     | 
| 
      
 55 
     | 
    
         
            +
                    AlexaVerifier.valid!(request)
         
     | 
| 
      
 56 
     | 
    
         
            +
                  end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                  def attempt_to_rewind_request_body(original_request)
         
     | 
| 
      
 59 
     | 
    
         
            +
                    original_request.body&.rewind
         
     | 
| 
      
 60 
     | 
    
         
            +
                  end
         
     | 
| 
      
 61 
     | 
    
         
            +
                end
         
     | 
| 
      
 62 
     | 
    
         
            +
              end
         
     | 
| 
      
 63 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,25 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Ralyxa
         
     | 
| 
      
 2 
     | 
    
         
            +
              module RequestEntities
         
     | 
| 
      
 3 
     | 
    
         
            +
                class User
         
     | 
| 
      
 4 
     | 
    
         
            +
                  attr_reader :id, :access_token
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                  def initialize(id:, access_token: nil)
         
     | 
| 
      
 7 
     | 
    
         
            +
                    @id = id
         
     | 
| 
      
 8 
     | 
    
         
            +
                    @access_token = access_token
         
     | 
| 
      
 9 
     | 
    
         
            +
                  end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  def self.build(request)
         
     | 
| 
      
 12 
     | 
    
         
            +
                    user_hash = request.dig('session', 'user') || request.dig('context', 'System', 'user') || {}
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                    new(
         
     | 
| 
      
 15 
     | 
    
         
            +
                      id: user_hash['userId'],
         
     | 
| 
      
 16 
     | 
    
         
            +
                      access_token: user_hash['accessToken']
         
     | 
| 
      
 17 
     | 
    
         
            +
                    )
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                  def access_token_exists?
         
     | 
| 
      
 21 
     | 
    
         
            +
                    !@access_token.nil?
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
              end
         
     | 
| 
      
 25 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,65 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'json'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require_relative './response_entities/response'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            module Ralyxa
         
     | 
| 
      
 5 
     | 
    
         
            +
              class ResponseBuilder
         
     | 
| 
      
 6 
     | 
    
         
            +
                def initialize(response_class, output_speech_class, reprompt_class, options)
         
     | 
| 
      
 7 
     | 
    
         
            +
                  @response_class      = response_class
         
     | 
| 
      
 8 
     | 
    
         
            +
                  @output_speech_class = output_speech_class
         
     | 
| 
      
 9 
     | 
    
         
            +
                  @reprompt_class      = reprompt_class
         
     | 
| 
      
 10 
     | 
    
         
            +
                  @options             = options
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                def self.build(options = {}, response_class = Ralyxa::ResponseEntities::Response, output_speech_class = Ralyxa::ResponseEntities::OutputSpeech, reprompt_class = Ralyxa::ResponseEntities::Reprompt)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  new(response_class, output_speech_class, reprompt_class, options).build
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                def build
         
     | 
| 
      
 18 
     | 
    
         
            +
                  merge_output_speech if response_text_exists?
         
     | 
| 
      
 19 
     | 
    
         
            +
                  merge_reprompt if reprompt_exists?
         
     | 
| 
      
 20 
     | 
    
         
            +
                  merge_card if card_exists?
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                  @response_class.as_hash(@options).to_json
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                private
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                def merge_output_speech
         
     | 
| 
      
 28 
     | 
    
         
            +
                  @options.merge!(output_speech: output_speech)
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                def merge_reprompt
         
     | 
| 
      
 32 
     | 
    
         
            +
                  @options.merge!(reprompt: reprompt)
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                def merge_card
         
     | 
| 
      
 36 
     | 
    
         
            +
                  @options[:card] = @options[:card].to_h
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                def card_exists?
         
     | 
| 
      
 40 
     | 
    
         
            +
                  @options[:card]
         
     | 
| 
      
 41 
     | 
    
         
            +
                end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                def response_text_exists?
         
     | 
| 
      
 44 
     | 
    
         
            +
                  @options[:response_text]
         
     | 
| 
      
 45 
     | 
    
         
            +
                end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                def reprompt_exists?
         
     | 
| 
      
 48 
     | 
    
         
            +
                  @options[:reprompt]
         
     | 
| 
      
 49 
     | 
    
         
            +
                end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                def output_speech
         
     | 
| 
      
 52 
     | 
    
         
            +
                  output_speech_params = { speech: @options.delete(:response_text) }
         
     | 
| 
      
 53 
     | 
    
         
            +
                  output_speech_params[:ssml] = @options.delete(:ssml) if @options[:ssml]
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                  @output_speech_class.as_hash(output_speech_params)
         
     | 
| 
      
 56 
     | 
    
         
            +
                end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                def reprompt
         
     | 
| 
      
 59 
     | 
    
         
            +
                  reprompt_params = { reprompt_speech: @options.delete(:reprompt) }
         
     | 
| 
      
 60 
     | 
    
         
            +
                  reprompt_params[:reprompt_ssml] = @options.delete(:reprompt_ssml) if @options[:reprompt_ssml]
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                  @reprompt_class.as_hash(reprompt_params)
         
     | 
| 
      
 63 
     | 
    
         
            +
                end
         
     | 
| 
      
 64 
     | 
    
         
            +
              end
         
     | 
| 
      
 65 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,89 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require_relative '../errors'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Ralyxa
         
     | 
| 
      
 4 
     | 
    
         
            +
              module ResponseEntities
         
     | 
| 
      
 5 
     | 
    
         
            +
                class Card
         
     | 
| 
      
 6 
     | 
    
         
            +
                  LINK_ACCOUNT_CARD_TYPE = 'LinkAccount'.freeze
         
     | 
| 
      
 7 
     | 
    
         
            +
                  SIMPLE_CARD_TYPE       = 'Simple'.freeze
         
     | 
| 
      
 8 
     | 
    
         
            +
                  STANDARD_CARD_TYPE     = 'Standard'.freeze
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  def initialize(options)
         
     | 
| 
      
 11 
     | 
    
         
            +
                    @options = options
         
     | 
| 
      
 12 
     | 
    
         
            +
                  end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  def self.as_hash(title, body, small_image_url = nil, large_image_url = small_image_url)
         
     | 
| 
      
 15 
     | 
    
         
            +
                    new(title: title, body: body, small_image_url: small_image_url, large_image_url: large_image_url).to_h
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  def self.link_account
         
     | 
| 
      
 19 
     | 
    
         
            +
                    new(link_account: true).to_h
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                  def to_h
         
     | 
| 
      
 23 
     | 
    
         
            +
                    {}.tap do |card|
         
     | 
| 
      
 24 
     | 
    
         
            +
                      add_type(card)
         
     | 
| 
      
 25 
     | 
    
         
            +
                      add_title(card) if @options[:title]
         
     | 
| 
      
 26 
     | 
    
         
            +
                      add_body(card)  if @options[:body]
         
     | 
| 
      
 27 
     | 
    
         
            +
                      add_image(card) if standard?
         
     | 
| 
      
 28 
     | 
    
         
            +
                    end
         
     | 
| 
      
 29 
     | 
    
         
            +
                  end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                  private
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                  def add_type(card)
         
     | 
| 
      
 34 
     | 
    
         
            +
                    return card[:type] = LINK_ACCOUNT_CARD_TYPE if link_account?
         
     | 
| 
      
 35 
     | 
    
         
            +
                    card[:type]        = SIMPLE_CARD_TYPE       if simple?
         
     | 
| 
      
 36 
     | 
    
         
            +
                    card[:type]        = STANDARD_CARD_TYPE     if standard?
         
     | 
| 
      
 37 
     | 
    
         
            +
                  end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                  def add_title(card)
         
     | 
| 
      
 40 
     | 
    
         
            +
                    card[:title] = @options[:title]
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  def add_body(card)
         
     | 
| 
      
 44 
     | 
    
         
            +
                    card[:content] = @options[:body] if simple?
         
     | 
| 
      
 45 
     | 
    
         
            +
                    card[:text]    = @options[:body] if standard?
         
     | 
| 
      
 46 
     | 
    
         
            +
                  end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                  def add_image(card)
         
     | 
| 
      
 49 
     | 
    
         
            +
                    raise Ralyxa::UnsecureUrlError, "Card images must be available at an SSL-enabled (HTTPS) endpoint. Your current image urls are: (small: #{@options[:small_image_url]}, large: #{@options[:large_image_url]}" unless secure_images?
         
     | 
| 
      
 50 
     | 
    
         
            +
                    card[:image] = {}
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                    small_image = @options[:small_image_url] || @options[:large_image_url]
         
     | 
| 
      
 53 
     | 
    
         
            +
                    large_image = @options[:large_image_url] || @options[:small_image_url]
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                    card[:image][:smallImageUrl] = small_image if small_image
         
     | 
| 
      
 56 
     | 
    
         
            +
                    card[:image][:largeImageUrl] = large_image if large_image
         
     | 
| 
      
 57 
     | 
    
         
            +
                  end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                  def link_account?
         
     | 
| 
      
 60 
     | 
    
         
            +
                    !@options[:link_account].nil?
         
     | 
| 
      
 61 
     | 
    
         
            +
                  end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                  def simple?
         
     | 
| 
      
 64 
     | 
    
         
            +
                    !@options[:small_image_url] && !@options[:large_image_url]
         
     | 
| 
      
 65 
     | 
    
         
            +
                  end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                  def standard?
         
     | 
| 
      
 68 
     | 
    
         
            +
                    @options[:small_image_url] || @options[:large_image_url]
         
     | 
| 
      
 69 
     | 
    
         
            +
                  end
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                  def secure_images?
         
     | 
| 
      
 72 
     | 
    
         
            +
                    small_secure = secure_uri?(@options[:small_image_url])
         
     | 
| 
      
 73 
     | 
    
         
            +
                    large_secure = secure_uri?(@options[:large_image_url])
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                    small_secure && large_secure
         
     | 
| 
      
 76 
     | 
    
         
            +
                  end
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                  # Given a uri string, retutrn true if:
         
     | 
| 
      
 79 
     | 
    
         
            +
                  #   * the scheme is https
         
     | 
| 
      
 80 
     | 
    
         
            +
                  #   * or if we are not interested in the uri being secure
         
     | 
| 
      
 81 
     | 
    
         
            +
                  #   * or if the value that has been passed is nil
         
     | 
| 
      
 82 
     | 
    
         
            +
                  def secure_uri?(uri_string)
         
     | 
| 
      
 83 
     | 
    
         
            +
                    return true if uri_string.nil?
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                    (URI.parse(uri_string).scheme == 'https' || !Ralyxa.require_secure_urls?)
         
     | 
| 
      
 86 
     | 
    
         
            +
                  end
         
     | 
| 
      
 87 
     | 
    
         
            +
                end
         
     | 
| 
      
 88 
     | 
    
         
            +
              end
         
     | 
| 
      
 89 
     | 
    
         
            +
            end
         
     |