<commit_before># encoding: utf-8

require 'state_machine'
require 'adhearsion-asr'

module Adhearsion
  class IVRController < Adhearsion::CallController
    class << self
      # list of prompts to be played to the caller.
      # this should have one prompt for each attempt
      # in case there are not enough prompts, the final prompt will be re-used until
      # the max_attempts are exceeded.
      def prompts
        @prompts ||= []
      end

      # maximum number of attempts to prompt the caller for input
      def max_attempts(num = nil)
        if num
          @max_attempts = num
        else
          @max_attempts || 3
        end
      end

      # timeout in seconds for each menu attempt
      def timeout(num = nil)
        if num
          @timeout = num
        else
          @timeout || nil
        end
      end

      # called when the caller successfully provides input
      def on_complete(&block)
        @completion_callback = block
      end
      attr_reader :completion_callback

      # Called when the caller errors more than the number of allowed attempts
      def on_failure(&block)
        @failure_callback = block
      end
      attr_reader :failure_callback
    end

    state_machine initial: :prompting do
      event(:match)    { transition prompting: :complete }
      event(:reprompt) { transition input_error: :prompting }
      event(:nomatch)  { transition prompting: :input_error }
      event(:noinput)  { transition prompting: :input_error }
      event(:failure)  { transition prompting: :failure, input_error: :failure }

      after_transition :prompting => :input_error do |controller|
        controller.increment_errors
        if controller.continue?
          controller.reprompt!
        else
          controller.failure!
        end
      end

      after_transition any => :prompting do |controller|
        controller.deliver_prompt
      end

      after_transition :prompting => :complete do |controller|
        controller.completion_callback
      end

      after_transition any => :failure do |controller|
        controller.failure_callback
      end
    end

    def run
      @errors = 0
      deliver_prompt
    end

    def deliver_prompt
      prompt = prompts[@errors] || prompts.last
      prompt = instance_exec(&prompt) if prompt.respond_to? :call
      logger.debug "Prompt: #{prompt.inspect}"

      if grammar
        ask_options = { grammar: grammar, mode: :voice }
      elsif grammar_url
        ask_options = { grammar_url: grammar_url, mode: :voice }
      else
        fail NotImplementedError, 'You must override #grammar or #grammar_url and provide an input grammar'
      end

      ask_options[:timeout] = timeout if timeout

      @result = ask prompt, ask_options
      logger.debug "Got result #{@result.inspect}"
      case @result.status
      when :match
        match!
      when :stop
        logger.info "Prompt was stopped forcibly. Exiting cleanly..."
      when :hangup
        logger.info "Call was hung up mid-prompt. Exiting controller flow..."
        fail Adhearsion::Call::Hangup
      when :nomatch
        nomatch!
      when :noinput
        noinput!
      else
        fail "Unrecognized result status: #{@result.status}"
      end
      @result
    end

    def grammar
      nil
    end

    def grammar_url
      nil
    end

    def prompts
      self.class.prompts
    end

    def max_attempts
      self.class.max_attempts
    end

    def timeout
      self.class.timeout
    end

    def increment_errors
      @errors += 1
    end

    def continue?
      @errors < max_attempts
    end

    def completion_callback
      instance_exec @result, &self.class.completion_callback if self.class.completion_callback
    end

    def failure_callback
      instance_exec &self.class.failure_callback if self.class.failure_callback
    end
  end
end
<commit_msg>validation callback to verify matched data.<commit_after># encoding: utf-8

require 'state_machine'
require 'adhearsion-asr'

module Adhearsion
  class IVRController < Adhearsion::CallController
    class << self
      # list of prompts to be played to the caller.
      # this should have one prompt for each attempt
      # in case there are not enough prompts, the final prompt will be re-used until
      # the max_attempts are exceeded.
      def prompts
        @prompts ||= []
      end

      # maximum number of attempts to prompt the caller for input
      def max_attempts(num = nil)
        if num
          @max_attempts = num
        else
          @max_attempts || 3
        end
      end

      # timeout in seconds for each menu attempt
      def timeout(num = nil)
        if num
          @timeout = num
        else
          @timeout || nil
        end
      end

      # called to verify matched input is valid
      def validate_input(&block)
        @validate_callback = block
      end
      attr_reader :validate_callback

      # called when the caller successfully provides input
      def on_complete(&block)
        @completion_callback = block
      end
      attr_reader :completion_callback

      # Called when the caller errors more than the number of allowed attempts
      def on_failure(&block)
        @failure_callback = block
      end
      attr_reader :failure_callback
    end

    state_machine initial: :prompting do
      event(:match)    { transition prompting: :validation }
      event(:valid)    { transition validation: :complete }
      event(:invalid)  { transition validation: :input_error }
      event(:reprompt) { transition input_error: :prompting }
      event(:nomatch)  { transition prompting: :input_error }
      event(:noinput)  { transition prompting: :input_error }
      event(:failure)  { transition prompting: :failure, input_error: :failure }

      after_transition any => :input_error do |controller|
        controller.increment_errors
        if controller.continue?
          controller.reprompt!
        else
          controller.failure!
        end
      end

      after_transition any => :validation do |controller|
        if controller.validate_callback
          controller.valid!
        else
          controller.invalid!
        end
      end

      after_transition any => :prompting do |controller|
        controller.deliver_prompt
      end

      after_transition any => :complete do |controller|
        controller.completion_callback
      end

      after_transition any => :failure do |controller|
        controller.failure_callback
      end
    end

    def run
      @errors = 0
      deliver_prompt
    end

    def deliver_prompt
      prompt = prompts[@errors] || prompts.last
      prompt = instance_exec(&prompt) if prompt.respond_to? :call
      logger.debug "Prompt: #{prompt.inspect}"

      if grammar
        ask_options = { grammar: grammar, mode: :voice }
      elsif grammar_url
        ask_options = { grammar_url: grammar_url, mode: :voice }
      else
        fail NotImplementedError, 'You must override #grammar or #grammar_url and provide an input grammar'
      end

      ask_options[:timeout] = timeout if timeout

      @result = ask prompt, ask_options
      logger.debug "Got result #{@result.inspect}"
      case @result.status
      when :match
        match!
      when :stop
        logger.info "Prompt was stopped forcibly. Exiting cleanly..."
      when :hangup
        logger.info "Call was hung up mid-prompt. Exiting controller flow..."
        fail Adhearsion::Call::Hangup
      when :nomatch
        nomatch!
      when :noinput
        noinput!
      else
        fail "Unrecognized result status: #{@result.status}"
      end
      @result
    end

    def grammar
      nil
    end

    def grammar_url
      nil
    end

    def prompts
      self.class.prompts
    end

    def max_attempts
      self.class.max_attempts
    end

    def timeout
      self.class.timeout
    end

    def increment_errors
      @errors += 1
    end

    def continue?
      @errors < max_attempts
    end

    def validate_callback
      if self.class.validate_callback
        instance_exec &self.class.validate_callback
      else
        true
      end
    end

    def completion_callback
      instance_exec @result, &self.class.completion_callback if self.class.completion_callback
    end

    def failure_callback
      instance_exec &self.class.failure_callback if self.class.failure_callback
    end
  end
end

{'fraction_non_alphanumeric': 0.06438226227175717, 'fraction_numerical': 0.0009485416172634575, 'mean_word_length': 2.187830687830688, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 12, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1867658', 'n_tokens_mistral': 2363, 'n_tokens_neox': 2213, 'n_words': 725}