You should be experiencing difficulties with all outbound calls that end up with default route (Voipbuster). The thing is that Voipbuster and VoipBuster are different names, from Ruby's point of view (case sensitive). The same applies to Smonica and SMonica.
Code: Select all
# Copyright 2010 Mike Telis
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
require 'mikesgem'
# ************* C O N F I G U R A T I O N S E C T I O N *************** #
Country = '52'
Area = '33' # my area code, this will be added to 7-digit dialouts
Tz = -6 # my time zone (GMT format, e.g. Eastern = -5, Central = -6)
# Speed dial entries. Format: "key" => "number"
Speeddial = {
'0' => '22210910', # Home
'21' => '*#2 (646) 657-8199', # New York Voice Mail, dial my NY GV using Santa Monica GV acnt
'27' => 'grrivado@sipsorcery.com', # a grace via sipsorcery account
'277' => '10312255', # Grace
'411' => '(800) 466-4411', # Google's Directory Assistance, GOOG-411
'303' => '303@sip.blueface.ie', # Blueface speaking clock (Ireland time)
'266' => '4153767253@podlinez.net', # CNN Headlines (266 = "CNN")
'677' => '8186882773@podlinez.net', # NPR's most e-mailed stories (677 ="NPR")
'742' => '6506441934@podlinez.net', # Prairie Home Companion's, or PHC's
# News from Lake Wobegon (742 = "PHC")
'932' => '7755333366', # Columbus OH-based national weather (932 = "WEA[ther]")
}
# CNAM table: number in ENUM format => caller's name
CNAM = {
'(212) 555-1212' => 'Dear mom',
'(215) 333-2211' => 'Bratty kid',
}
# Uncomment next line and insert your White Pages API key, if you have it
# WP_key = '427961d1960ac97d0799e6562bbc3bd9dsewe' # White Pages API key
# Uncomment line below to enable misdialing safeguards
EnableSafeguards = 1
# Excluded Prefixes. Provides a safeguard against accidentally calling premium numbers
ExcludedPrefixes = [
' 1 (900 | 809)', # USA Premium
' 1 \d\d\d 555 1212', # USA Directory assistance
' 52 (900 | 809)', # Mexico Premium
]
# Yet another safeguard, list of blessed country codes
Allowed_Country = %w{
1 52 54
}
# My own ENUM database
MyENUM = {
'+1 (408) 334-1234' => 'brother@local', # Brother George
}
# Enum database list
EnumDB = [
MyENUM, # look in MyENUM first
'e164.org',
'e164.info',
'e164.arpa',
'e164.televolution.net',
'enum.org',
]
Host = 'sipsorcery.com' # Replaces "host" on incoming calls
# Google Voice accounts
# SIP accounts
Nextel = VSP.new '#0', ' ${EXTEN}@Nextel', 'Nextel route'
RealEstate = VSP.new '#2', ' ${EXTEN}@RealEstate', 'RealEstate route'
VoipBuster = VSP.new '#3', '00 ${EXTEN}@VoipBuster', 'Voipbuster route'
Casa = VSP.new '#4', ' ${EXTEN}@Home', 'Casa route'
DFine = VSP.new '#5', ' ${EXTEN}@DFine', 'DFine route'
Monterrey = VSP.new '#8', ' ${EXTEN}@Mont', 'Mont route'
Voxalot = VSP.new '#6', ' ${EXTEN}@Voxalot', 'Voxalot Internacional Route'
INUM = VSP.new nil, '011${EXTEN}@Gizmo', 'Gizmo'
NewYork = GV.new '*#1', nil, 'GV-NY', :usr => '*****', :pwd => '*****',
:cb => '(305) 760-XXXX', :repeat => 2
SMonica = GV.new '*#2', nil, 'GV-SM', :usr => '*****', :pwd => '*****',
:cb => '(305) 760-XXXX', :repeat => 2
Provider '**', 'DisableSafeGuards' # Disable safeguards prefix is **
# ******************** s e l e c t V S P *******************************
def selectVSP # VoIP provider selection
case @num
when /^\*/ # For *500, *600 and other Voxalot services
route_to Voxalot, "Voxalot Services", nil
when /^883/ # iNUM
route_to INUM, "iNUM", nil # disable ENUM
when /(^1([2-9]\d\d)[2-9]\d{6})/ # North America
@num = $1 # Truncate to 11 digits
case $2 # check area code
when "800", "866", "877", "888" # toll free numbers...
route_to NewYork, "USA toll-free", nil # call from GV/New York, disable ENUM search
when "424" # Santa Monica CA patch, I have GV number there
route_to SMonica, "Santa Monica"
else
route_to NewYork # all other destinations within US & Canada
end
when /^52(\d\d)/
case $1
when '33' then route_to Nextel, 'Local'
when '55' then route_to DFine, 'Mexico city'
when '81' then route_to Monterrey, 'Monterrey'
end
else
rejectCall(603,"Number's too short, check & dial again") if @num.length < 9
route_to VoipBuster, 'Default route'
end
end
# ------------ O P T I O N A L C O N F I G U R A T I O N --------------- #
# ******************** i n c o m i n g C a l l *************************
def incomingCall
sys.SetFromHeader(formatNum(@cname || @cid,true), nil, Host) # Set FromName & FromHost for sys.Dial
# Forward call to the bindings (ATA / softphone)
# Change FromURI when forwarding to @local, or else Bria won't find contact in its phonebook!
callswitch("#{@user}@local[fu=#{@cid}]",45) unless (90..745) === @t.hour*100 + @t.min # reject incoming calls from 1:30a to 7:45a
@code, @reason = 480, "#{@user} is asleep" unless @code # if nothing else, must be the night hour
@code = 486 if @trunk =~ /IPCOMM/i ## *** temporary fix for IPCOMMS ***
end
# ************************** t o E N U M *******************************
INTERNATIONAL = /^(\+|00|09)/
LONGDISTANCE = /^(01|02)(\d{10})$/
MOBILE = /^(044|045)(\d{10})$/
LOCALCALL = /^\d{#{10-Area.length}}$/
def to_ENUM num
num.gsub!(/[^0-9*+]/,'') # Delete all fancy chars (only digits, '+' and '*' allowed)
# Check if the number begins with one of international prefixes:
# '+' - international format
# 00, 01 - direct/operator international dialing
num =~ INTERNATIONAL and return $' # if yes, remove prefix and return
case num # Special cases
when LOCALCALL # Local call
Country + Area + num # prefix it with country and area code
when MOBILE # Mobile number
Country + '1' + $2 # Country + '1' + national number
when LONGDISTANCE # Mexico long distance
Country + $2 # add country code
when /^\*/ # Voxalot voicemail, echotest & other special numbers
num # ... as is
else
rejectCall(603,"Wrong number: '#{num}', check & dial again")
end
end
# ****** E N D O F C O N F I G U R A T I O N S E C T I O N ******** #
# ************************** C A L L S W I T C H **********************
def callswitch(num,*args)
@timeout = args[0]
num.gsub!(/%(..)/) {$1.hex.chr} # Convert %hh into ASCII
@num = Speeddial[num] || num # If there is speed dial entry for it...
if @num =~ /@/ # If we already have URI, just dial and return
sys.Log("URI dialing: #@num")
dial(@num,*args)
else # Not URI
rexp = VSP.tab.keys.sort {|a,b| b.length <=> a.length}.map {|x| Regexp.escape(x)}.join('|')
if @num =~ /^(#{rexp})/ # If number starts with VSP selection prefix
@num = $'; @forcedRoute = VSP.tab[$1]
@noSafeGuards = (@forcedRoute.fmt =~ /Disable\s*Safe\s*Guards/i)
end
@num = to_ENUM(@num) # Convert to ENUM
rejectCall(503,"Number's empty") if @num.empty?
sys.Log("Number in ENUM format: #{@num}")
if @forcedRoute && !@noSafeGuards
route_to @forcedRoute, "Forced routing!", false # if forced with prefix, skip ENUM, safeguards & VSP selection
else
checkNum if defined?(EnableSafeguards) && !@noSafeGuards
selectVSP # Pick appropriate provider for the call
end
end # URI
end
# *************************** R O U T E _ T O ****************************
def route_to vsp, dest=nil, enum = EnumDB
enum.to_a.each do |db| # if enum enabled, look in all enum databases
if uri = (db.class == Hash)? db[@num] : sys.ENUMLookup("#{@num}.#{db}")
sys.Log("ENUM entry found: '#{uri}' in #{db.class == Hash ? 'local' : db} database")
dial(uri); break
end
end # ENUM not found or failed, call via regular VSP
return unless vsp # No VSP - do nothing
uri = vsp.fmt.gsub(/\s+/,'').gsub(/\$\{EXTEN(:([^:}]+)(:([^}]+))?)?\}/) {@num[$2.to_i,$4? $4.to_i : 100]}
dest &&= " (#{dest})"; with = vsp.name; with &&= " with #{with}"
sys.Log("Calling #{formatNum(@num)}#{dest}#{with}")
if vsp.is_gv?
vsp.repeat.times do |i|
@code, @reason = 200, "OK" # assume OK
sys.GoogleVoiceCall *vsp.getparams(uri, i + (vsp.rand ? @t.to_i : 0))
sys.Log("Google Voice Call failed!")
@code, @reason = 603, 'Service Unavailable'
end
else
vsp.repeat.times do
dial(uri, @timeout || vsp.tmo || 300) # Dial, global time-out overrides account
end
end
end
# ******************************* D I A L ********************************
def dial *args
@code, @reason = nil
sys.Dial *args # dial URI
status() # We shouldn't be here! Get error code...
sys.Log("Call failed: code #{@code}, #{@reason}")
end
# ***************************** S T A T U S ******************************
def status
begin
@code, @reason = 487, 'Cancelled by Sipsorcery'
sys.LastDialled.each do |ptr|
if ptr
ptr = ptr.TransactionFinalResponse
@code = ptr.StatusCode; @reason = ptr.ReasonPhrase; break if @code == 200
# sys.Log("#{ptr.ToString()}")
end
end
rescue
end
end
# ************************ r e j e c t C a l l ***************************
def rejectCall code, reason
@code = code; @reason = reason
sys.Respond code, reason
end
# **************************** C H E C K N U M **************************
def checkNum
return if @num.match(/^\D/) # skip if number doesn't begin with a digit
# Reject calls to not blessed countries and premium numbers
# (unless VSP was forced using #n dial prefix)
rejectCall(503,"Calls to code #{formatNum(@num).split(' ')[0]} not allowed") \
unless @num.match "^(#{Allowed_Country.join('|')})"
rejectCall(503,"Calls to '#{formatNum($&)}' not allowed") if @num.match \
'^(' + ExcludedPrefixes.map { |x| "(:?#{x.gsub(/\s*/,'')})" }.join('|') + ')'
end
# ********************** k e y s t o E N U M *************************
def keys_to_ENUM (table)
Hash[*table.keys.map! {|key| to_ENUM(key.dup)}.zip(table.values).flatten]
end
# ************************** g e t T I M E *******************************
def getTime
Time.now + ((Tz-6)*60*60) # Get current time and adjust to local. SS Server is in GMT-6
end
# ******************************* M A I N ********************************
begin
sys.Log("** Call from #{req.Header.From} to #{req.URI.User} **")
sys.ExtendScriptTimeout(15) # preventing long running dialscript time-out
@t = getTime()
sys.Log(@t.strftime('Local time: %c'))
EnumDB.map! {|x| x.class == Hash ? keys_to_ENUM(x) : x } # rebuild local ENUM table
if sys.In # If incoming call...
prs = req.URI.User.split('.') # parse User into chunks
@trunk = prs[-2] # get trunk name
@user = prs[-1] # called user name
@cid = req.Header.from.FromURI.User.to_s # Get caller ID
case @trunk.to_s.downcase
when 'gvny', 'gvsm' # called at Google Voice DID
@cid = ('1' + @cid) if @cid =~ /^[2-9]\d\d[2-9]\d{6}$/ # Prepend 10-digit numbers with "1" (US country code)
@cid.sub!(/^(\+|00|011)/,'') #remove int'l prefix (if present)
else # Mexican DID
@cid = (Country + @cid) if @cid =~ /^[2-9]\d{9}$/ # Prepend numbers with "52" (Mexico country code)
end
# Check CNAM first. If not found and US number, try to lookup caller's name in Whitepages
if !(@cname = keys_to_ENUM(CNAM)[@cid]) && @cid =~ /^1([2-9]\d\d[2-9]\d{6})$/ && defined?(WP_key)
url = "http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20xml%20where%20url%3D'http%3A%2F%2Fapi.whitepages.com%2Freverse_phone%2F1.0%2F%3Fphone%3D#{$1}%3Bapi_key%3D#{WP_key}'%20and%20itemPath%3D'wp.listings.listing'&format=json"
if js = sys.WebGet(url,4).to_s
@cname, dname, city, state = %w(businessname displayname city state).map {|x| js =~ /"#{x}":"([^"]+)"/; $1}
@cname ||= dname; @cname ||= "#{city}, #{state}" if city && state
end
end
sys.Log("Caller's number: '#{@cid}'"); sys.Log("Caller's name: '#{@cname}'") if @cname
incomingCall() # forward incoming call
else # Outbound call ...
# check if it's URI or phone number.
# If destination's host is in our domain, it's a phone call
num = req.URI.User.to_s; reqHost = req.URI.Host.to_s # Get User and Host
num << '@' << reqHost unless sys.GetCanonicalDomain(reqHost) # URI dialing unless host is in our domain
callswitch(num)
end
sys.Respond(@code,@reason) # Forward error code to ATA
rescue
# Gives a lot more details at what went wrong (borrowed from Myatus' dialplan)
sys.Log("** Error: " + $!) unless $!.to_s =~ /Thread was being aborted./
end
Don't forget to enter your GV login/password pairs as well as callback numbers.