...they have allocated hundreds of 1-403-222-xxxx phone numbers
Oh, it explains everything, thank you!
...they have allocated hundreds of 1-403-222-xxxx phone numbers
# 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 *************** #
Area = '026' # my area code, this will be added to 7-digit dialouts
Tz = +2 # my time zone (GMT format, e.g. Eastern = -5, Central = -6)
# Speed dial entries. Format: "key" => "number"
Speeddial = {
'0' => '31261234567@sip1.budgetphone.nl', # Home
'303' => '303@sip.blueface.ie', # Blueface speaking clock (Ireland time)
'266' => '4153767253@podlinez.net', # CNN Headlines (266 = "CNN")
'73' => '31261234765@sip1.budgetphone.nl' # Piet
}
# CNAM table: number in ENUM format => caller's name
CNAM = {
'31261234567' => 'Pa en ma'
# '(215) 333-2211' => 'Bratty kid',
}
# Uncomment next line and insert your White Pages API key, if you have it
# WP_key = 'Insert your White Pages key here' # 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
'44 (9 | 55 | 70 | 84 | 87)', # UK Premium
'49 (1 [^567] | 900)', # Germany Premium
'39 (1 | 84 | 89)', # Italy Premium
'420 90', # Czech Premium
'32 (70 | 90\d)' # Belgium Premium
]
# Yet another safeguard, list of blessed country codes
Allowed_Country = %w{
1 31 32 33 34 35[2-3] 41 43 44 48 49
}
# My own ENUM database
MyENUM = {
'0261234567' => '31261234567@sip1.budgetphone.nl', # eigen nummer
'0261234765' => '31261234765@sip1.budgetphone.nl' # Piet
}
# Enum database list
EnumDB = [
MyENUM, # look in MyENUM first
'e164.org',
'e164.info',
'e164.arpa',
'e164.televolution.net',
'enum.org'
]
# Serviced domains, must be in lowercase!
Domains = ['sipsorcery.com','sip.sipsorcery.com','sip1.sipsorcery.com','sip2.sipsorcery.com','69.59.142.213']
Host = 'sipsorcery.com' # Replaces "host" on incoming calls
# Google Voice accounts
#
# Myacnt1 = { :usr => 'myname1', :pwd => 'password1' }
# Myacnt2 = { :usr => 'myname2', :pwd => 'password2' }
#
# SJaccount = [
# Myacnt1 + { :cb => '(206) 424-1234' },
# Myacnt1 + { :cb => '(253) 753-4321' },
# Myacnt1 + { :cb => '(401) 648-1234' },
# ]
#
# SFaccount = [
# Myacnt2 + { :cb => '(603) 413-1234' },
# Myacnt2 + { :cb => '(747) 493-4321' },
# ]
# SIP accounts
12voip = VSP.new '#0', '${EXTEN}@12voip', '12voip (default)'
budgetphone = VSP.new '#1', '${EXTEN}@budgetphone', 'budgetphone'
# SanJose = GV.new '*#1', nil, 'GV-408', :account => SJaccount, :repeat => 3, :rand => true
# Frisco = GV.new '*#2', nil, 'GV-415', :account => SFaccount, :repeat => 2
# Chicago = GV.new '*#3', nil, 'GV-773', :usr => 'myname3', :pwd => 'password3',
# :cb => '(305) 760-1234', :repeat => 2
# VSP.new '**', '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", false
#
# when /^883/ # iNUM
# route_to INUM, "iNUM"
#
# 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 SanJose, "USA toll-free", false # call from GV/San Jose, disable ENUM search
# when "415" # San Francisco patch, I have GV number there
# route_to Frisco, "San Francisco"
# when "312", "773", "872" # Chicago, I have GV number there
# route_to Chicago, "Chicago"
# else
# route_to SanJose # all other destinations within US & Canada
# end
#
# route_to Ribbit, nil, false # If GV call failed, try one more time with Ribbit
#
case @num
when /^34/
route_to 12voip, "Spanje"
else
rejectCall(603,"Number's too short, check & dial again") if @num.length < 10
route_to 12voip
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 (30..745) === @t.hour*100 + @t.min # reject incoming calls from 0: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 *******************************
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 - European style international prefix (00)
# 011 - US style international prefix (011)
num =~ /^(\+|00|011)/ and return $' # if yes, remove prefix and return
case num # Special cases
when /^[2-9]\d{6}$/ # Local call, 7-digit number
'1' + Area + num # prefix it with country and area code
when /^[01]?([2-9]\d{9})/ # US number with or without "1" country code
'1' + $1 # add country code and truncate number to 10-digit
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!(/%([0-9A-F]{2})/) {$1.to_i(16).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)
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+8)*60*60) # Get current time and adjust to local. SS Server is in GMT-8
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...
@cid = req.Header.from.FromURI.User.to_s # Get caller ID
# Prepend 10-digit numbers with "1" (US country code) and remove int'l prefix (if present)
@cid = ('1' + @cid) if @cid =~ /^[2-9]\d\d[2-9]\d{6}$/
@cid.sub!(/^(\+|00|011)/,'') # Remove international prefixes, if any
prs = req.URI.User.split('.') # parse User into chunks
@trunk = prs[-2] # get trunk name
@user = prs[-1] # called user name
# 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
host = reqHost.downcase.slice(/[^:]+/) # Convert to lowercase and delete optional ":port"
num << '@' << reqHost unless Domains.include?(host) # URI dialing unless host is in our domain list
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./
endTheMountainMan wrote:When using some of the providers such a sipgate, IPComms IPKall with this dialplan, is there an easy way to change the order of the preferred carrier?
Also, when using more than one GV account, can I manually force the call to a second GV account from one the the main speed dials, a speed dial that normally uses the default GC number?
TheMountainMan wrote:Can you explain what the names GV-408' GV-415' actually do in the section below? I don't see those names anyplace else in your dialplan. I know they are area codes .
TheMountainMan wrote:I have 2 GV numbers, one of which is only used to call a few numbers. After I make what I think are the correct changes to this cool dialplan I always get a busy signal after about 5 seconds when dialing. I think I am getting close, but I am obviously missing something to get it all working.
All I need is to have my main GV number used for just about all calls, and a select few other calls using the other GV number.
MainGV = GV.new nil, nil, ‘Google Voice / Main’,
:usr => ‘myname’, # Google login name
:pwd => ‘password’, # Google password
:cb => ‘(403) 234-5678’ # Callback number GV.new '*1', nil, ‘Google Voice / Occasional’,
:usr => ‘myname1’, # Google login name
:pwd => ‘password1’, # Google password
:cb => ‘(403) 234-7722’ # Callback number when /(^1([2-9]\d\d)[2-9]\d{6})/ # North America
@num = $1 # Truncate to 11 digits
route_to MainGVUsers browsing this forum: No registered users and 1 guest