Librevox

A Ruby library for interacting with FreeSWITCH through mod_event_socket, using async I/O.

Prerequisites

You should be familiar with mod_event_socket and the differences between inbound and outbound event sockets before getting started.

Requires Ruby 3.0+.

Installation

Add to your Gemfile:

gem "librevox"

Inbound listener

Subclass Librevox::Listener::Inbound to create an inbound listener. It connects to FreeSWITCH and subscribes to events.

Events

React to events in two ways:

  1. Override on_event, called for every event.
  2. Use event hooks for specific event names.
class MyInbound < Librevox::Listener::Inbound
  def on_event(e)
    puts "Got event: #{e.content[:event_name]}"
  end

  event :channel_hangup do
    do_something
  end

  # The hook block receives a Response when it takes an argument:
  event :channel_bridge do |e|
    puts e.content[:caller_caller_id_number]
  end

  def do_something
    # ...
  end
end

Event filtering

By default, inbound listeners subscribe to all events. Use events to limit which events are received, and filters to filter by header values:

class MyInbound < Librevox::Listener::Inbound
  events ['CHANNEL_EXECUTE', 'CUSTOM foo']
  filters 'Caller-Context' => ['default', 'example'],
          'Caller-Privacy-Hide-Name' => 'no'
end

Note on CUSTOM events: FreeSWITCH custom events have a subclass name (e.g. CUSTOM conference::maintenance). You must include both the event name and subclass — events ['CUSTOM conference::maintenance']. Using just events ['CUSTOM'] will not match any custom events.

Outbound listener

Subclass Librevox::Listener::Outbound to create an outbound listener. FreeSWITCH connects to it when a call hits a socket application in the dialplan.

Outbound listeners have the same event functionality as inbound, but scoped to the session.

Dialplan

When FreeSWITCH connects, session_initiated is called. Build your dialplan here.

Each application call blocks until FreeSWITCH signals completion (CHANNEL_EXECUTE_COMPLETE), so applications execute sequentially:

class MyOutbound < Librevox::Listener::Outbound
  def session_initiated
    answer
    digit = play_and_get_digits "enter-digit.wav", "bad-digit.wav"
    bridge "sofia/gateway/trunk/#{digit}"
  end
end

Applications that read input (like play_and_get_digits and read) return the collected value directly.

def session_initiated
  answer
  set "foo", "bar"
  multiset "baz" => "1", "qux" => "2"
  playback "welcome.wav"
  hangup
end

For apps not yet wrapped by a named helper, call application directly:

application "park"

Channel variables are available through session (a hash) and variable:

def session_initiated
  answer
  number = variable(:destination_number)
  playback "greeting-#{number}.wav"
end

API commands

To avoid name clashes between applications and commands, commands are accessed through api:

def session_initiated
  answer
  api.status
  api.originate 'sofia/user/coltrane', extension: "1234"
end

Starting listeners

Start a single listener:

Librevox.start MyInbound

With connection options:

Librevox.start MyInbound, host: "1.2.3.4", port: 8021, auth: "secret"

Start multiple listeners:

Librevox.start do
  run MyInbound
  run MyOutbound, port: 8084
end

Default ports are 8021 for inbound and 8084 for outbound.

Closing connections

After a session ends (e.g. the caller hangs up), the outbound socket connection to FreeSWITCH remains open for post-session events. Close it manually when done to avoid lingering sessions:

class MyOutbound < Librevox::Listener::Outbound
  event :channel_hangup do
    disconnect
  end
end

Command socket

Librevox::CommandSocket connects to the FreeSWITCH management console for one-off commands:

require "librevox/command_socket"

socket = Librevox::CommandSocket.new(server: "127.0.0.1", port: 8021, auth: "ClueCon")

socket.originate 'sofia/user/coltrane', extension: "1234"
#=> #<Librevox::Protocol::Response ...>

socket.status
#=> #<Librevox::Protocol::Response ...>

socket.close

Configuration

Librevox.options[:log_file]  = "librevox.log"  # default: STDOUT
Librevox.options[:log_level] = Logger::DEBUG    # default: Logger::INFO