Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
SlideShare a Scribd company logo
RoR puede también hablar:
Interface de voz con Rails

              Svet Ivantchev, eFaber
                 svet@efaber.net

 22 de noviembre de 2007, ConferenciaRails 2007, Madrid
¿Qué es VoIP?
Ejemplos




           ... y 1000s más
¿Es por dinero?


• Ok, las llamadas VoIP son baratas pero no
  eso es importante
• Flexibilidad y integración con aplicaciones
Demo

• Consulta de notas por web
• Consulta de notas por teléfono

        La voz es una interface más
¿Por qué ahora?

• Web stack (ej: SO, BD, RoR :-))
• VoIP (ej: Asterisk)
• Ancho de banda
• Los IVR dejan mucho que desear
Asterisk (conceptos)
                    http://asterisk.org



• Centralita Open
  Source (PBX)
• Protocolos: SIP, IAX
• Proveedores
Componentes
Clientes VoIP                                      DID


                      SIP, IAX   SIP, IAX




                                        SIP, IAX
                ZAP

  Red PSTN                                   Terminación
Asterisk (instalación)
compilamos:
  $ wget http://downloads.digium.com/pub/
                 asterisk/releases/asterisk-1.4.13.tar.gz
  $ tar zxvf asterisk-1.4.13.tar.gz
  $ cd asterisk-1.4.13
  $ ./configure
  $ make menuselect
  $ make
  $ sudo make install
  $ sudo make samples


comprobamos:
  $ sudo asterisk -vvvvvc
voces en castellano: http://www.voipnovatos.es
Asterisk (config)
en /etc/asterisk/sip.conf

   [pb15]                             [spa941]
   context=demo1                      context=demo1
   type=friend                        type=friend
   secret=sekreto42                   secret=otrapassw
   host=dynamic                       host=dynamic
   canreinvite=no                     canreinvite=no



en /etc/asterisk/extensions.conf
    [demo1]
    exten => 101,1,Dial(SIP/pb15)
    exten => 102,1,Dial(SIP/spa941)
Cliente softphone
Cliente hardware
Números públicos
DID y SIP URI
Demo


• Llamada entre dos números
• Con el número público
Ideas
• Respuesta diferente por horas
• Respuesta diferente en función del CallerID
• Integración con Jabber
• Voicemail
• IVR, colas
• 1000s de cosas mas ...
El dialplan se complica
[efmenu]
include => users5xx
include => rooms
include => efhq
exten => s,1,NoOp(Entering eF main menu for caller ${CALLERID})
exten => s,2,Answer
exten => s,3,Wait(2)
exten => s,4,Playback(beep)
exten => s,5,Background(${SALUDOEN}enter-ext-of-person)
exten => s,6,Background(beep)
exten => s,7,WaitExten(15)
exten => 101,1,Playback(room-service)
exten => 101,2,Goto(efmenu,s,4)
exten => 999,1,Goto(svetdisa,s,1)
exten => 999,2,Goto(efmenu,s,4)
exten => i,1,Playback(pbx-invalid)
exten => i,2,Goto(efmenu,s,6)
exten => t,1,Playback(vm-goodbye)
exten => t,2,Hangup()
...y se hace feo
exten => 508,1,Macro(voicemail,SIP/esti)
exten => 509,1,Macro(voicemail,SIP/kolio)
exten => 510,1,Macro(voicemail,SIP/jean)
exten => 511,1,Macro(voicemail,SIP/ruben)
exten => 512,1,Macro(voicemail,SIP/inaki)



[macro-voicemail]
exten => s,1,Dial(${ARG1},30)
exten => s,2,Goto(s-${DIALSTATUS},1)
exten => s-NOANSWER,1,Voicemail(u${MACRO_EXTEN})
exten => s-NOANSWER,n,Hangup()
exten => s-CHANUNAVAIL,1,Playback(unavailable)
exten => s-CHANUNAVAIL,n,Hangup()
exten => s-CONGESTION,1,Busy(5)
exten => s-CONGESTION,n,Hangup()
exten => s-BUSY,1,Voicemail(b${MACRO_EXTEN})
exten => s-BUSY,n,Hangup()
exten => _s-.,1,Goto(s-NOANSWER,1)
AGI y AMI


