Flexible table-controlled dialplan, optimized for US, GV

Catalog of dial plans
trav
Posts: 120
Joined: Tue Sep 08, 2009 8:34 pm

Post by trav » Tue Feb 23, 2010 5:17 am

my tweak on this dialplan is to try a more private version of e164, where i know the URI of people i want to call, but don't want to register them or myself on e164. i don't think there's enough protections from spammers from getting hold of that info (maybe not now, but eventually) and calling people up at will.

so to setup, i add the number to be recognized (needs to be fully formatted, can't leave out 1's or area codes, etc) and the URI to be dialed under the speedial section like:

Code: Select all

'12345678901' => '12345@in.callcentric.com', # Home
then i basically run the speed dial check/substitution one more time in the callswitch function right before the E164 check:

Code: Select all

      if urinumber = Speeddial[@num]      # Check if phone=known URI user
        sys.Log("*** OUTGOING URI dialing: #{@num} => #{urinumber} ***") # Assume URI dialing
        sys.Dial(urinumber)                 # dial selected number or URI
        status()                            # If this is not what you want, add "return"
        sys.Log("Call to #{urinumber} FAILED (#{@reason}), will call again via regular VoIP provider")
      end
####### New code above, Old code below #################
      # Invoke selectVSP prior to ENUM lookup just in case we need to modify @num
      
      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
based on where i placed the new code, i had some problems with infinite looping, but i think i'm ok with where it is now.

MikeTelis
Posts: 1582
Joined: Wed Jul 30, 2008 6:48 am

Post by MikeTelis » Tue Feb 23, 2010 6:21 am

I don't see any reason for modifications of the code. You could have achieved the same goal by adding a Speeddial entries like this:

'1' => '12345@in.callcentric.com|12345678901@provider',

If you dial 1 then existing (unmodified) code would try calling 12345@in.callcentric.com and failover to 12345678901@provider if their ATA / softphone is offline.

trav
Posts: 120
Joined: Tue Sep 08, 2009 8:34 pm

Post by trav » Tue Feb 23, 2010 1:09 pm

how about if other people use the dialplan, and doesn't know (or forgets) which speedial entry is associated with which number?

or on my cell/voip phone, say i missed a call from my dad (who has his own ATA). i have my dad listed by his full number, so when he calls, it shows up on my display by his name. from the missed call log i press the dial button.

this way i can catch all cases, and make sure those are dialed directly as URI's instead of being put through google voice or a outgoing pay plan. i am still learning my way through ruby and SS as we go, so please let me know if there's a more straightforward way to cover the above cases.

MikeTelis
Posts: 1582
Joined: Wed Jul 30, 2008 6:48 am

Post by MikeTelis » Tue Feb 23, 2010 4:41 pm

I think you have a valid point, but mixing up speed dial entries and your private ENUM entries looks odd, to say the least. It's appropriate to keep a separate hash for ENUM. Here is how:

Code: Select all

# Enum list from sipbroker

EnumDB = [
 'local',                             # look in MyENUM first
 'e164.org',
 'e164.info',
 'e164.arpa',
 'e164.televolution.net',
 'enum.org',
]

MyENUM = {
 '12345678901' => '12345@in.callcentric.com', # Home
}

# ... code ... code ... code

      EnumDB.each do |db|   # Look in all enum databases
        if enumuri = (db == 'local')? MyENUM[@num] : sys.ENUMLookup("#{@num}.#{db}")
          sys.Log("ENUM entry found: '#{enumuri}' in #{db} database")
          sys.Dial(enumuri) # If yes, call that URI
          status()
          sys.Log("Call to #{enumuri} failed (#{@reason})")
        end
      end                   # ENUM not found or failed, call via regular VSP

trav
Posts: 120
Joined: Tue Sep 08, 2009 8:34 pm

Post by trav » Wed Feb 24, 2010 1:28 am

thanks, will try that piece of code out later

beaver
Posts: 241
Joined: Tue Feb 09, 2010 5:32 am
Location: Beaverton USA (PST, GMT - 8)

Post by beaver » Sun Mar 07, 2010 9:21 pm

I have been using the simple "one Google Voice Dial Plan" developed by Mike. It runs fine.
This Flexible dialplan covers most issues in normal phone calls, No-Disturb hours, toll-free calls, VM if ATA down…, very interesting.

