[Freeswitch-svn] [commit] r8526 - in freeswitch/trunk/scripts/contrib/jpalley/telegraph: . trunk trunk/generators trunk/generators/voice_events_controller trunk/generators/voice_events_controller/templates trunk/generators/voice_model trunk/generators/voice_model/templates trunk/lib trunk/lib/core trunk/lib/core/voice_events trunk/lib/core/voice_view trunk/lib/freeswitch trunk/lib/freeswitch/voice_model trunk/test

Freeswitch SVN jpalley at freeswitch.org
Thu May 22 13:16:33 EDT 2008


Author: jpalley
Date: Thu May 22 13:16:32 2008
New Revision: 8526

Added:
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/README
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/Rakefile
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/generators/
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/generators/voice_events_controller/
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/generators/voice_events_controller/templates/
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/generators/voice_events_controller/templates/routes.rb
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/generators/voice_events_controller/templates/voice_events.rb
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/generators/voice_events_controller/voice_events_controller_generator.rb
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/generators/voice_model/
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/generators/voice_model/templates/
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/generators/voice_model/templates/model.rb
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/generators/voice_model/voice_model_generator.rb
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/init.rb
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/install.rb
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/core/
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/core/logger_overide.rb
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/core/script_base.rb
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/core/telegraph.rb
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/core/voice_events/
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/core/voice_events/router.rb
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/core/voice_events/voice_events_base.rb
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/core/voice_view/
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/core/voice_view/action_controller_extensions.rb
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/core/voice_view/dispatcher.rb
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/core/voice_view/interface.rb
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/core/voice_view/request.rb
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/core/voice_view/response.rb
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/core/voice_view/template.rb
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/freeswitch/
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/freeswitch/voice_events_server.rb
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/freeswitch/voice_model/
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/freeswitch/voice_model/voice_channel_model.rb
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/freeswitch/voice_model/voice_conference_model.rb
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/freeswitch/voice_model/voice_connector.rb
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/freeswitch/voice_model/voice_sip_model.rb
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/freeswitch/voice_view_interface.rb
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/freeswitch/voice_view_server.rb
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/telegraph.yml
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/voice_events
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/voice_view
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/license.txt
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/test/
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/test/ami_test.rb
   freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/test/rai_test.rb

Log:
initial import of telegraph rails/ruby bindings. 

Added: freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/README
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/README	Thu May 22 13:16:32 2008
@@ -0,0 +1,30 @@
+Telegraph for FreeSWITCH
+Please see http://code.google.com/p/telegraph for more information
+
+Telegraph allows you to write MVC voice application in Ruby on Rails that tightly integrate with the web.  In our second release we go FreeSWITCH (the new OSS carrier grade switch) and Rails 2.  Here's the details:
+
+For the FreeSWITCH fan:
+ - Complete implementation of Outbound Socket, Event (Inbound) Socket and XML-RPC
+ - ORM (ActiveRecord) like wrappings around the XML-RPC.  Do things like VoiceChannel.find(uuid).destroy to hangup a channel or Conference.find(conf_id).members.first.mute? to see if the first member of a conference is muted.
+ - Event socket routing to different code modules. Allows events related to one set business logic go to one codebase and another set go to another.  No more if..thens..
+ - Easy to read "voice view" syntax for outbound socket.
+ - Complete integration with Ruby on Rails and all of its database logic.
+ - Easy to deploy, log and maintain.
+
+For the Ruby/Rails fan:
+  - Voice code fits right into your usual MVC patterns.  
+		- Originate/destroy/change calls, conferences and sip settings just like you'd manipulate database tables.
+		- Write voice_views like you write html/js/xml views.  Use respond_to blocks to share application logic between web/voice interfaces.
+	- Write a callback application in only a few lines of code.
+	- Need to understand VoIP kept to a minimum.
+	
+For The Sys Admin:
+	- Easy to deploy, daemonize
+	- Based on EventMachine - the super solid and fast networking library
+	
+  
+		
+		
+
+
+

Added: freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/Rakefile
==============================================================================

Added: freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/generators/voice_events_controller/templates/routes.rb
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/generators/voice_events_controller/templates/routes.rb	Thu May 22 13:16:32 2008
@@ -0,0 +1,14 @@
+# Use this to route events to various voice events controller based on parameters sent in the event.
+# See example below
+class VoiceEventsRouter
+  def self.route(map)
+    
+#    Example of how to connect a route based on params sent my Voice Server
+#     map.connect :dialer do |params|
+#       MyModel.find_by_uuid(params[:caller_unique_id])  or params[:job_command_arg] =~ /dialers/
+#    end
+    
+    map.default :<%= file_name %>
+    
+  end
+end

Added: freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/generators/voice_events_controller/templates/voice_events.rb
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/generators/voice_events_controller/templates/voice_events.rb	Thu May 22 13:16:32 2008
@@ -0,0 +1,28 @@
+class <%= class_name %>VoiceEvents < Telegraph::Core::VoiceEventsBase
+# Common events are commented below
+
+# Background job is called as a result of VoiceChannelModel.create and will have the uuid of the call
+#  def background_job
+#  end
+  
+# Called on hangup
+#  def channel_hangup
+#  end
+  
+# Called when two channels are bridged
+#  def channel_bridge
+#  end
+  
+# When a sip user registers (watch out..every time the register is called this fires..not just the first time)
+#  def custom_sofia_register
+#  end
+  
+# When a sip user unregisters
+#  def custom_sofia_unregister
+#  end
+  
+# When a sip user is expired.
+#  def custom_sofia_expire
+#  end
+  
+end

Added: freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/generators/voice_events_controller/voice_events_controller_generator.rb
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/generators/voice_events_controller/voice_events_controller_generator.rb	Thu May 22 13:16:32 2008
@@ -0,0 +1,11 @@
+class VoiceEventsControllerGenerator < Rails::Generator::NamedBase
+  def manifest
+    puts self.class_name
+    record do |m|
+      m.directory File.join('app/voice_events')
+      routes_path = File.join('app/voice_events', 'routes.rb')
+      m.template ("routes.rb", routes_path) unless File.exists?(routes_path)
+      m.template "voice_events.rb", File.join('app/voice_events', "#{file_name}_voice_events.rb")
+    end
+  end
+end

Added: freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/generators/voice_model/templates/model.rb
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/generators/voice_model/templates/model.rb	Thu May 22 13:16:32 2008
@@ -0,0 +1,2 @@
+class <%= model_name.camelize %> < Telegraph::FreeSWITCH::Voice<%= model_type.camelize %>Model
+end
\ No newline at end of file

Added: freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/generators/voice_model/voice_model_generator.rb
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/generators/voice_model/voice_model_generator.rb	Thu May 22 13:16:32 2008
@@ -0,0 +1,15 @@
+class VoiceModelGenerator < Rails::Generator::NamedBase
+  def initialize(runtime_args, runtime_options = {})
+    super
+    @model_type = runtime_args.shift.downcase
+    @model_name = runtime_args.shift
+  end
+  def manifest
+    raise "Invalid Voice Model Name" unless %w{channel sip conference}.include?(@model_type)
+    
+    record do |m|
+      m.directory File.join('app/models')
+      m.template "model.rb", File.join('app/models', "#{@model_name.tableize.singularize}.rb"), :assigns=>{:model_type=>@model_type, :model_name=>@model_name}
+    end
+  end
+end