• Asterisk Gateway Interface
• Asterisk Manager Interface
Alternativas


  • Adhearsion
  • Telegraph
Adhearsion
   http://adhearsion.com


• El dialplan escrito en Ruby
• ActiveRecord integrado
• Integración con Rails
• versiones (0.7.7 vs 0.8)
Adhearsion 1: Hello World

  $ sudo gem install adhearsion
  $ ahn create hello1
  Creating /Users/svet/hello1/config/adhearsion.sqlite3
  Creating /Users/svet/hello1/config/adhearsion.yml
  Creating /Users/svet/hello1/config/database.rb
  Creating /Users/svet/hello1/config/database.yml
  Creating /Users/svet/hello1/config/helpers/drb_server.yml
  Creating /Users/svet/hello1/config/helpers/factorial.alien.c.yml
  Creating /Users/svet/hello1/config/helpers/growler.yml
  Creating /Users/svet/hello1/config/helpers/lookup.yml
  Creating /Users/svet/hello1/config/helpers/manager_proxy.yml
  Creating /Users/svet/hello1/config/helpers/micromenus/collab.rb
  ...
Hello World
en /etc/asterisk/extensions.conf

  [adhearsion]
  exten => 101,1,AGI(agi://127.0.0.1)


$ cd hello1


en extensions.rb

  dhearsion {
      play 'hello-word'
  }


$ ahn start .
Interface de Voz con Rails
internal {
    case extension
      when 101...200
        employee = User.find_by_extension extension
          if employee.busy? then voicemail extension
          else
            dial employee, :for => 10.rings
            voicemail unless last_call_successful?
          end
      when 888
        play weather_report(quot;Dallas Texasquot;)
      when 999 then +joker_voicemail
    end
}
Adhearsion II: Amazon
require 'amazon/search'

r = Amazon::Search::Request.new( '10N1KX1NVQZ2VXXXXXXX')

begin
  resp = r.keyword_search( '0596529260', 'books' )
  resp.products.each do |p|
    unless p.availability =~ /not available/
       printf( quot;%s (ASIN %s) -- %snquot;,
               p.product_name, p.asin, p.our_price )
    end
  end
rescue
  puts quot;No se encuentra.quot;
end



$ ruby price2
RESTful Web Services (ASIN 0596529260) -- $26.39
Adhearsion II: Amazon
adhearsion {
  require 'amazon/search'
  r = Amazon::Search::Request.new( '10N1KX1NVQZ2VXXXXXXX')
  begin
    resp = r.keyword_search( extension, 'books' )
    resp.products.each do |p|
      unless p.availability =~ /not available/
          play p.our_price
      end
    end
  rescue
    play quot;no-info-about-numberquot;
  end
}
Interface de Voz con Rails
Interface de Voz con Rails
voxbone {
  sleep 2.seconds
  play quot;hello-worldquot;
  isbn = input()
  require 'amazon/search'
  r = Amazon::Search::Request.new( '10N1KX1NVQZ2VXXXXXXX')
  begin
    resp = r.keyword_search( isbn, 'books' )
    resp.products.each do |p|
      unless p.availability =~ /not available/
         play p.our_price
      end
    end
  rescue
    play quot;no-info-about-numberquot;
  end
}
Telegraph
http://telegraph.rubyforge.org




       • MVC
       • respond_to
Telegraph y AGI
exten => 105, 1, AGI(agi://127.0.0.1/my_route?param1=value)



wants.voice do
 render_voice do |voice|
    voice.play “hello-world”
    voice.link_to_dtmf 'bank-lineitem-menu' do
      link 1, :action=>quot;newquot;
      link 2, :action=>quot;listquot;
      link 3, :action=>quot;indexquot;
    end
 end
end
Demo
Interface de Voz con Rails
Interface de Voz con Rails
<h1>Consultas</h1>
 <ul>
      <li> <%= link_to quot;Faltasquot;, :action=>quot;faltasquot; %> </li>
      <li> <%= link_to quot;Notasquot;, :action=>quot;notasquot; %> </li>
 </ul>

                                <h1>Consultar notas</h1>
                                <% form_tag '/alumnos/show_notas' do %>
                                  DNI: <%= text_field_tag 'dni' %>
                                  <%= submit_tag 'Ver notas' %>
                                <% end %>


def show_notas
      @alumno = Alumno.find_by_dni(params[:dni])
      @notas = @alumno.notas_ultima_evaluacion
end



                  <% @notas.each do |nota| %>
                    <tr>
                      <td class=quot;listquot;><%= nota.asignatura %></td>
                      <td class=quot;listquot;><%= nota.nota %></td>
                    </tr>
                    <% end %>
Telegraph
$ script/plugin install svn://rubyforge.org/var/svn/telegraph/trunk




en /etc/asterisk/extensions.conf:
exten => 777,1, AGI(agi://192.168.1.1:4574/alumnos/index)




en app/controllers/alumnos_controller.rb:
def index
      respond_to do |wants|
        wants.html { render }
        wants.voice {
          render_voice { |voice| voice.play_sound quot;hello-worldquot; }
        }
      end
end
Telegraph: configuración
$ vi config/telegraph.yaml


  development:
    agi_server: 127.0.0.1
    agi_port: 4574
    ami_server: druby://localhost:9000
    outgoing_call_path: /var/spool/asterisk/outgoing
    wakeup_call_path: /var/spool/asterisk/wakeups
    sound_path: /var/lib/asterisk/sounds
    recording_path: /var/lib/asterisk/sounds



$ script/agi_server
respond_to

  def index
    respond_to do |wants|
      wants.html { render }
      wants.voice { render_voice }
    end
  end


$ vi app/views/alumnos/index.voice


voice.play_sound quot;hello-worldquot;
index
# index.rhtml
<h1>Consultas</h1>
<ul>
  <li> <%= link_to quot;Faltasquot;, :action=>quot;faltasquot; %> </li>
  <li> <%= link_to quot;Notasquot;,   :action=>quot;notasquot; %> </li>
</ul>




                     # index.voice
                     voice.play_sound 'thank-you-for-calling'
                     voice.link_to_dtmf 'presione1-2' do
                       link 1, :controller => quot;alumnosquot;, :action => quot;faltasquot;
                       link 2, :action => quot;notasquot;
                     end
formulario
# notas.rhtml
<h1>Consultar notas</h1>
<% form_tag '/alumnos/show_notas' do %>
  DNI: <%= text_field_tag 'dni' %>
  <%= submit_tag 'Ver notas' %>
<% end %>




                           # notas.voice
                           voice.form :url=>{:controller =>
                           'alumnos', :action=>'show_notas'} do |form|
                            form.numeric_input 'dni', 'dni', :max_digits=>8
                           end
resultados
# show_notas rhtml
<h3>Notas de <%= @alumno.nombre %></h3>
<table class=quot;listquot;>
  <% @notas.each do |nota| %>
  <tr>
    <td class=quot;listquot;><%= nota.asignatura %></td>
    <td class=quot;listquot;><%= nota.nota %></td>
  </tr>
  <% end %>
</table>


                                      # show.voice
                                      @notas.each do |nota|
                                          voice.play nota.asignatura.to_slug
                                          voice.play nota.nota
                                      end
                                      sleep 2.seconds
                                      voice.play quot;vm-goodbyequot;
Interface de Voz con Rails
No hay que pasarse


  • Web vs Mobile web vs Voz
  • ej: 466453.com
Telegraph con AMI
$ script/generate ami_model AMIModel



# environment.rb (user y password en manager.conf):
Telegraph::AMIManager.establish_connection!(
    :host=> 'your.host.name.com',
    :username=>'user',
    :secret=>'password')
$ script/ami_server


# usar así:
AMIModel.create(:call, :channel=>'SIP/101', :context=>'default',
                :exten => 'outgoing', :priority=>1)
Links
•   http://asterisk.org/

•   http://www.voipnovatos.es

•   http://www.voip-info.org

•   http://adhearsion.com

•   http://www.linuxjournal.com/article/9519

•   http://telegraph.rubyforge.org

•   http://telegraph.rubyforge.org/wiki/wiki.pl?UsingAGI
Q &A

More Related Content

Interface de Voz con Rails

  • 1. RoR puede también hablar: Interface de voz con Rails Svet Ivantchev, eFaber svet@efaber.net 22 de noviembre de 2007, ConferenciaRails 2007, Madrid
  • 3. Ejemplos ... y 1000s más
  • 4. ¿Es por dinero? • Ok, las llamadas VoIP son baratas pero no eso es importante • Flexibilidad y integración con aplicaciones
  • 5. Demo • Consulta de notas por web • Consulta de notas por teléfono La voz es una interface más
  • 6. ¿Por qué ahora? • Web stack (ej: SO, BD, RoR :-)) • VoIP (ej: Asterisk) • Ancho de banda • Los IVR dejan mucho que desear
  • 7. Asterisk (conceptos) http://asterisk.org • Centralita Open Source (PBX) • Protocolos: SIP, IAX • Proveedores
  • 8. Componentes Clientes VoIP DID SIP, IAX SIP, IAX SIP, IAX ZAP Red PSTN Terminación
  • 9. Asterisk (instalación) compilamos: $ wget http://downloads.digium.com/pub/ asterisk/releases/asterisk-1.4.13.tar.gz $ tar zxvf asterisk-1.4.13.tar.gz $ cd asterisk-1.4.13 $ ./configure $ make menuselect $ make $ sudo make install $ sudo make samples comprobamos: $ sudo asterisk -vvvvvc voces en castellano: http://www.voipnovatos.es
  • 10. Asterisk (config) en /etc/asterisk/sip.conf [pb15] [spa941] context=demo1 context=demo1 type=friend type=friend secret=sekreto42 secret=otrapassw host=dynamic host=dynamic canreinvite=no canreinvite=no en /etc/asterisk/extensions.conf [demo1] exten => 101,1,Dial(SIP/pb15) exten => 102,1,Dial(SIP/spa941)
  • 14. DID y SIP URI
  • 15. Demo • Llamada entre dos números • Con el número público
  • 16. Ideas • Respuesta diferente por horas • Respuesta diferente en función del CallerID • Integración con Jabber • Voicemail • IVR, colas • 1000s de cosas mas ...
  • 17. El dialplan se complica [efmenu] include => users5xx include => rooms include => efhq exten => s,1,NoOp(Entering eF main menu for caller ${CALLERID}) exten => s,2,Answer exten => s,3,Wait(2) exten => s,4,Playback(beep) exten => s,5,Background(${SALUDOEN}enter-ext-of-person) exten => s,6,Background(beep) exten => s,7,WaitExten(15) exten => 101,1,Playback(room-service) exten => 101,2,Goto(efmenu,s,4) exten => 999,1,Goto(svetdisa,s,1) exten => 999,2,Goto(efmenu,s,4) exten => i,1,Playback(pbx-invalid) exten => i,2,Goto(efmenu,s,6) exten => t,1,Playback(vm-goodbye) exten => t,2,Hangup()
  • 18. ...y se hace feo exten => 508,1,Macro(voicemail,SIP/esti) exten => 509,1,Macro(voicemail,SIP/kolio) exten => 510,1,Macro(voicemail,SIP/jean) exten => 511,1,Macro(voicemail,SIP/ruben) exten => 512,1,Macro(voicemail,SIP/inaki) [macro-voicemail] exten => s,1,Dial(${ARG1},30) exten => s,2,Goto(s-${DIALSTATUS},1) exten => s-NOANSWER,1,Voicemail(u${MACRO_EXTEN}) exten => s-NOANSWER,n,Hangup() exten => s-CHANUNAVAIL,1,Playback(unavailable) exten => s-CHANUNAVAIL,n,Hangup() exten => s-CONGESTION,1,Busy(5) exten => s-CONGESTION,n,Hangup() exten => s-BUSY,1,Voicemail(b${MACRO_EXTEN}) exten => s-BUSY,n,Hangup() exten => _s-.,1,Goto(s-NOANSWER,1)
  • 19. AGI y AMI • Asterisk Gateway Interface • Asterisk Manager Interface
  • 20. Alternativas • Adhearsion • Telegraph
  • 21. Adhearsion http://adhearsion.com • El dialplan escrito en Ruby • ActiveRecord integrado • Integración con Rails • versiones (0.7.7 vs 0.8)
  • 22. Adhearsion 1: Hello World $ sudo gem install adhearsion $ ahn create hello1 Creating /Users/svet/hello1/config/adhearsion.sqlite3 Creating /Users/svet/hello1/config/adhearsion.yml Creating /Users/svet/hello1/config/database.rb Creating /Users/svet/hello1/config/database.yml Creating /Users/svet/hello1/config/helpers/drb_server.yml Creating /Users/svet/hello1/config/helpers/factorial.alien.c.yml Creating /Users/svet/hello1/config/helpers/growler.yml Creating /Users/svet/hello1/config/helpers/lookup.yml Creating /Users/svet/hello1/config/helpers/manager_proxy.yml Creating /Users/svet/hello1/config/helpers/micromenus/collab.rb ...
  • 23. Hello World en /etc/asterisk/extensions.conf [adhearsion] exten => 101,1,AGI(agi://127.0.0.1) $ cd hello1 en extensions.rb dhearsion { play 'hello-word' } $ ahn start .
  • 25. internal { case extension when 101...200 employee = User.find_by_extension extension if employee.busy? then voicemail extension else dial employee, :for => 10.rings voicemail unless last_call_successful? end when 888 play weather_report(quot;Dallas Texasquot;) when 999 then +joker_voicemail end }
  • 27. require 'amazon/search' r = Amazon::Search::Request.new( '10N1KX1NVQZ2VXXXXXXX') begin resp = r.keyword_search( '0596529260', 'books' ) resp.products.each do |p| unless p.availability =~ /not available/ printf( quot;%s (ASIN %s) -- %snquot;, p.product_name, p.asin, p.our_price ) end end rescue puts quot;No se encuentra.quot; end $ ruby price2 RESTful Web Services (ASIN 0596529260) -- $26.39
  • 28. Adhearsion II: Amazon adhearsion { require 'amazon/search' r = Amazon::Search::Request.new( '10N1KX1NVQZ2VXXXXXXX') begin resp = r.keyword_search( extension, 'books' ) resp.products.each do |p| unless p.availability =~ /not available/ play p.our_price end end rescue play quot;no-info-about-numberquot; end }
  • 31. voxbone { sleep 2.seconds play quot;hello-worldquot; isbn = input() require 'amazon/search' r = Amazon::Search::Request.new( '10N1KX1NVQZ2VXXXXXXX') begin resp = r.keyword_search( isbn, 'books' ) resp.products.each do |p| unless p.availability =~ /not available/ play p.our_price end end rescue play quot;no-info-about-numberquot; end }
  • 33. Telegraph y AGI exten => 105, 1, AGI(agi://127.0.0.1/my_route?param1=value) wants.voice do render_voice do |voice| voice.play “hello-world” voice.link_to_dtmf 'bank-lineitem-menu' do link 1, :action=>quot;newquot; link 2, :action=>quot;listquot; link 3, :action=>quot;indexquot; end end end
  • 34. Demo
  • 37. <h1>Consultas</h1> <ul> <li> <%= link_to quot;Faltasquot;, :action=>quot;faltasquot; %> </li> <li> <%= link_to quot;Notasquot;, :action=>quot;notasquot; %> </li> </ul> <h1>Consultar notas</h1> <% form_tag '/alumnos/show_notas' do %> DNI: <%= text_field_tag 'dni' %> <%= submit_tag 'Ver notas' %> <% end %> def show_notas @alumno = Alumno.find_by_dni(params[:dni]) @notas = @alumno.notas_ultima_evaluacion end <% @notas.each do |nota| %> <tr> <td class=quot;listquot;><%= nota.asignatura %></td> <td class=quot;listquot;><%= nota.nota %></td> </tr> <% end %>
  • 38. Telegraph $ script/plugin install svn://rubyforge.org/var/svn/telegraph/trunk en /etc/asterisk/extensions.conf: exten => 777,1, AGI(agi://192.168.1.1:4574/alumnos/index) en app/controllers/alumnos_controller.rb: def index respond_to do |wants| wants.html { render } wants.voice { render_voice { |voice| voice.play_sound quot;hello-worldquot; } } end end
  • 39. Telegraph: configuración $ vi config/telegraph.yaml development: agi_server: 127.0.0.1 agi_port: 4574 ami_server: druby://localhost:9000 outgoing_call_path: /var/spool/asterisk/outgoing wakeup_call_path: /var/spool/asterisk/wakeups sound_path: /var/lib/asterisk/sounds recording_path: /var/lib/asterisk/sounds $ script/agi_server
  • 40. respond_to def index respond_to do |wants| wants.html { render } wants.voice { render_voice } end end $ vi app/views/alumnos/index.voice voice.play_sound quot;hello-worldquot;
  • 41. index # index.rhtml <h1>Consultas</h1> <ul> <li> <%= link_to quot;Faltasquot;, :action=>quot;faltasquot; %> </li> <li> <%= link_to quot;Notasquot;, :action=>quot;notasquot; %> </li> </ul> # index.voice voice.play_sound 'thank-you-for-calling' voice.link_to_dtmf 'presione1-2' do link 1, :controller => quot;alumnosquot;, :action => quot;faltasquot; link 2, :action => quot;notasquot; end
  • 42. formulario # notas.rhtml <h1>Consultar notas</h1> <% form_tag '/alumnos/show_notas' do %> DNI: <%= text_field_tag 'dni' %> <%= submit_tag 'Ver notas' %> <% end %> # notas.voice voice.form :url=>{:controller => 'alumnos', :action=>'show_notas'} do |form| form.numeric_input 'dni', 'dni', :max_digits=>8 end
  • 43. resultados # show_notas rhtml <h3>Notas de <%= @alumno.nombre %></h3> <table class=quot;listquot;> <% @notas.each do |nota| %> <tr> <td class=quot;listquot;><%= nota.asignatura %></td> <td class=quot;listquot;><%= nota.nota %></td> </tr> <% end %> </table> # show.voice @notas.each do |nota| voice.play nota.asignatura.to_slug voice.play nota.nota end sleep 2.seconds voice.play quot;vm-goodbyequot;
  • 45. No hay que pasarse • Web vs Mobile web vs Voz • ej: 466453.com
  • 46. Telegraph con AMI $ script/generate ami_model AMIModel # environment.rb (user y password en manager.conf): Telegraph::AMIManager.establish_connection!( :host=> 'your.host.name.com', :username=>'user', :secret=>'password') $ script/ami_server # usar así: AMIModel.create(:call, :channel=>'SIP/101', :context=>'default', :exten => 'outgoing', :priority=>1)
  • 47. Links • http://asterisk.org/ • http://www.voipnovatos.es • http://www.voip-info.org • http://adhearsion.com • http://www.linuxjournal.com/article/9519 • http://telegraph.rubyforge.org • http://telegraph.rubyforge.org/wiki/wiki.pl?UsingAGI
  • 48. Q &A