My free DID providers are sipgate, ipcomms and ipkall. My idea is to randomly select one of them for outbound calls using GV callback. For toll-free outbound calls, always use sipgate (unless it's down).
Last edited by beaver on Tue May 11, 2010 3:43 am, edited 2 times in total.

mavekal
Posts: 2
Joined: Fri Feb 26, 2010 7:02 pm

international dialling

Post by mavekal » Tue Mar 09, 2010 3:52 am

i cannot connect to india with this dial plan.mike can you help
i usually dial 011-91-area code-phone number.
thanks in advance

asloan5
Posts: 7
Joined: Thu Mar 11, 2010 9:37 pm

Post by asloan5 » Fri Mar 12, 2010 3:26 am

dont flame me for asking .... Does anyone have a working version of this for Google voice Sip sorcery with sipgate and not gizmo5 ?
I've tried adjusting little things and cannot dial out. I believe that sipgate uses 10 digit calling not 11 like gizmo5. I'm not a programmer Thanks in advance. here is below what little I modified.

Code: Select all

 #Ruby 

Area = '386'                       # My area code 
Tz   = -6                          # Time zone (GMT-8) 

# Speed dial entries. Format: "key" => "number" 

Speeddial = { 
  '0'   =>       '234-5678',       # Home 
  '1'   => '1 408 234-1212',       # Mom 
  '411' => '411@Gizmo',            # Directory 
  '911' => '911@F9',               # Emergency 
  '303' => '303@sip.blueface.ie',  # Blueface speaking clock 
  '612' => '612@fwd.pulver.com',   # 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 @ F9',                # Future-nine default route 
  '2' => '02 @ F9',                # Future-nine grey route 
  '3' => '03 @ F9',                # Future-nine white route 
  '4' => '04 @ F9',                # Future-nine premium route 
  '5' => '@ Gizmo',                # Gizmo5 
} 

# ********************  G o o g l e V o i c e  ***************************** 
# !!!!!!!!!!!!    Make sure to enter your credentials below    !!!!!!!!!!!!! 

def googleVoice 
  sys.GoogleVoiceCall("XXXXX@gmail.com","GVPASSWD","4155551212","#{req.URI.User}",".*",1)
  
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 F9") 
          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./ 

asloan5
Posts: 7
Joined: Thu Mar 11, 2010 9:37 pm

Post by asloan5 » Sat Mar 13, 2010 11:32 am

never mind I found a modified version for my needs here
http://www.mysipswitch.com/forum/viewtopic.php?t=1799
thankyou anyway

beaver
Posts: 241
Joined: Tue Feb 09, 2010 5:32 am
Location: Beaverton USA (PST, GMT - 8)

Post by beaver » Sun Mar 14, 2010 4:59 am

asloan5 wrote:dont flame me for asking .... Does anyone have a working version of this for Google voice Sip sorcery with sipgate and not gizmo5 ?
I've tried adjusting little things and cannot dial out. I believe that sipgate uses 10 digit calling not 11 like gizmo5. I'm not a programmer Thanks in advance.
I see you are still asking help in another thread.

Here is my Outbound dial plan. It's a mix of "google-voice-sipsorcery-dialplans" on Google.com and Dial Plan on the 1st page by Mike. it works fine for me. You just need to change Area code to yours.

Code: Select all

Area = '503'

# Google Voice accounts: login, password, callback number, match, phone type, callback timeout

class Hash; alias :+ :merge; end

MyGV1 = { :usr   => 'user1@gmail.com', :pwd   => 'abcd',}
MyGV2 = { :usr   => 'user2@gmail.com', :pwd   => 'wxyz',}

CB_num1 = '1321xxxxxxx' #callback to IPCOMMS
CB_num2 = '1503xxxxxxx' #callback to Sipgate
CB_num3 = '1360717xxxx' #callback to IPKall

GVaccounts = [
   MyGV1 + { :cb => CB_num1 },
   MyGV1 + { :cb => CB_num2 },
   MyGV1 + { :cb => CB_num3 },
   MyGV2 + { :cb => CB_num1 },
   MyGV2 + { :cb => CB_num2 },
   MyGV2 + { :cb => CB_num3 },
]

def gvcall(num,acnt)
  a = {:num => num, :type => 'Home', :tmo => 10, :match => '.*'} # init with num & default values
  a.update(GVaccounts[acnt % GVaccounts.length])                # add other params
  a[:type] = {'Home' => 1, 'Mobile' => 2, 'Work' => 3, 'Gizmo' => 7}[a[:type]] # Encode type
  sys.GoogleVoiceCall *a.values_at(:usr, :pwd, :cb, :num, :match, :type, :tmo) # timeout 10 sec.
end

SPEED_DIAL = {                         # my speed dial numbers
# '1'   => '19879879876',              # Mom
# '123' => '12345678901',              # Work
  '411' => '8004664411',               # 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]")                    
}

begin
    sys.Log("** Call from #{req.Header.From.to_s} to #{req.URI.User} **")

  if sys.Out    # if outbound call
    num = req.URI.User.to_s           # Get a string copy of the number to dial
    num = SPEED_DIAL[num] || num      # Substitute with speed dial entry, if any

    case num
      when /@/ then sys.Dial num      # URI dialing
      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
      else sys.Respond(603,'Wrong number, check & dial again')
    end

    sys.Log("Calling #{num} via Google Voice")
    r = Time.now.to_i
    2.times { |x| gvcall num,r+x }

  else          # sys.Out
    sys.Dial("#{sys.Username}@local")
  end

rescue
  sys.Log("** Error: " + $!) unless $!.to_s =~ /Thread was being aborted./
end

Post Reply