Added: freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/init.rb
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/init.rb	Thu May 22 13:16:32 2008
@@ -0,0 +1,12 @@
+#Core modules
+require 'core/telegraph'
+
+#Load the voice model
+require 'freeswitch/voice_model/voice_connector'
+require 'freeswitch/voice_model/voice_channel_model'
+require 'freeswitch/voice_model/voice_sip_model'
+require 'freeswitch/voice_model/voice_conference_model'
+
+#Connect to server...
+Telegraph::FreeSWITCH::VoiceConnector.load_configuration_and_connect!
+

Added: freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/install.rb
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/install.rb	Thu May 22 13:16:32 2008
@@ -0,0 +1,12 @@
+# Copy server files into script/server
+require 'ftools'
+
+dir = File.dirname(__FILE__)
+
+#Copy script files
+%w{voice_view voice_events}.each do |f|
+  File.copy "#{dir}/lib/#{f}", "#{dir}/../../../script/#{f}"
+end
+
+#Copy configuration
+File.copy "#{dir}/lib/telegraph.yml", "#{dir}/../../../config/telegraph.yml"
\ No newline at end of file

Added: freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/core/logger_overide.rb
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/core/logger_overide.rb	Thu May 22 13:16:32 2008
@@ -0,0 +1,9 @@
+module Rails
+  class Configuration
+  
+    private
+      def default_log_path
+        ENV['LOG_FILE_PATH']
+      end
+   end
+end

Added: freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/core/script_base.rb
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/core/script_base.rb	Thu May 22 13:16:32 2008
@@ -0,0 +1,181 @@
+require 'optparse'
+require 'ostruct'
+require 'pp'
+require 'rubygems'
+
+module Telegraph
+  class ScriptOptions
+    def self.parse(args, process)
+      options=OpenStruct.new
+      options.daemonize = false
+      options.pid_file = "log/#{process}_server.pid"
+      options.environment = "development"
+      options.cwd = Dir.pwd
+      options.action = 'start'
+      options.debug = false
+      options.verbose = false
+      opts = OptionParser.new do |opts|
+        opts.banner = "Usage: #{process} [options]"
+        opts.separator  ""
+        opts.separator "Specific options:"
+
+        opts.on('-d', '--daemon', 'Run as daemon', 'runs in shell by default') do |ext|
+          options.daemonize=true
+        end
+        opts.on('-p','--pid FILE', 'PID File', 'Defaults to log directory') do |ext|
+           options.pid_file = ext
+        end
+        opts.on('-e','--environment ENV', "Rails environment to run as") do |ext|
+          options.environment = ext
+        end
+        opts.on('-a', '--action [TYPE]', [:start, :stop], "Action To Do") do |action|
+          options.action = action
+        end
+        opts.on('-C PATH', 'Change dir before starting') do |path|
+          options.cwd = path
+        end
+        opts.on('-v', '--verbose', 'Log Voice Server Lines') do |verbose|
+          options.verbose = true
+        end
+        opts.on('-l', '--list', 'Answer to All Events') do |debug|
+          options.debug = true
+        end
+      end
+
+      opts.parse!(args)
+      options
+    end
+  end
+
+  class ScriptHandler
+     def initialize(process, &block)
+       @process_name = process
+       @options=ScriptOptions.parse(ARGV, process)
+       @pid_file =File.join(@options.cwd, @options.pid_file)
+       if @options.action == 'start'
+          self.start &block
+       else
+         self.stop
+        end
+     end
+
+     def start
+    
+        log "Starting Server"
+        ENV["RAILS_ENV"]=@options.environment
+
+
+       @log_file = File.expand_path(File.join(File.dirname(__FILE__), "/../../../../../log/#{@process_name}_#{@options.environment}.log"))
+        
+        log "Logging at #{@log_file}"
+
+        if @options.daemonize
+           if File.exists?(@pid_file)
+              log "PID File Already Exists!  Exiting..."
+              exit
+            else
+              daemonize
+              write_pid_file
+            end
+         end
+        setup_signals
+
+
+        require File.dirname(__FILE__) + '/../../../../../config/boot'
+        
+        #Surely a better way to do this...
+        ENV['LOG_FILE_PATH'] = @log_file
+        require File.dirname(__FILE__) + '/../../../../../vendor/plugins/telegraph/lib/core/logger_overide'
+
+        require RAILS_ROOT + "/config/environment"
+
+        require 'commands/servers/base'
+
+        ActiveRecord::Base.allow_concurrency = true #Prevents connection from dieing...
+        require 'eventmachine'
+        
+        tail_thread = tail(Pathname.new(@log_file).cleanpath) unless @options.daemonize
+      
+       
+         begin
+            yield @options.verbose, @options.debug
+         ensure
+          tail_thread.kill if tail_thread
+           log 'Exiting'
+         end
+    
+ 
+     end
+     def stop
+      send_signal("INT", @pid_file)    
+     end
+
+     def kill
+       log "Exiting..."
+       File.unlink(@pid_file) if @pid_file and File.exists?(@pid_file)
+      #Need to do this twice as voice_events catches it...
+      exit!
+     end
+
+     def setup_signals
+     # forced shutdown, even if previously restarted (actually just like TERM but for CTRL-C)
+         # clean up the pid file always
+        at_exit { 
+          log "Exiting..."
+          File.unlink(@pid_file) if @pid_file and File.exists?(@pid_file) 
+          }
+      
+           trap("INT") { log "INT signal received."; kill}
+          if RUBY_PLATFORM !~ /mswin/
+            # graceful shutdown
+            trap("TERM") { log "TERM signal received."; kill }
+            log "Signals ready.  TERM => stop. INT => stop (no restart)."
+          end
+   
+   
+     end
+     def send_signal(signal, pid_file)
+     begin 
+       pid = open(pid_file).read.to_i
+       print "Sending #{signal} to #{@options.process} at PID #{pid}..."
+       Process.kill(signal, pid)
+     rescue Errno::ESRCH
+       print "Process does not exist.  Not running."
+     rescue Errno::ENOENT
+        print "No .pid file.  Not shutting down"
+      end
+  
+      puts "Done."
+      end
+     def log(msg)
+ #      STDERR.print "** ", msg, " **\n"
+        puts "** #{msg} **"
+     end
+   
+    # Writes the PID file but only if we're on windows.
+     def write_pid_file
+       log "Write pid"
+       log @pid_file
+     
+       if RUBY_PLATFORM !~ /mswin/
+          open(@pid_file,"w") {|f| f.write(Process.pid) }
+       end
+     end
+    def daemonize(options={})
+       #ops = resolve_defaults(options)
+        # save this for later since daemonize will hose it
+        if RUBY_PLATFORM !~ /mswin/
+          require 'daemons/daemonize'
+          log 'daemonizing!'
+          Daemonize.daemonize(@log_file)
+
+          #log File.join(@options.cwd, @options.pid_file)
+          # change back to the original starting directory
+          Dir.chdir(@options.cwd)
+        else
+          log "WARNING: Win32 does not support daemon mode."
+        end
+      end
+
+  end
+end
\ No newline at end of file

Added: freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/core/telegraph.rb
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/core/telegraph.rb	Thu May 22 13:16:32 2008
@@ -0,0 +1,22 @@
+Mime::Type.register "telegraph/voice", :voice
+module Telegraph
+  def self.config
+    rails_config = File.join(RAILS_ROOT, 'config/telegraph')
+    if File.exist?(rails_config)
+      yml = YAML.load(File.open(File.join(RAILS_ROOT, 'config/telegraph.yml')))
+    else
+      yml = YAML.load(File.open(File.join(RAILS_ROOT, 'vendor/plugins/telegraph/lib/telegraph.yml')))
+    end
+    
+    config = yml[RAILS_ENV].nil? || yml[RAILS_ENV].empty? ? yml['development'] : yml[RAILS_ENV]
+    return config
+  end
+  
+  def self.logger 
+    RAILS_DEFAULT_LOGGER
+  end
+  
+  def self.log(txt)
+    puts "#{Time.now.utc.to_s(:long)}: #{txt}\n"
+  end
+end
\ No newline at end of file

