# encoding: utf-8 require 'adhearsion/uri_list' module Adhearsion module Rayo module Component class Output < ComponentNode include HasHeaders register :output, :output class Document < RayoNode register :document, :output SSML_CONTENT_TYPE = 'application/ssml+xml' # @return [String] the URL from which the fetch the grammar attribute :url # @return [String] the document content type attribute :content_type, String, default: ->(grammar, attribute) { grammar.url ? nil : SSML_CONTENT_TYPE } # @return [String, RubySpeech::SSML::Speak, URIList] the document attribute :value def inherit(xml_node) super self.value = if ssml? RubySpeech::SSML.import xml_node.content elsif urilist? URIList.import xml_node.content else xml_node.content end self end def rayo_attributes { 'url' => url, 'content-type' => content_type } end def rayo_children(root) root.cdata xml_value super end def size if ssml? value.children.count else value.size end end def ssml? content_type == SSML_CONTENT_TYPE end private def xml_value if ssml? value.to_s elsif urilist? value.to_s elsif value end end def urilist? content_type == 'text/uri-list' end end def inherit(xml_node) document_nodes = xml_node.xpath 'ns:document', ns: self.class.registered_ns self.render_documents = document_nodes.to_a.map { |node| Document.from_xml node } super end # @return [String] the TTS voice to use attribute :voice, String # @return [Symbol] input type on which to interrupt output attribute :interrupt_on, Symbol # @return [Integer] Indicates some offset through which the output should be skipped before rendering begins. attribute :start_offset, Integer # @return [true, false] Indicates wether or not the component should be started in a paused state to be resumed at a later time. attribute :start_paused, Boolean # @return [Integer] Indicates the duration of silence that should space repeats of the rendered document. attribute :repeat_interval, Integer # @return [Integer] Indicates the number of times the output should be played. attribute :repeat_times, Integer # @return [Integer] Indicates the maximum amount of time for which the output should be allowed to run before being terminated. Includes repeats. attribute :max_time, Integer # @return [String] the rendering engine requested by the component attribute :renderer, String def rayo_attributes { 'voice' => voice, 'interrupt-on' => interrupt_on, 'start-offset' => start_offset, 'start-paused' => start_paused, 'repeat-interval' => repeat_interval, 'repeat-times' => repeat_times, 'max-time' => max_time, 'renderer' => renderer } end def rayo_children(root) render_documents.each do |render_document| render_document.to_rayo root.parent end super end # @return [Document] the document to render attribute :render_documents, Array[Document], default: [] ## # @param [Hash] other # @option other [String] :content_type the document content type # @option other [String] :value the output doucment # @option other [String] :url the url from which to fetch the document # def render_document=(other) self.render_documents = [other].compact end def ssml=(other) self.render_documents = [{:value => other}] end state_machine :state do event :paused do transition :executing => :paused end event :resumed do transition :paused => :executing end end # Pauses a running Output # # @return [Command::Output::Pause] an Rayo pause message for the current Output # # @example # output_obj.pause_action.to_xml # # returns: # def pause_action Pause.new :component_id => component_id, :target_call_id => target_call_id end ## # Sends an Rayo pause message for the current Output # def pause! raise InvalidActionError, "Cannot pause a Output that is not executing" unless executing? pause_action.tap do |action| result = write_action action paused! if result end end ## # Create an Rayo resume message for the current Output # # @return [Command::Output::Resume] an Rayo resume message # # @example # output_obj.resume_action.to_xml # # returns: # def resume_action Resume.new :component_id => component_id, :target_call_id => target_call_id end ## # Sends an Rayo resume message for the current Output # def resume! raise InvalidActionError, "Cannot resume a Output that is not paused." unless paused? resume_action.tap do |action| result = write_action action resumed! if result end end class Pause < CommandNode # :nodoc: register :pause, :output end class Resume < CommandNode # :nodoc: register :resume, :output end ## # Creates an Rayo seek message for the current Output # # @return [Command::Output::Seek] a Rayo seek message # # @example # output_obj.seek_action.to_xml # # returns: # def seek_action(options = {}) Seek.new({ :component_id => component_id, :target_call_id => target_call_id }.merge(options)).tap do |s| s.original_component = self end end ## # Sends a Rayo seek message for the current Output # def seek!(options = {}) raise InvalidActionError, "Cannot seek an Output that is already seeking." if seeking? seek_action(options).tap do |action| write_action action end end state_machine :seek_status, :initial => :not_seeking do event :seeking do transition :not_seeking => :seeking end event :stopped_seeking do transition :seeking => :not_seeking end end class Seek < CommandNode # :nodoc: register :seek, :output attribute :direction attribute :amount def request! source.seeking! super end def execute! source.stopped_seeking! super end def rayo_attributes {'direction' => direction, 'amount' => amount} end end ## # Creates an Rayo speed up message for the current Output # # @return [Command::Output::SpeedUp] a Rayo speed up message # # @example # output_obj.speed_up_action.to_xml # # returns: # def speed_up_action SpeedUp.new(:component_id => component_id, :target_call_id => target_call_id).tap do |s| s.original_component = self end end ## # Sends a Rayo speed up message for the current Output # def speed_up! raise InvalidActionError, "Cannot speed up an Output that is already speeding." unless not_speeding? speed_up_action.tap do |action| write_action action end end ## # Creates an Rayo slow down message for the current Output # # @return [Command::Output::SlowDown] a Rayo slow down message # # @example # output_obj.slow_down_action.to_xml # # returns: # def slow_down_action SlowDown.new(:component_id => component_id, :target_call_id => target_call_id).tap do |s| s.original_component = self end end ## # Sends a Rayo slow down message for the current Output # def slow_down! raise InvalidActionError, "Cannot slow down an Output that is already speeding." unless not_speeding? slow_down_action.tap do |action| write_action action end end state_machine :speed_status, :initial => :not_speeding do event :speeding_up do transition :not_speeding => :speeding_up end event :slowing_down do transition :not_speeding => :slowing_down end event :stopped_speeding do transition [:speeding_up, :slowing_down] => :not_speeding end end class SpeedUp < CommandNode # :nodoc: register :'speed-up', :output def request! source.speeding_up! super end def execute! source.stopped_speeding! super end end class SlowDown < CommandNode # :nodoc: register :'speed-down', :output def request! source.slowing_down! super end def execute! source.stopped_speeding! super end end ## # Creates an Rayo volume up message for the current Output # # @return [Command::Output::VolumeUp] a Rayo volume up message # # @example # output_obj.volume_up_action.to_xml # # returns: # def volume_up_action VolumeUp.new(:component_id => component_id, :target_call_id => target_call_id).tap do |s| s.original_component = self end end ## # Sends a Rayo volume up message for the current Output # def volume_up! raise InvalidActionError, "Cannot volume up an Output that is already voluming." unless not_voluming? volume_up_action.tap do |action| write_action action end end ## # Creates an Rayo volume down message for the current Output # # @return [Command::Output::VolumeDown] a Rayo volume down message # # @example # output_obj.volume_down_action.to_xml # # returns: # def volume_down_action VolumeDown.new(:component_id => component_id, :target_call_id => target_call_id).tap do |s| s.original_component = self end end ## # Sends a Rayo volume down message for the current Output # def volume_down! raise InvalidActionError, "Cannot volume down an Output that is already voluming." unless not_voluming? volume_down_action.tap do |action| write_action action end end state_machine :volume_status, :initial => :not_voluming do event :voluming_up do transition :not_voluming => :voluming_up end event :voluming_down do transition :not_voluming => :voluming_down end event :stopped_voluming do transition [:voluming_up, :voluming_down] => :not_voluming end end class VolumeUp < CommandNode # :nodoc: register :'volume-up', :output def request! source.voluming_up! super end def execute! source.stopped_voluming! super end end class VolumeDown < CommandNode # :nodoc: register :'volume-down', :output def request! source.voluming_down! super end def execute! source.stopped_voluming! super end end class Complete class Finish < Event::Complete::Reason register :finish, :output_complete end class MaxTime < Event::Complete::Reason register :'max-time', :output_complete end end end end end end