Administrator
发布于 2026-11-02 / 3 阅读
0
0

Build a FreeSWITCH IVR with key routing and fallback handling | Practical 05

English
中文

Dial 7180 to reach a menu: 1 calls 7101, 2 calls 7102, 0 enters queue access number 7190, 9 repeats the top-level menu, and star ends the call. Invalid input and no-input limits also send callers to 7190. The SIP extensions, queue and inbound routes from episodes 01–04 remain in use.

This is a controlled LAN exercise. Public NAT is outside this episode. The configuration was checked against FreeSWITCH v1.10.12 vanilla templates and source, but has not been tested on a running FreeSWITCH instance. Numbers, menu names and prompt scripts are fictional lab choices.

Locate the menu and its dialplan entry

The ivr application in mod_dptools plays prompts and collects DTMF. Extensions 7101 and 7102 are SIP users. Number 7190 still invokes training@default through its existing rule; this menu creates no callcenter agents or tiers.

Check the actual paths and existing destinations in fs_cli:

eval $${conf_dir}
eval $${sounds_dir}
sofia status profile internal reg
callcenter_config queue list agents training@default

CONF below means the returned configuration directory; SOUNDS means the returned sounds directory. Verify registration, extension-to-extension calling and access to 7190 before adding the menu. Check that 7180 is unused and back up files before editing.

CONF/autoload_configs/ivr.conf.xml aggregates menu files. Keep the existing vanilla structure; if only the include is absent, insert it inside menus rather than nesting another configuration element.

<configuration name="ivr.conf" description="IVR menus">
  <menus>
    <!-- 以当前配置文件目录为起点,读入ivr_menus下的菜单 -->
    <X-PRE-PROCESS cmd="include" data="../ivr_menus/*.xml"/>
  </menus>
</configuration>

CONF/dialplan/default.xml must also include the default directory's XML files. One include chain exposes the menu definition; the other exposes the dialplan action that invokes it.

Prepare three local audio prompts

Create a training directory under SOUNDS. Supply mono PCM WAV files named ivr-main.wav, ivr-invalid.wav and ivr-bye.wav. The first announces all five choices; the second asks the caller to choose again after invalid input; the third says goodbye. An 8 kHz, 16-bit mono PCM file is a suitable asset for this exercise. Renaming an extension to .wav does not convert the encoding.

The FreeSWITCH service user needs directory traversal and file-read permission, and the instance needs a file module capable of reading WAV. No Chinese sound pack, phrase macro or TTS service is required. The XML preprocessor expands $${sounds_dir}; use the path returned by this instance, not an assumed installation path.

Create the menu definition

Save this as CONF/ivr_menus/05_training_main.xml. Keep the recorded instructions synchronized with the key bindings.

<include>
  <!-- 独立菜单名,供拨号计划中的ivr应用引用 -->
  <menu name="training_main_05"
      greet-long="$${sounds_dir}/training/ivr-main.wav"
      greet-short="$${sounds_dir}/training/ivr-main.wav"
      invalid-sound="$${sounds_dir}/training/ivr-invalid.wav"
      timeout="5000"
      inter-digit-timeout="2000"
      max-failures="2"
      max-timeouts="2"
      digit-len="1"
      exec-on-max-failures="transfer 7190 XML default"
      exec-on-max-timeouts="transfer 7190 XML default">
    <!-- 按1:重新进入default里的7101拨号规则 -->
    <entry action="menu-exec-app" digits="1"
           param="transfer 7101 XML default"/>
    <!-- 按2:同样调用现有7102规则,不在此复制bridge -->
    <entry action="menu-exec-app" digits="2"
           param="transfer 7102 XML default"/>
    <!-- 按0:交给7190,由已有规则执行callcenter -->
    <entry action="menu-exec-app" digits="0"
           param="transfer 7190 XML default"/>
    <!-- 按9:回到顶层菜单,本例只有一层 -->
    <entry action="menu-top" digits="9"/>
    <!-- 按星号:退出菜单,后面的拨号计划负责告别与挂机 -->
    <entry action="menu-exit" digits="*"/>
  </menu>
</include>

The name identifies the menu used by the ivr application; it is not a dialable number. greet-long supplies the initial prompt, while greet-short supplies later rounds. Both use the full prompt here; a production menu can use a separate shorter file. invalid-sound handles received but unmatched input. A no-input timeout follows a different counter path.

timeout="5000" is a no-input waiting value in milliseconds, or five seconds, not a five-second limit on the entire call. inter-digit-timeout="2000" controls the interval between digits in a multi-digit input. Single-key selections in this exercise can complete without waiting for more digits. Prompt playback, retries and transfers add time, so these values do not define a fixed total waiting duration.

max-failures="2" controls handling at the invalid-input limit; max-timeouts="2" controls handling at the no-input limit. Both exec-on-max attributes contain a dialplan application followed by arguments. Here they execute transfer to 7190 XML default. Zero is not a general-purpose unlimited-wait setting; both limits are explicitly positive.

digit-len="1" collects a one-digit choice. Each entry binds digits to an action and its param. menu-exec-app runs a dialplan application. transfer takes the destination, dialplan type XML and context, reusing existing routing rather than duplicating bridge or callcenter configuration. Queue fallback still depends on agent status, tiers and wrap-up eligibility.

Give 7180 a dialplan entry

Create CONF/dialplan/default/05_training_ivr.xml:

<include>
  <!-- 内部分机拨7180时进入本期菜单 -->
  <extension name="training-ivr-entry-05">
    <condition field="destination_number" expression="^7180$">
      <!-- 菜单播放前接听本通电话 -->
      <action application="answer"/>
      <!-- 给接听后的媒体留出200毫秒缓冲 -->
      <action application="sleep" data="200"/>
      <!-- 名称必须与菜单定义一致 -->
      <action application="ivr" data="training_main_05"/>
      <!-- 星号退出或菜单意外返回时,继续以下收尾 -->
      <action application="playback"
              data="$${sounds_dir}/training/ivr-bye.wav"/>
      <!-- 结束该通测试电话 -->
      <action application="hangup"/>
    </condition>
  </extension>
</include>

answer accepts the call before the menu. sleep uses milliseconds; 200 is an example media-settling delay. The ivr data value must match training_main_05. menu-exit on star exits the menu; the following playback and hangup provide explicit cleanup. It does not automatically transfer to an agent. A successful transfer changes the dialplan destination; the cleanup handles a normal menu exit or an unexpected return.

Use 7102 as the caller when testing key 1. Use 7101 as the caller for key 2, so the originating phone is not also the destination.

Add an inbound mapping to the previous episode

For the episode 04 lab, insert this fragment inside the include element of CONF/dialplan/public/04_training_inbound.xml, before training-inbound-unassigned. The fictional number 55507180 is not an assigned carrier DID.

<!-- 放入04_training_inbound.xml的include内,位于未分配号码规则之前 -->
<extension name="training-inbound-to-ivr-05">
  <condition field="destination_number" expression="^55507180$">
    <!-- 保存入口号码,与04期保持相同口径 -->
    <action application="set" data="inbound_did=${destination_number}"/>
    <!-- 只放行这个固定测试入口到菜单接入号 -->
    <action application="transfer" data="7180 XML default"/>
  </condition>
</extension>

Episode 04's ^55507[0-9]{3}$ rejection rule covers this lab range. If it comes first, it rejects 55507180 before the new mapping is reached. A new public file sorted after that rule may have the same problem. Keep the existing mappings for 55507101 and 55507190 and add only this fixed destination.

Reload and verify each branch

Run reloadxml in fs_cli and inspect XML errors. New calls can enter the updated configuration; calls already inside a menu do not automatically acquire the new menu definition.

Call 7180 from 7102, listen to the prompt and press 1. Extension 7101 should ring and support two-way speech after answer. Reverse the caller for key 2. Before queue tests, record both agents' current status, place the test caller On Break and leave the other test agent Available. Restore the original statuses afterwards.

callcenter_config agent set status agent7102 On Break
callcenter_config agent set status agent7101 Available

From 7102, press 0 and check training@default. In a separate call, press 8 in each of two input rounds to test invalid-input fallback. Make another call without pressing a key to test timeout fallback. Key 9 should repeat the top-level menu. Star should play the goodbye message and end the call. Test each path with a fresh call.

To exercise the public mapping, replace YOUR_DOMAIN with the user domain established in episode 01:

originate user/7102@YOUR_DOMAIN 55507180 XML public

7102 rings first; after answer, the call enters 55507180 in public. This validates local routing, not the carrier's inbound path.

Useful options beyond this minimal configuration

The demo template includes phrase prompts, tts-engine, tts-voice, confirm-macro, confirm-key and confirm-attempts. A phrase prompt needs its macro; TTS needs a working engine and voice. Copying flite/rms from a template installs neither. Confirmation options let callers approve collected digits and are omitted from this one-key menu.

For nested menus, menu-sub references another defined menu, menu-back returns one level, and menu-top returns to the top of the current invocation stack. This exercise has one level, so 9 repeats it. exit-sound supplies a menu-exit prompt, while transfer-sound is used before a transfer invoked through menu-exec-app. The example puts the goodbye playback in the dialplan; configuring both locations can produce duplicate announcements.

As a separate experiment, change digit-len to 4 and add this entry to allow direct dialing of 7101 or 7102:

<!-- 可选实验:配合菜单digit-len="4",只允许这两个四位分机 -->
<entry action="menu-exec-app" digits="/^(710[12])$/"
       param="transfer $1 XML default"/>

$1 is the captured extension, restricted to those two values. Existing one-key entries can remain, but collecting up to four digits can make a one-key choice wait for the inter-digit timeout. Test overlapping input, timing and fallback again. Explicitly define any additional allowed destination range. The default configuration in this episode remains digit-len="1".

Diagnose failures and roll back

If 7180 immediately ends, check whether the entry matched, whether the menu name is correct, whether ivr.conf includes it, and whether the audio is readable. Unable to find menu points to the name or include chain before it points to the called number.

If prompts are audible but keys do nothing, distinguish missing DTMF from received digits that fail to match. Compare the phone's DTMF mode, negotiated telephone-event and FreeSWITCH logs. Voice audio and digit events are different content. RFC 4733 defines event transport in RTP; the example assumes no fixed payload number and does not enable several simultaneous sending modes as a workaround.

If key 1 selects the destination but the phone does not ring, check the 7101 default rule, domain and registration. If the queue answers but no agent rings, inspect eligibility and the original 7190 rule. Slow one-key selection can follow a change to digit-len="4". A 404 at the public test entry can mean that episode 04's broader rejection rule runs first.

End test calls before rollback. Restore the backed-up public file, move the two new XML files out of their included directories, restore any parent include edits and run reloadxml. Restore agent statuses and retest extension calling and 7190. Remove audio assets only after confirming they are unused; the extensions and queue do not need rebuilding.

The next episode covers carrier gateways and outbound trunks, including registration-based and IP-authenticated connections.

Sources and applicability

Configuration was checked against the official v1.10.12 menu template, menu implementation and ivr application implementation, with the current IVR manual for supporting explanation. Checked on 2026-11-02; compare other versions with their installed templates and modules.


评论