Added: freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/core/voice_events/router.rb
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/core/voice_events/router.rb	Thu May 22 13:16:32 2008
@@ -0,0 +1,34 @@
+module Telegraph
+  module Core
+    class Router
+      def initialize
+        @routes = []
+      end
+      
+      def connect(name, &block)
+        @routes << {:controller=>name, :block=>block}
+      end
+      
+      def default(name)
+        @routes << {:controller=>name, :block=>nil}
+      end
+      
+      def dispatch!(event_name, params)
+        controller_name = @routes.detect{|r| 
+          r[:block].nil? || r[:block].call(params)}[:controller]
+        controller = controller_from_name(controller_name)
+        controller.params = params
+        controller.send(event_name.to_sym)
+      end
+      
+      def used_events
+        @routes.map{|r| controller_from_name(r[:controller]).methods}.flatten - Telegraph::Core::VoiceEventsBase.new.methods
+      end
+      
+      private
+      def controller_from_name(name)
+        "#{name}_voice_events".camelize.constantize.new
+      end
+    end
+  end
+end
\ No newline at end of file

Added: freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/core/voice_events/voice_events_base.rb
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/core/voice_events/voice_events_base.rb	Thu May 22 13:16:32 2008
@@ -0,0 +1,12 @@
+module Telegraph
+  module Core
+    class VoiceEventsBase
+      attr_accessor :params
+
+      def initialize
+        @params = nil
+      end
+
+    end
+  end
+end
\ No newline at end of file

Added: freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/core/voice_view/action_controller_extensions.rb
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/core/voice_view/action_controller_extensions.rb	Thu May 22 13:16:32 2008
@@ -0,0 +1,23 @@
+module ActionController
+  class Base
+    def render_voice(&block)
+      begin
+        yield response.interface
+      end
+      @performed_render = true
+    end
+
+    #generic action that can be used to detect hangups
+    def call_hung_up
+      if respond_to?(:hung_up)
+        hung_up
+      end
+    end
+
+    #used to update session before action completes
+    def update_session(key,val)
+      session[key]=val
+      session.update
+    end
+  end
+end

Added: freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/core/voice_view/dispatcher.rb
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/core/voice_view/dispatcher.rb	Thu May 22 13:16:32 2008
@@ -0,0 +1,58 @@
+module Telegraph
+  module VoiceView
+    class StdOutEmulator
+        def write(str)
+        end
+    end
+    class Dispatcher
+      attr_accessor :request
+      attr_accessor :response
+      attr_accessor :params
+      
+      require 'action_controller/dispatcher'
+      
+      def dispatch(interface)
+         ENV['REQUEST_METHOD'] = "post"
+ 
+         @cgi=CGI.new
+         @request = Telegraph::VoiceView::Request.new(interface, at cgi,nil,nil,ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS)  
+
+         @response = Telegraph::VoiceView::Response.new(interface)
+        
+         prepare_application
+            
+         ActionController::Routing::Routes.recognize(@request)
+
+         # bit of a hack.  Need to setup next_action/next_controller
+         
+         @request.next_action = @request.parameters['action']
+         @request.next_controller = @request.parameters['controller'].camelize + 'Controller'
+         
+                    
+          #Loops until we are done executing all the actions for this call
+          while @request.next_action !=nil do
+            path_params={:action=>@request.next_action, :controller=>@request.next_controller}
+            @request.path_parameters = path_params
+            @request.next_action=nil
+            ActionController::Dispatcher.new(StdOutEmulator.new, @request, @response).dispatch
+            #Check for errors
+            if @response.headers['Status'] == "500 Internal Server Error" or @response.headers['Status'] == "406 Not Acceptable"
+              @request.interface.hangup
+              break
+            end
+
+          end
+          
+          return @response.commands
+          
+      end
+      
+      def prepare_application
+          ActionController::Routing::Routes.reload if Dependencies.load?
+          require_dependency('application.rb') unless Object.const_defined?(:ApplicationController)
+          ActiveRecord::Base.verify_active_connections!
+      end
+    end
+  end
+end
+        
\ No newline at end of file

Added: freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/core/voice_view/interface.rb
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/core/voice_view/interface.rb	Thu May 22 13:16:32 2008
@@ -0,0 +1,143 @@
+module Telegraph
+  module VoiceView
+    class Interface
+      def redirect(args)
+        @request.create_redirect args
+      end
+
+      # The following has not been updated/tested with FreeSWITCH.
+      # It should work with minor modifications for someone who needs the features
+      
+      
+      # def play(str)
+      #   str.split(' ').each {|e| say_element(e)}
+      # end
+      # 
+      # def form (opts={})
+      #   form = Telegraph::CallForm.new
+      #   @params = {}
+      #   yield form
+      # 
+      #   for element in form.elements
+      #     build_form_element(element)
+      #   end
+      # 
+      #   @params.update(opts[:url])
+      #   @request.create_redirect @params
+      # end
+      # 
+      # def extract_hash(key, value)
+      #   split_key=key.split('[')
+      #   if split_key.size == 1 then
+      #     {key.to_sym => value}
+      #   else
+      #     {split_key[0].to_sym => {split_key[1].slice!(0..(split_key[1].size-2)).to_sym => value}}
+      #   end
+      # end
+      # 
+      # def nested_merge(original, added)
+      #    added.each{|key,value| 
+      #       if value.is_a?(Hash) && original[key] && original[key].is_a?(Hash)
+      #         original[key] = nested_merge original[key], value
+      #       else
+      #         original[key] = value
+      #       end   
+      #    }
+      #   original
+      # end
+      # 
+      # def link_to_dtmf(sound, args={}, &block)
+      #   @links = Array.new
+      # 
+      #   instance_eval &block
+      # 
+      #   return if @links.empty?
+      # 
+      #   max_digits = @links.max{|a, b| a[:link].to_s.to_i.to_s.length <=> b[:link].to_s.to_i.to_s.length}[:link].to_s.length
+      #   args[:timeout] ||= max_digits * 1500
+      # 
+      #   n= get_data full_sound_path(sound), args[:timeout], max_digits
+      # 
+      #   input = n.length == 0 ? 'no_response' :  n.to_s
+      # 
+      #   #find the matching link
+      #   @links.each do |link|
+      #     if link[:link].to_s == input
+      #       @request.create_redirect link
+      #       return
+      #     end
+      #   end
+      # 
+      #   #we got nothing
+      #   default = @links.detect{|l| l[:link] == :default}
+      #   @request.create_redirect default if @request.next_action.nil? and default
+      # end
+      # 
+      # def link(link, url)
+      #   url[:link] = link
+      #   @links  << url
+      # end
+      # 
+      # 
+      # def redirect_to(opts={})
+      #   @request.create_redirect opts
+      # end 
+      # 
+      # def say_datetime(time,escapeDigits=ALL_SPECIAL_DIGITS)
+      #   #calc the number of seconds elapsed since epoch (00:00:00 on January 1, 1970) 
+      #   diff = time.to_i
+      #   msg = "SAY DATETIME #{diff} #{escape_digit_string(escapeDigits)}"
+      #   send(msg)
+      #   return get_int_result()
+      # end
+      # 
+      # def say_element(item)
+      #   if item.match(/^Datetime:/) then
+      #     item.delete!('Datetime:')
+      #     say_datetime(item)
+      #   elsif item.to_i > 0 || item=='00'
+      #     say_number(item.to_i.to_s)
+      #   elsif item.to_f < 0 then
+      #     play_sound 'negative'
+      #     say_number(item.to_f.abs)
+      #   else
+      #     play_sound(full_sound_path(item))
+      #   end
+      # end
+      # 
+      # def build_form_element(element)
+      #   return get_data_element(element) if element[:type] == 'get_data'
+      #   return record_input_element(element) if element[:type] == 'record_input'
+      # end
+      # 
+      # def get_data_element(element)
+      #   num = get_data full_sound_path(element[:sound]), element[:timeout], element[:max_digits]
+      #   @params.merge!(extract_hash(element[:param], num))
+      # end
+      # 
+      # 
+      # def record_input_element(element)
+      #   play element[:label]
+      #   ret = record_file(Telegraph::Config::Globals["RECORDINGPATH"] + '/' + element[:filename], element[:max_time], element[:beep], element[:silence_detect])
+      # 
+      #   @params = nested_merge(@params, extract_hash(element[:param],element[:filename]))
+      #   #params.update(extract_hash(element[:param],element[:filename]))
+      # end
+      # 
+      # def output(filename)
+      #   play_sound full_sound_path(filename)
+      # end
+      # 
+      # def full_sound_path(filename)
+      # 
+      #   name =Telegraph::Config::Globals["SOUNDPATH"] + '/' + filename
+      #   if File.exists?(name + '.gsm') || File.exists?(name + '.sln')
+      #     name
+      #   else
+      #     filename
+      #   end
+      # end
+      
+    end
+  end
+end
\ No newline at end of file

