Skip to content

Instantly share code, notes, and snippets.

@digitalknk
Created January 24, 2010 09:03
Show Gist options
  • Save digitalknk/285112 to your computer and use it in GitHub Desktop.
Save digitalknk/285112 to your computer and use it in GitHub Desktop.
#Ruby
Area = '555' # My area code
Tz = -8 # Time zone (GMT-8)
# Speed dial entries. Format: "key" => "number"
Speeddial = {
'0' => '555-5555', # Home
'1' => '1 555 555-5555', # Another Number
'411' => '1 800 466 4411', # Google 411
'911' => '911@F9', # Emergency - Not really using this so it's not setup
'303' => '[email protected]', # Blueface speaking clock
'612' => '[email protected]', # FWD speaking clock
}
# Serviced domains, must be in lowercase!
Domains = ['sipsorcery.com','174.129.236.7']
# Excluded Prefixes. Provides a safeguard against accidentally calling premium
# numbers. Accepts both strings and patterns, spaces ignored
ExcludedPrefixes = [
' 1 (900 | 976 | 809)', # USA Premium
'44 (9 | 55 | 87 (0|1))', # UK Premium
'44 84 (4|5)', # UK Local Premium
'44 70', # UK Personal Premium
'43 (8|9)', # Austria Premium
'32 (7|90)', # Belgium Premium
'45 (1 | 50 (1|2|3) | 70 (1|2))', # Denmark Premium
'45 (8|9) 0', # Denmark Premium (...)
'33 (7|9)', # France Premium
'49 (1 | 900)', # Germany Premium
'39 [^3]', # Italy Premium (...)
'31 (14 | 6 (3|8|9) | 8 | 9)', # Netherlands Premium (...)
'48 (39 | (2|7|8) 0)', # Poland Premium
'46 9 (00 | 39 | 44)', # Sweden Premium
'41 90 (0|1|6)', # Switzerland Premium
]
# Providers: "key" => "template". Template format: prefix@Provider
VSPtab = {
'0' => '00 @ Freecall', # Future-nine default route
'2' => '02 @ F9', # Future-nine grey route
'3' => '03 @ Freecall', # Future-nine white route
'4' => '04 @ Freecall', # Future-nine premium route
'5' => '@ Gizmo5', # Gizmo5
}
# ******************** G o o g l e V o i c e *****************************
# !!!!!!!!!!!! Make sure to enter your credentials below !!!!!!!!!!!!!
def googleVoice
sys.GoogleVoiceCall('[email protected]','myPassword:','myCallBackNumber',@num,'.*',7) # Typically would use a Free DID pointing to a sip account or your Gizmo5 747 account number as a callback number.
end
# ******************** s e l e c t V S P *******************************
def selectVSP # VoIP provider selection
# Reject calls to premium numbers unless VSP was forced
ExcludedPrefixes.each { |p| p.gsub!(/\s*/,''); sys.Respond(503,"Calls to #{$1}* not allowed") if @num =~ /^(#{p})/ }
case @num
when /^1([2-9]\d\d)/ # North America
case $1 # check area code
when "808", "907", "867" # AK, HI, Canada Yukon
route(4,"Destination - Alaska, Hawaii, Yukon")
when /(800|866|877|888|747)/ # Toll-free and 1-747 calling
route(5,"Destination - US toll-free or G5")
else
googleVoice
sys.Log("GoogleVoiceCall failed, routing thru Freecall")
route(4,"Destination - North America")
end
# when /^972(5|6)/ # Israel mobile
# route(3,"Destination - Israel mobile")
# else
# route(0,"Default route applied")
end
end
# ************************** C A L L S W I T C H **********************
def callswitch(num,*args)
route # Initialize vars
@num = num unless @num = Speeddial[num] # If there is speed dial entry for it...
@l = "URI dialing: #{@num}" # Assume URI dialing
unless @num =~ /@/ # If we already have URI, skip all number processing
@num.gsub!(/%(..)/) {$1.to_i(16).chr} # Convert %hh into ASCII
if @num =~ /^#(.)(.*)/ # If number starts with '#'
@p = $1; @num = $2 # next char is VSP code
end
@num.gsub!(/[^0-9*+]/,'') # Delete all fancy chars (only digits, '+' and '*' allowed)
# sub! below removes prefixes:
# '+' - international format
# 00 - European style international prefix (00)
# 011 - US style international prefix (011)
unless @num.sub!(/^(\+|00|011)/,'') # If no international prefix, process special cases below
case @num
when /^[2-9]\d{6}$/ # Local call, 7-digit number
@num = "1#{Area}#{@num}" # prefix it with country and area code
when /^[01]?([2-9]\d{9})/ # US number with or without "1" country code
@num = '1' + $1 # add country code and truncate number to 10-digit
when /^\*/ # Voxalot voicemail, echotest & other special numbers
else
sys.Respond(603,'Wrong number, check & dial again')
end
end
sys.Log("Number in ENUM format: #{@num}")
@l = "Forced routing to provider #{@p}, template '#{VSPtab[@p]}'" # Assume user explicitly selected VSP
if @p.empty? # Automatic VSP selection?
# Invoke selectVSP prior to ENUM lookup just in case we need to modify @num
route # re-initialize variables
selectVSP # Pick appropriate provider for the call
if enumuri = sys.ENUMLookup("+#{@num}.e164.org") # Check if NAPTR exists for the number
sys.Log("ENUM entry found: '#{enumuri}'") # If yes, call that URI
sys.Dial(enumuri) # if call failed, call via regular VSP.
status() # If this is not what you want, add "return"
sys.Log("Call to #{enumuri} failed (#{@reason}), will call again via regular VoIP provider")
end
end # @p.empty
end # URI
dial(*args) # dial selected number or URI
end
# ******************************* D I A L ********************************
def dial(*args)
sys.Log(@l) unless @l.empty? # for the record :)
if tpl=VSPtab[@p.to_s] # if provider is in our table
tpl.gsub!(/\s*/,'') # Remove spaces
@num = tpl.gsub(/@/,@num+'@') # Insert number before '@'
end
sys.Dial(@num,*args) # Dial
status() # We shouldn't be here! Get error code...
sys.Log("Call failed: code #{@code}, #{@reason}")
end
# ****************************** R O U T E *******************************
def route(p='', l='')
@p = p; @l = l
end
# ***************************** S T A T U S ******************************
def status
if (ptr = sys.LastDialled[0]).nil?
@code = 487; @reason = 'Cancelled by Sipsorcery'
else
ptr = ptr.TransactionFinalResponse
@code = ptr.StatusCode; @reason = ptr.ReasonPhrase
# sys.Log("#{ptr.ToString()}")
end
end
# ******************************* M A I N ********************************
begin
sys.Log("** Call from #{req.Header.From.ToString()} to #{req.URI.User} **")
t = Time.now + ((Tz+8)*60*60) # Get current time and adjust to local. SS Server is in GMT-8
sys.Log(t.strftime('Local time: %c'))
if sys.In # If incoming call...
name = req.Header.from.FromURI.User.to_s # Get caller ID
# Prepend 10-digit numbers with "1" (US country code) and remove 011 prefix (if present)
name = ('1' + name) if name =~ /^[2-9]\d\d[2-9]\d{6}$/
name.sub!(/^011/,'')
sys.Log("FromName: '#{name}'")
# Set FromName for sys.Dial. Change FromURI when forwarding to @local, or
# else Bria won't find contact in its phonebook!
sys.SetFromHeader(name, nil, nil)
if sys.IsAvailable() # If my ATA is registered
callswitch("#{sys.Username}@local[fu=#{name}]") # forward all incoming calls to it
elsif (8..23) === t.hour # else forward calls to my home
sys.Log("#{sys.Username} is not online, forwarding call to home number...")
callswitch("0",35) # Note that '0' is in my speed dial list
end
sys.Respond(480, "#{sys.Username} Not online") # switch to voice mail
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.find {|x| x == 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./
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment