Mod xml curl Ramaze/Sequel groups example
From FreeSWITCH Wiki
This is the portion of TinyQueue (http://code.rubyists.com/projects/tq)
Which returns the dial groups. This is written using Sequel for the database layer and Ramaze for the web framework. First, the configuration, in /usr/local/freeswitch/conf/autoload_confs/xml_curl.conf.xml
<configuration name="xml_curl.conf" description="cURL XML Gateway">
<bindings>
<binding name="dynamic_group_conf">
<param name="gateway-url" value="http://127.0.0.1:8080/directory.xml" bindings="directory"/>
</binding>
</bindings>
</configuration>
And in /usr/local/freeswitch/conf/dialplan/default/04_group_dial.xml
<include>
<extension name="Group IT">
<condition field="destination_number" expression="9988">
<action application="set" data="ringback=${texas-ring}"/>
<action application="set" data="hangup_after_bridge=true"/>
<action application="set" data="continue_on_fail=true"/>
<action application="set" data="call_timeout=10"/>
<action application="bridge" data="group/it@$${domain}+F"/>
<action application="transfer" data="9988 XML default" />
<action application="hangup" />
</condition>
</extension>
</include>
Now for the Sequel models, migration for agents: 001_agents_table.rb
require "sequel"
class AgentsTable < Sequel::Migration
def up
create_table :agents do |t|
primary_key :id
varchar :first_name, :null => false
varchar :last_name, :null => false
varchar :extension, :null => false
end
end
def down
drop_table :agents if DB.table_exists?(:agents)
end
end
Our table to hold queues, which have agents: 002_tiny_queues_table.rb
class TinyQueuesTable < Sequel::Migration
def up
create_table :tiny_queues do |t|
primary_key :id
varchar :name, :null => false
text :description
end
end
def down
drop_table :tiny_queues if DB.table_exists?(:tiny_queues)
end
end
And the join table for these: 003_agents_tiny_queues_table.rb
class AgentsTinyQueuesTable < Sequel::Migration
def up
create_table :agents_tiny_queues do |t|
primary_key :id
foreign_key :tiny_queue_id, :references => :tiny_queues, :null => false
foreign_key :agent_id, :references => :agents, :null => false
varchar :status, :null => false, :default => "Idle"
timestamp :status_stamp, :default => "now()"
timestamp :last_call_stamp
timestamp :login_stamp, :null => false, :default => "now()"
boolean :login, :null => false, :default => false
end
end
def down
drop_table :agents_tiny_queues if DB.table_exists?(:agents_tiny_queues)
end
end
Load those with sequel -m (see docs for full command line) The models which correspond with the above tables follow agent.rb
class Agent < Sequel::Model
one_to_many :agents_tiny_queues
many_to_many :tiny_queues, :join_table => :agents_tiny_queues
def logged_in?
self.agents_tiny_queues.first.login
end
def login
now = Time.now
self.agents_tiny_queues.each do |agent|
agent.update_with_params(:status => "Ready", :status_stamp => now, :login => true, :login_stamp => now, :last_call_stamp => now)
end
end
def logoff
now = Time.now
self.agents_tiny_queues.each do |agent|
agent.update_with_params(:status => "Offline", :status_stamp => now, :login => false)
end
end
def fullname
"%s %s" % [first_name, last_name]
end
end
agents_tiny_queue.rb
class AgentsTinyQueue < Sequel::Model many_to_one :tiny_queue many_to_one :agent end
and tiny_queue.rb
class TinyQueue < Sequel::Model
many_to_many :agents, :join_table => :agents_tiny_queues
one_to_many :unavailable_agents, :class => "AgentsTinyQueue" do |ds|
ds.filter(:login => false).order(:last_call_stamp.desc, :login_stamp.asc)
end
one_to_many :consumers, :class => "AgentsTinyQueue" do |ds|
ds.filter(:login => true).order(:last_call_stamp.asc, :login_stamp.asc)
end
end
The Ramaze controller - controller/main.rb
# Default url mappings are:
# a controller called Main is mapped on the root of the site: /
# a controller called Something is mapped on: /something
# If you want to override this, add a line like this inside the class
# map '/otherurl'
# this will force the controller to be mounted on: /otherurl
Ramaze::Route["/directory.xml"] = "/directory"
class MainController < Controller
# the index action is called automatically when no other action is specified
# scaffold_all_models :except => ['init']
def index
end
def directory
if request[:section] == "directory" and request[:group]
@queues = TinyQueue.all
if this_queue = @queues.detect { |n| n.name.downcase == request[:group_name] } # request[:group_name] must match a TinyQueue name
Ramaze::Log.info "Sending Group.conf, requested #{request[:group_name]} matched #{this_queue}"
@queues = TinyQueue.all
response['Content-Type'] = 'text/xml'
output = render_template("groups.rhtml")
got_called = this_queue.consumers.first
got_called.update_with_params(:last_call_stamp => Time.now)
output
else
not_found # MUST return not_found or fs gets an error parsing the empty response as xml
end
else
not_found # If it's not a directory request, return not_found so the static files get looked up as well
end
end
# the string returned at the end of the function is used as the html body
# if there is no template for the action. if there is a template, the string
# is silently ignored
def notemplate
"there is no 'notemplate.xhtml' associated with this action"
end
def not_found
respond '<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<include>
<document type="freeswitch/xml">
<section name="result">
<result status="not found" />
</section>
</document>
</include>', 200, "Content-Type" => "text/xml"
end
end
And the view: view/groups.rhtml (Erubis templating)
<document type="freeswitch/xml">
<section name="directory" description="group test">
<domain name="$${domain}">
<groups>
<% @queues.each do |queue| %>
<group name="#{queue.name}">
<users>
<% queue.consumers.each do |consumer| %>
<% agent = consumer.agent %>
<% if agent.extension.to_s.match(/^sofia/) %>
<user id="#{agent.fullname.to_s.gsub(/\s/,'_')}">
<params>
<param name="dial-string" value="#{agent.extension}"/>
</params>
<variables>
<variable name="user_context" value="default"/>
</variables>
</user>
<% else %>
<user id="#{agent.fullname.to_s.gsub(/\s/,'_')}">
<params>
<param name="dial-string" value="user/#{agent.extension}@$${domain}"/>
</params>
<variables>
<variable name="user_context" value="default"/>
</variables>
</user>
<% end %>
<% end %>
</users>
</group>
<% end %>
</groups>
</domain>
</section>
</document>