Added: freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/core/voice_view/request.rb
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/core/voice_view/request.rb	Thu May 22 13:16:32 2008
@@ -0,0 +1,159 @@
+# Copyright (c) 2007 Jonathan Palley, Idapted Inc.
+# All rights reserved
+
+module Telegraph
+  module VoiceView
+    class Request < ActionController::AbstractRequest
+      attr_accessor :session_options, :path, :session, :env
+      attr_accessor :host, :interface, :next_action, :next_controller, :path
+    
+    #
+      def initialize(interface, cgi, query_parameters ={}, request_parameters = {}, session_opts = ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS)
+        @interface = interface      
+        @query_parameters   = query_parameters 
+        @session_options            = session_opts
+        @redirect = false
+        @env = {}
+    
+        #these allow us to do redirects (or psuedo form submits)to other actions since we can't do a HTTP 302
+        @next_action             = nil
+        @next_controller         = nil
+        @redirect_parameters     = nil
+    
+    
+        @cgi = cgi
+        @host                    = "voice_view"
+        @request_uri             = "/"
+        @env['REQUEST_METHOD']   = "POST"
+
+        @path= @interface.url
+        @session_options['session_id'] = @interface.params['session_id']
+    
+        
+        super()
+      end
+      def sound?
+        true
+      end
+      def accepts
+        [Mime::Type.lookup_by_extension('voice')]
+      end
+      
+ 
+
+    #copies parameters from the agi request (in the call_connection object)
+    #if this is a redirect we add in the redict parameters
+    def request_parameters
+      @interface.params.dup
+    end
+    
+    def query_parameters
+      @redirect_parameters || {}
+    end
+    
+    def remote_ip
+      return '123.123.123.123'
+    end
+   
+    
+
+    def create_redirect(args)
+    
+      @next_action=args.delete(:action).to_s
+      @next_controller = args.delete(:controller).to_s.camelize + 'Controller' unless args[:controller].nil?
+      
+      @redirect_parameters = args
+      parameters
+    end
+
+     def remote_addr=(addr)
+      @env['REMOTE_ADDR'] = addr
+    end
+
+    def remote_addr
+      @env['REMOTE_ADDR']
+    end
+
+    def request_uri
+      @request_uri || super()
+    end
+
+    def path
+      @path || super()
+    end
+   
+
+    def session=(session)
+      @session = session
+      @session.update
+    end
+    
+    def session  
+      unless @session
+         if @session_options == false
+            @session = Hash.new
+        else
+          stale_session_check! do
+            
+            if session_options_with_string_keys['new_session'] == true
+              @session = new_session
+            else
+              @session = CGI::Session.new(@cgi, session_options_with_string_keys)
+            end
+            session['__valid_session']
+          end
+        end
+      end
+      @session
+    end
+
+    def reset_session
+      @session.delete if CGI::Session === @session
+      @session = new_session
+    end
+
+    def method_missing(method_id, *arguments)
+      @cgi.send(method_id, *arguments) rescue super
+    end
+    
+    def logger=(logger)
+      @logger=logger
+    end
+    
+    def logger
+      @logger
+    end
+    
+    private
+      # Delete an old session if it exists then create a new one.
+      def new_session
+        if @session_options == false
+          Hash.new
+        else
+          CGI::Session.new(@cgi, session_options_with_string_keys.merge("new_session" => false)).delete rescue nil
+          CGI::Session.new(@cgi, session_options_with_string_keys.merge("new_session" => true))
+        end
+      end
+          def stale_session_check!
+        yield
+      rescue ArgumentError => argument_error
+        if argument_error.message =~ %r{undefined class/module (\w+)}
+          begin
+            Module.const_missing($1)
+          rescue LoadError, NameError => const_error
+            raise ActionController::SessionRestoreError, "Session contains objects whose class definition isn\'t available. Remember to require the classes for all objects kept in the session. (Original exception: #{const_error.message} [#{const_error.class}])"
+          end
+
+          retry
+        else
+          raise
+        end
+      end
+
+      def session_options_with_string_keys
+        @session_options_with_string_keys ||= ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS.merge(@session_options).inject({}) { |options, (k,v)| options[k.to_s] = v; options }
+      end
+  end
+  
+end
+end

Added: freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/core/voice_view/response.rb
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/core/voice_view/response.rb	Thu May 22 13:16:32 2008
@@ -0,0 +1,20 @@
+module Telegraph
+  module VoiceView
+    class Response < ActionController::AbstractResponse
+      attr_accessor :interface
+      
+      def initialize(interface)
+        @interface = interface
+        @commands = []
+        super()
+      end
+      
+      def out(output)
+      end
+      
+      def commands
+        @interface.commands
+      end
+    end
+  end
+end
\ No newline at end of file

Added: freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/core/voice_view/template.rb
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/core/voice_view/template.rb	Thu May 22 13:16:32 2008
@@ -0,0 +1,43 @@
+# :stopdoc:
+# This file makes Haml work with Rails
+# using the > 2.0.1 template handler API.
+
+module Telegraph
+  module VoiceView
+    
+  class Template
+    include ActionView::TemplateHandlers::Compilable if defined?(ActionView::TemplateHandlers::Compilable)
+
+    def self.line_offset
+      1
+    end
+
+    def compilable?
+      true
+    end
+ 
+    def line_offset
+      self.class.line_offset
+    end
+
+    def initialize(view)
+      @view = view
+    end
+
+    def compile(template)
+      #options = Haml::Template.options.dup
+      puts "COMPILE"
+      puts template
+
+      "controller.render_voice do |voice|\n #{template}\n end"
+#      Haml::Engine.new(template, options).send(:precompiled_with_ambles, [])
+    end
+   
+    def read_template_file(template_path, extension)
+      File.read(template_path)
+    end
+  end
+end
+end
+
+ActionView::Base.register_template_handler(:freeswitch, Telegraph::VoiceView::Template)

Added: freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/freeswitch/voice_events_server.rb
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/freeswitch/voice_events_server.rb	Thu May 22 13:16:32 2008
@@ -0,0 +1,164 @@
+Dir[File.join(RAILS_ROOT, "app/voice_events/**")].each do |f|
+  require f
+end
+
+module Telegraph
+  module FreeSWITCH
+    class VoiceEventsServer < EventMachine::Connection
+
+      def initialize *args
+
+        @received_buffer = ''
+        @last_command = nil
+        @closing = false
+        @routes = Telegraph::Core::Router.new
+        VoiceEventsRouter.route(@routes)
+        super
+      end
+      
+      def set_modes(verbose, debug)
+        Telegraph.log "Turning on Debug Mode" if  @debug = debug
+        Telegraph.log "Turning on Verbose Mode" if @verbose = verbose
+      end
+      
+        
+      def receive_data(data)
+        # IMPORTANT: There is no gauruntee where data starts/ends
+        # We must make sure we buffer correctly
+        @received_buffer << data
+
+        Telegraph.log "Recieved:\n#{data}" if @verbose
+        #Return unless we have a complete event
+        process_event
+        
+     
+          
+        while @received_buffer =~  /Content-Type:\s([\w\/]+)/
+          content_type = $1
+          after_buffer = $'
+          
+          #Is this an authorizatin reqest?
+          if content_type == 'auth/request'
+            @received_buffer = after_buffer
+            send_command "auth ClueCon"
+            
+          #Reply to Authorization request?
+          elsif content_type == 'command/reply'
+            @received_buffer = ''
+            if after_buffer.include?('Reply-Text: +OK accepted')
+             send_command "event plain #{subscribed_events}"
+            end
+              
+          #Everything else
+          else
+            #Search for content-length/content-type tags and extra the values
+            while @received_buffer =~ /Content-Length:\s(\d+)\nContent-Type:\s([\w\/\-]+)/
+              content_type = $2
+              @next_event_size = $1.to_i
+              
+              @received_buffer = $'.gsub(/\A\s+/, '') #Remove leading whitespaces. 
+              
+              process_event
+            end
+          end
+        end
+          
+      end
+      
+      def process_event
+        if @next_event_size and @received_buffer and @received_buffer.size >= @next_event_size
+            @event_data = @received_buffer[0..(@next_event_size-1)]
+            @recieved_buffer = @received_buffer[@next_event_size..(@received_buffer.size-1)]
+            @next_event_size = nil
+
+            #Split at content length
+            data = @event_data.split(/Content-Length:\s(\d+)\n\n/)
+            
+           headers = Hash.new
+            data[0].split("\n").each do |line| 
+              unless line.empty?
+                k, v = line.split(': ')
+                headers[k.gsub('-', '_').downcase.to_sym] = URI.decode(v) if k and v
+              end
+            end
+            
+            headers[:content] = data[2] if data[2]
+
+            event_name = headers[:event_name].downcase
+           event_name = "custom_" + headers[:event_subclass].gsub('::', '_') if headers[:event_name] == "CUSTOM"
+            begin
+               Telegraph.log "CALLING: #{event_name}"
+               @routes.dispatch!(event_name.to_sym, headers)
+               # @voice_events.params = headers
+               # @voice_events.send(event_name.to_sym)
+             rescue ActiveRecord::StatementInvalid => e
+               #If not used for a while we will lose AR Connection
+               Telegraph.log "Lost AR Connection .... "
+               ActiveRecord::Base.connection.reconnect!
+               unless @ar_reconnect_retried
+                 Telegraph.log "Reconnecting to AR..."
+                 @ar_reconnected_retried = true
+                 retry
+               end
+               raise
+             rescue Exception => e
+               Telegraph.log "Error in sending event: #{headers[:event_name]}"
+               Telegraph.log e.inspect
+             else  
+              @ar_reconnected_retried = false
+             
+             end
+          
+        end
+      end
+      def unbind
+        return if @closing
+        
+        Telegraph.log "UNBIND!"
+
+        begin
+          sleep 10
+          Telegraph.log "Attempting to reconnect..."
+          config = Telegraph.config
+          reconnect config['server'], config['events']['port']
+        rescue SystemExit => e
+          Telegraph.log "system exit"
+          exit
+        rescue Exception => e
+          Telegraph.log "Error in reconnecting..."
+          Telegraph.log e.inspect
+
+          retry
+        end
+        
+      end
+      
+      protected
+      def subscribed_events
+        return "all" if @debug
+
+        custom_events = []
+        normal_events = []
+
+        @routes.used_events.each do |e|
+          e = e.downcase
+          if e =~ /custom\_/ then
+            custom_events << $'.gsub('_', '::')
+          else
+            normal_events << e.upcase
+          end
+        end
+        
+        meths = normal_events.join(' ')
+        meths << " CUSTOM #{custom_events.join(' ')}"
+        Telegraph.log "Subscribe to: #{meths}"
+        return meths
+      end
+      
+      def send_command(str)
+        @last_command = str
+        send_data(str + "\n\n")
+      end
+    end
+  end
+end
\ No newline at end of file

Added: freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/freeswitch/voice_model/voice_channel_model.rb
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/freeswitch/voice_model/voice_channel_model.rb	Thu May 22 13:16:32 2008
@@ -0,0 +1,146 @@
+module Telegraph
+  module FreeSWITCH
+    class VoiceChannelModel
+      attr_accessor :exists
+      attr_accessor :last_error
+      attr_accessor :job_uuid
+      attr_accessor :uuid
+      
+      @uuid = nil
+      @params = nil
+      @@connector = nil
+      @exists = nil
+      @last_error = nil
+      def initialize(params={})
+        @params = params
+        @uuid = params[:uuid]
+        @job_uuid = params[:job_uuid]
+        @@connector = params[:connector] || VoiceConnector.new
+        @valid=true
+      end
+
+      %w{park hold}.each do |command|
+        class_eval <<-END
+          def #{command}()
+            puts @@connector.send_command("uuid_#{command}", at uuid)
+          end
+        END
+      end
+
+      def destroy
+        puts @@connector.send_command("uuid_kill", @uuid)
+      end
+
+      def invalidate
+        @valid = false
+      end
+      
+      def valid?
+        @valid
+      end
+
+      def start_recording(filename)
+         @@connector.send_command('uuid_record', "#{@uuid} start #{filename}")
+        
+      end
+      def stop_recording(filename)
+         @@connector.send_command('uuid_record', "#{@uuid} stop #{filename}")
+      end
+      
+      def inline_transfer(apps)
+         @@connector.send_command('uuid_transfer', "#{uuid} #{apps} inline")
+      end
+      
+      def dump
+        begin
+          
+          dump=@@connector.send_command('uuid_dump', @uuid)
+        
+          result = {}
+          dump.split("\n").each do |line|
+            l = line.split(': ')
+            result[l[0].gsub('-', '_').downcase] = result[l[1]]
+          end
+          return result
+       rescue Exception => e
+        return nil
+       end
+      end
+      
+      def method_missing(method, *args)
+        properties = self.dump
+        properties[method] ? properties[method] : properties["channel_#{method}"]
+      end
+      
+
+      def self.humanize_error_code(status)
+        case status
+        when /NO_ROUTE_DESTINATION|CHAN_NOT_IMPLEMENTED/
+          "Invalid Number.  Perhaps the user is not logged in?"
+        when /NO_USER_RESPONSE|ORIGINATOR_CANCEL/
+          "The phone was not answered."
+        when /USER_BUSY/
+          "The number is busy now.  Please try again later."
+        when /INVALID_NUMBER_FORMAT/
+          "The number is not a valid format.  Please enter another number and try again."
+        else
+          status 
+        end
+      end
+      
+      def self.find(meth, *args)
+        voice_connector = @@connector || VoiceConnector.new
+        if meth == :all
+          channels_csv = voice_connector.show(:channels)
+          if args.empty?
+            return channels_csv.map{|c| VoiceChannelModel.new(c, voice_connector)}
+          end
+        else
+          klass = self.new(:uuid=>meth)
+          if klass.dump
+            return klass
+          else
+            return nil
+          end
+        end
+      end
+    
+      def self.create(opts={})
+        voice_connector = @@connector || VoiceConnector.new
+        
+        destination = opts[:destination]
+        cid_name = opts[:cid_name] || 'idapted'
+        cid_number = opts[:cid_number] || '000'
+        variables = opts[:vars] || {}
+        variables[:origination_caller_id_name] = cid_name
+        variables[:origination_caller_id_number] = cid_number
+        variables[:ignore_early_media] = "true"
+        
+        variables = variables.map{|k,v| "#{k}=#{v}"}.join(',')
+
+        if callback = opts[:callback]
+          port = opts[:port] || '8084'
+          #Remove http://blah/ if it exists
+          callback.gsub!(/http:\/\/[\d\w\.:]+\//, '')
+          params = "&socket(${rails_server}:#{port}/#{callback} async full)"
+        else
+          params = opts[:params]
+        end
+        
+        begin
+          command = "originate {#{variables}}#{destination} #{params}"
+          job = voice_connector.send_command('bgapi', command)
+          job_uuid = job.gsub('Job-UUID: ', '').gsub("\n", '')
+          return self.new :job_uuid=>job_uuid, :connector=>voice_connector
+        rescue Exception => e
+
+          mdl = self.new
+          mdl.last_error = e.message
+          mdl.invalidate
+          return mdl
+        end
+      end
+    end
+    
+  end
+end
\ No newline at end of file

Added: freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/freeswitch/voice_model/voice_conference_model.rb
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/freeswitch/voice_model/voice_conference_model.rb	Thu May 22 13:16:32 2008
@@ -0,0 +1,105 @@
+module Telegraph
+  module FreeSWITCH
+    class VoiceConferenceModel
+      attr_accessor :name
+      attr_accessor :members
+      @name = nil
+      @members = nil
+      @@connector = nil
+      def initialize(name, members_data)
+        return nil if members_data =~ /not found/
+        @name = name
+        @@connector ||= VoiceConnector.new
+        @members = VoiceConferenceMemberCollection.new
+        members_data.split("\n").each do |member_data|
+          @members << VoiceConferenceMember.new(member_data, name, @@connector)
+        end
+      end
+      
+      def start_recording(filename)
+        @connector.send_command('conference', "#{@name} record #{filename}")
+      end
+
+      def stop_recording(filename)
+        @connector.send_command('conference', "#{@name} norecord #{filename}")
+      end
+      
+      def self.find(id)
+         @@connector ||= VoiceConnector.new
+
+        if id == :all
+          data = @@connector.send_command('conference', 'list')
+          #TODO: Deal with no conference
+          conferences_data = data.split(/Conference\s([\d\w]+)\s[\w\s\d\(]+\)\n/)
+          conferences_data = conferences_data[1..conferences_data.size-1] #Pop off first one..its useless
+          conferences = []
+          while not conferences_data.empty?
+            members = conferences_data.pop
+            name = conferences_data.pop
+            conferences << self.new(name, members)
+          end
+          return conferences
+        else
+          data = @@connector.send_command('conference', "#{id} list")
+          return nil if data =~ /not found/
+          return self.new(id, data)
+      end
+      end
+    end
+    
+    class VoiceConferenceMemberCollection < Array
+      def find(id)
+        self.detect{|o| o.id.to_s == id.to_s}
+      end
+    end
+    class VoiceConferenceMember
+      attr_accessor :id, :channel, :uuid, :deaf, :mute, :volume_in, :volume_out, :energy, :conf_name
+      
+      def initialize(data, conf_name, voice_connector)
+        data = data.split(';')
+        @id = data[0]
+        @channel = data[1]
+        @uuid = data[2]
+        @mute = !(data[5] =~ /speak/) and true
+        @deaf = !(data[5] =~ /hear/) and true
+        @volume_in = data[6]
+        @volume_out = data[7]
+        @energy = data[8]
+        @conf_name = conf_name
+        @connector = voice_connector
+      end
+      
+      def mute?
+        @mute
+      end
+      
+      def deaf?
+        @deaf
+      end
+      
+      def update_attributes(params)
+        puts "UPDATE"
+        pp params
+        params.each do |k,v|
+          puts "param"
+          puts k.to_sym
+          if [:mute, :deaf].include?(k.to_sym)
+            tru = (v and not v == "0")
+            prefix = tru ? '' : 'un'
+            unless tru == self.send(k.to_sym)
+              if @connector.send_command('conference', "#{conf_name} #{prefix}#{k.to_s} #{id}")
+                self.send("#{k.to_s}=".to_sym, v)
+              end
+            end
+          elsif [:volume_in, :volume_out, :energy].include?(k.to_sym)
+            puts "in valume"
+            if @connector.send_command('conference', "#{conf_name} #{k.to_s} #{id} #{v}")
+              self.send("#{k}=", v)
+            end
+          end
+        end
+      end
+            
+    end
+  end
+end
\ No newline at end of file

Added: freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/freeswitch/voice_model/voice_connector.rb
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/freeswitch/voice_model/voice_connector.rb	Thu May 22 13:16:32 2008
@@ -0,0 +1,99 @@
+require 'xmlrpc/client'
+require 'xmlrpc/marshal'
+require 'pp'
+
+
+module Telegraph
+  module FreeSWITCH 
+  class VoiceModelFunctionError < StandardError; end
+  class VoiceModelResponseError < StandardError; end
+  
+  
+
+  
+  class VoiceConnector
+    SHOW_COMMANDS = %w{codec application api dialplan file timer calls channels}
+    @@connection = nil
+    @@connection_options = nil
+    
+    def self.connect!(opts)
+      Telegraph::FreeSWITCH::VoiceModel.new.connect(opts)
+    end
+    
+    def self.load_configuration_and_connect!
+      config = Telegraph.config
+      
+      Telegraph::FreeSWITCH::VoiceConnector.new.connect!(:server=>config['server'], :port=>config['model']['port'], :username=>config['model']['username'], :password=>config['model']['password'], :timeout=>10)
+    end
+    
+    def connect!(opts={})
+      #   opts[:timeout] =|| 10
+      @@connection_options = opts
+    
+      @@connection = XMLRPC::Client.new(@@connection_options[:server], '/RPC2', @@connection_options[:port], nil, nil, @@connection_options[:username], @@connection_options[:password], nil, @@connection_options[:timeout])
+    end
+  
+    def show(command, options='')
+      return parse_csv_result(send_command('show', "#{command} #{options}"))
+    end
+    
+    def method_missing(meth, *args)
+      send_command(meth.to_s, args.join(' '))
+    end
+    
+    def self.find(command, options='')
+      voice_model = Telegraph::FreeSWITCH::VoiceModel.new
+      if SHOW_COMMANDS.include?(command.to_s)
+        result = voice_model.send_command('show', "#{command} #{options}")
+        return voice_model.parse_csv_result(result)
+      end
+      
+    end
+    
+        
+        
+    
+    def parse_csv_result(result)
+      name_array = []
+      return_array = []
+      result.split("\n").each do |line|
+        if name_array.empty?
+          name_array = line.split(',')
+        elsif line.empty?
+          break
+        else
+          item = Hash.new
+          line.split(',').each_with_index{|v,i| item[name_array[i].to_sym]=v}
+          return_array << item
+        end
+      end
+      
+      return return_array
+    end
+          
+    def send_command(command, options='')
+      result = @@connection.call("freeswitch.api", command, options)
+      #Parse results
+      if result =~ /ERROR/
+        raise VoiceModelFunctionError, "Invalid Command"                              
+      elsif result =~ /^-ERR/
+        raise VoiceModelResponseError, result.gsub('-ERR', '')
+      else
+        return (result.gsub('+OK ', '') || true)
+      end 
+    end
+  end
+end
+end
+
+# puts 'start'
+# v = Telegraph::FreeSWITCH::VoiceConnector.new
+# puts 'v'
+# v.connect!(:server=>'192.168.1.10', :port=>8080, :username=>'freeswitch', :password=>"works")
+# puts '2'
+# channels = Telegraph::FreeSWITCH::VoiceChannelModel.find(:all)
+# pp channels
+# 
+# channels.first.destroy
+# 
+# puts '4'
\ No newline at end of file

Added: freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/freeswitch/voice_model/voice_sip_model.rb
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/freeswitch/voice_model/voice_sip_model.rb	Thu May 22 13:16:32 2008
@@ -0,0 +1,80 @@
+module Telegraph
+  module FreeSWITCH
+    class VoiceSipModel
+      attr_accessor :profile
+      attr_accessor :gateway
+      attr_accessor :params
+      def initialize(connector = nil)
+        @@connector = @@connector || connector || VoiceConnector.new
+        @params = {}
+      end
+      
+      def self.find_by_gateway(gateway)
+        klass = self.new
+        klass.gateway = gateway
+        klass.profile = nil
+        klass
+      end
+      
+      def self.find_by_profile(profile)
+        klass = self.new
+        klass.profile = profile
+        klass.gateway = nil
+        klass
+      end
+      
+      def self.find_by_username(username, profile="default")
+          registrations = self.find_all
+          registrations.detect{|r| r.params[:user] =~ /^#{username}\@/}
+      end
+      
+      def self.find_all(profile="default")
+        @@connector ||=VoiceConnector.new
+        @@data_cache ||= Hash.new
+        if @@data_cache[:set_at].nil? or @@data_cache[:set_at] < 3.second.ago.utc
+          data = @@connector.send_command("sofia", "status profile default")
+          @@data_cache[:data] = data
+          @@data_cache[:set_at] = Time.now.utc
+        else
+          data = @@data_cache[:data]
+        end
+        
+        registrations = data.split("Registrations:\n")[1]
+        registrations = registrations.split("\n\n")
+        registrations = registrations.find_all{|r| r.split("\n").size > 2} 
+        registrations = registrations.map do |r| 
+          klass = self.new
+          klass.profile = profile
+          regs = r.split("\n")
+          regs.each do |p|
+            p = p.split(" \t")
+            klass.params[p[0].gsub(' ', '').downcase.gsub('-', '_').to_sym] = p[1] if p.size > 1
+          end
+          klass
+        end
+        return registrations
+      end
+      
+      def flush
+        re= @@connector.send_command("sofia", "profile #{profile} flush_inbound_reg #{@params[:call_id]}")
+        @params = {} if re=~ /OK/
+      end
+      
+      def restart_and_reload
+        @@connector.send_command("sofia", "profile #{profile} restart all reloadxml")
+      end
+      
+      def status
+        if @gateway
+          @@connector.send_command("sofia", "status gateway #{@gateway}")
+        else
+          @@connector.send_command("sofia", "status profile #{profile}")
+        end
+      end
+    end
+  end
+end
+        
+      
+      
+      
\ No newline at end of file

Added: freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/freeswitch/voice_view_interface.rb
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/freeswitch/voice_view_interface.rb	Thu May 22 13:16:32 2008
@@ -0,0 +1,102 @@
+module Telegraph
+  module FreeSWITCH
+    class VoiceViewInterface < Telegraph::VoiceView::Interface
+      #The following is the interface to the freeswitch voice view functions...
+      attr_accessor :params, :commands
+      
+      def initialize(params)
+        ENV['REQUEST_METHOD'] = "post"
+        puts "INITIALIZE VOICE"
+        @params = params
+        @commands = Array.new
+        @cgi=CGI.new
+      end
+  
+      def url
+        '/' + (@params['variable_socket_path'] || '')
+      end
+  
+      #spell macro
+      def spell(words)
+        phrase "spell", words
+      end
+  
+      #spell phonetic macro
+      def spell_phonetic(words)
+        phrase "spell-phonetic", words
+      end
+  
+      #say time macro
+      def say_time(time = Time.now.utc)
+        phrase "saydate", time.to_i
+      end
+  
+      #say time difference
+      def say_timespec(time)
+        phrase "timespec", time
+      end
+  
+      #answer channel
+      def answer
+        msg "answer"
+      end
+  
+      def play_sound(file)
+        msg "playback", file
+      end
+  
+      def record(filename, length, silence_threshold=500, silence_time=5)
+        msg "record", "#{filename} #{length} #{silence_threshold} #{silence_time}"
+      end
+  
+      def phrase(macro, data)
+        msg "phrase", "#{macro},#{data}"
+      end
+    
+      def bridge(data)
+        msg "bridge", data
+      end
+      
+      def set(data)
+        msg "set", data
+      end
+      
+      def conference(data)
+        msg "conference", data
+      end
+      
+      def hangup
+        @commands << "SendMsg #{params['unique-id']}\ncall-command: hangup\nhangup-cause: SERVICE_NOT_IMPLEMENTED"
+      end
+      
+      def hold
+        @commands << "SendMsg #{params['unique-id']}\ncall-command: hold"
+      end
+      
+      def ring_ready
+        msg "ring_ready"
+      end
+      
+      def socket(url)
+        msg "socket", "#{params['variable_socket_host']}:8084#{url} async full"
+      end
+      
+      def fifo_in(queue_name, announce = "undef", music="undef")
+        msg "fifo", "#{queue_name} in #{announce} #{music}"
+      end
+      
+      def fifo_out(queue_name, announce="undef", music="undef")
+        msg "fifo", "#{queue_name} out nowait #{announce} #{music}"
+      end
+      
+      def export(val)
+        msg "export", val
+      end
+      
+      private
+      def msg(app, params='')
+        @commands << {:type=>:msg, :app=>app, :params=>params}
+      end
+    end
+  end
+end
\ No newline at end of file

Added: freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/freeswitch/voice_view_server.rb
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/freeswitch/voice_view_server.rb	Thu May 22 13:16:32 2008
@@ -0,0 +1,79 @@
+require 'eventmachine'
+
+   module VoiceViewServer
+     def post_init
+       Telegraph.log "New Connection"
+       @receive_buffer = ''
+       @connection_params = []
+       @command_queue = []
+       @command_queue <<    "log DEBUG\n\n"
+       @command_queue << "myevents\n\n"
+          
+       send "connect"
+     end
+     
+     def set_modes(verbose, debug)
+       Telegraph.log "Turning on Debug Mode" if  @debug = debug
+       Telegraph.log "Turning on Verbose Mode" if @verbose = verbose
+     end
+      
+     def receive_data data
+       @receive_buffer << data
+       
+       Telegraph.log "Received:\n#{data}" if @verbose
+       
+       #If we've recieved the last line of the response
+       if @receive_buffer =~ /\n\n/ 
+         #Hashify params and clear buffer
+         @params = hashify @receive_buffer
+         @receive_buffer = ''
+         
+         #Set connection params if this is the first response
+         unless @command_queue.empty?
+            command = @command_queue.shift
+            if command.is_a?(Hash)
+              send_application command[:app], command[:params]
+            else
+              send command
+            end
+          else
+            #close_connection
+          end
+          
+          if @connection_params.empty?
+            @connection_params = @params
+         
+            make_commands
+          end
+ 
+        end
+     end
+     
+     def make_commands
+       interface = Telegraph::FreeSWITCH::VoiceViewInterface.new(@connection_params)
+       @command_queue = @command_queue + Telegraph::VoiceView::Dispatcher.new.dispatch(interface)
+      end
+
+     def unbind
+       puts "UNBIND"
+     end
+     
+     def hashify data
+       hsh = Hash.new
+       data.split("\n").each do |line|
+         hsh[line.split(': ')[0].gsub('-', '_').downcase] = line.split(': ')[1] unless line.empty?
+       end
+       return hsh
+    end
+    
+     def send_application app, params=nil
+       msg = "SendMsg #{@connection_params['unique-id']}\ncall-command: execute\nexecute-app-name: #{app}"
+       msg << "\nexecute-app-arg: #{params}" #if params
+       send msg
+     end
+     
+     def send data
+       Telegraph.log "Sending:\n#{data}" if @verbose
+       send_data data + "\n\n"
+      end
+   end
\ No newline at end of file

Added: freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/telegraph.yml
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/telegraph.yml	Thu May 22 13:16:32 2008
@@ -0,0 +1,18 @@
+development:
+    server: 192.168.1.27
+    model:
+        port: 8080
+        username: freeswitch
+        password: works
+    events:
+        port: 8021
+        
+
+production:
+    server: voip1.lan
+    model:
+        port: 8080
+        username: freeswitch
+        password: works
+    events:
+        port: 8021
\ No newline at end of file

Added: freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/voice_events
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/voice_events	Thu May 22 13:16:32 2008
@@ -0,0 +1,21 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../vendor/plugins/telegraph/lib/core/script_base'
+
+Telegraph::ScriptHandler.new('voice_events') do |v, d|
+
+  telegraph_path = 'vendor/plugins/telegraph/lib'
+  require File.join(RAILS_ROOT, telegraph_path, 'core/voice_events/voice_events_base')
+  require File.join(RAILS_ROOT, telegraph_path, 'core/voice_events/router')
+
+  require File.join(RAILS_ROOT, telegraph_path, 'freeswitch/voice_events_server')
+
+
+  config = Telegraph.config
+
+  EventMachine::run {
+     EventMachine::connect config['server'], config['events']['port'], Telegraph::FreeSWITCH::VoiceEventsServer do |conn|
+       conn.set_modes v, d
+    end
+  }
+  
+end

Added: freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/voice_view
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/lib/voice_view	Thu May 22 13:16:32 2008
@@ -0,0 +1,24 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../vendor/plugins/telegraph/lib/core/script_base'
+
+Telegraph::ScriptHandler.new('voice_view') do |v, d|
+   files = ['core/voice_view/dispatcher','core/voice_view/request', 'core/voice_view/template', 'core/voice_view/response',
+     'core/voice_view/interface', 'core/voice_view/action_controller_extensions', 'freeswitch/voice_view_interface', 'freeswitch/voice_view_server']
+   
+   plugin_path = File.join(RAILS_ROOT, 'vendor/plugins/telegraph/lib')
+   
+   files.each do |file|
+     require File.join(plugin_path, file)
+   end
+   
+   
+   EventMachine::run {
+         EventMachine::start_server "0.0.0.0", 8084, VoiceViewServer do |server|
+           server.set_modes v, d
+          end
+          puts "Now Accept Connections on Port 8084"
+         
+      }
+ end
+    

Added: freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/license.txt
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/license.txt	Thu May 22 13:16:32 2008
@@ -0,0 +1,25 @@
+COPYRIGHT:
+Copyright (c) 2008 Idapted Ltd.
+
+AUTHORS:
+Jonathan Palley (Primary)
+
+LICENSE:
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+	
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file

Added: freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/test/ami_test.rb
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/test/ami_test.rb	Thu May 22 13:16:32 2008
@@ -0,0 +1,120 @@
+
+require 'test/unit'
+
+require File.expand_path(File.join(File.dirname(__FILE__), '../../../../config/environment.rb'))
+class RAI::TelegraphTCP
+  @@recieved_buffer = Array.new
+  @@write_buffer = Array.new
+  def set_recieved(data, add_crlf = true)
+    if data.kind_of?(Array)
+      data.reverse_each do |r|
+        add_to_recieved_buffer(r, add_crlf)
+      end
+    elsif data =~ /\n/
+      data.split("\n").reverse_each do |r|
+        add_to_recieved_buffer(r.gsub(/^\s+/, ""), true)
+      end
+    else
+      add_to_recieved_buffer(data, add_crlf)
+    end
+  end
+  
+  def add_to_recieved_buffer(line, add_crlf)
+    line = line + "\n\r" if add_crlf
+    @@recieved_buffer << line
+  end
+  
+  def clear_recieved_buffer
+    @@recieved_buffer.clear
+  end
+  
+  def gets
+    puts "gets"
+    s =@@recieved_buffer.pop
+    puts "Sending: " + s
+    return s
+  end
+  
+  def initialize(host=nil, port=nil)
+    puts "initialize"
+  end
+  
+  def write(w)
+    @@write_buffer << w
+  end
+  
+  def written
+    @@write_buffer
+  end
+  
+  def clear_write_buffer
+    @@write_buffer.clear
+  end
+  
+end
+
+
+class AMIServerTest < Test::Unit::TestCase
+  def setup
+    @blah = Test::Unit::MockObject(TCPSocket).new
+    @tcp = RAI::TelegraphTCP.new
+    @srv = RAI::AMIServer.new
+  end
+  
+  def test_mock_tcp
+    @tcp.set_recieved('Hi There')
+    assert_equal "Hi There\n\r", @tcp.gets
+    
+    @tcp.set_recieved(['Hi There', 'Long time no talk'], false)
+    assert_equal 'Hi There', @tcp.gets
+    assert_equal 'Long time no talk', @tcp.gets
+         
+    s = <<-EOL 
+      line_1
+      line_2
+      line_3
+    EOL
+    puts s
+    
+    @tcp.set_recieved(s)
+    assert_equal "line_1\n\r", @tcp.gets
+    
+    @tcp.write("You there?")
+    assert  @tcp.written.include?('You there?')
+    
+    
+  end
+   
+  def test_connects
+    puts "test_connect"
+    
+    s = <<-EOM
+    Asterisk Call Manager/1.0
+    Response: Success
+    ActionID: 1177582847.67069
+    Challenge: 18535068
+    
+    Response: Success
+    ActionID: 1177582847.84917
+    Message: Authentication accepted
+    
+    EOM
+    
+    @tcp.set_recieved(s)
+    tcp2 = RAI::TelegraphTCP.new
+    assert_equal tcp2.gets, "Asterisk Call Manager/1.0\n\r"
+    puts "recieved"
+    
+     @srv.connect
+     
+#     puts "connect"
+#     assert_written 'Action: challenge'
+#     assert_written 'Action: login'
+  end
+  
+  
+  private 
+  def assert_written(line)
+    assert @tcp.written.include?(line)
+  end
+end
\ No newline at end of file

Added: freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/test/rai_test.rb
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/contrib/jpalley/telegraph/trunk/test/rai_test.rb	Thu May 22 13:16:32 2008
@@ -0,0 +1,8 @@
+require 'test/unit'
+
+class RaiTest < Test::Unit::TestCase
+  # Replace this with your real tests.
+  def test_this_plugin
+    flunk
+  end
+end



More information about the Freeswitch-svn mailing list