FreeSWITCH 简单图形化界面54 - 拨号应用att_xfer协商转移使用

FreeSWITCH 简单图形化界面54 - 拨号应用att_xfer协商转移使用

  • [00、 一个fs的web配置界面预览](#00、 一个fs的web配置界面预览)
  • [01、 Web 配置界面预览](#01、 Web 配置界面预览)
  • [02、 Web界面安装参考](#02、 Web界面安装参考)
  • [1.att_xfer 简介](#1.att_xfer 简介)
  • [2. 用法](#2. 用法)
  • [3. 可配置 DTMF 按键(通道变量)](#3. 可配置 DTMF 按键(通道变量))
  • [4. 示例](#4. 示例)
    • [示例 1:拨号计划实现 attended 转接](#示例 1:拨号计划实现 attended 转接)
    • [示例 2:带自定义按键的完整配置](#示例 2:带自定义按键的完整配置)
    • [示例 3:API/事件套接字调用](#示例 3:API/事件套接字调用)
    • [示例 4:使用lua脚本进行协商转移](#示例 4:使用lua脚本进行协商转移)
  • [5. 按键行为总结](#5. 按键行为总结)
  • [6. 注意事项](#6. 注意事项)

00、 一个fs的web配置界面预览

如果您想通过图形化界面管理 FreeSWITCH 的场景,可直接访问以下 Web 配置界面。

01、 Web 配置界面预览

界面地址 备用地址 登录信息
http://www.fspbx.cn/ http://myfs.f3322.net:8020/ 用户名:admin密码:admin

02、 Web界面安装参考

若需自行部署 FreeSWITCH 图形化界面,包含 Docker、脚本、ISO 镜像三种安装方式,适配不同系统环境:

  1. Docker 安装:快速部署;
  2. 脚本安装:适配 Almalinux、RockyLinux、Debian、Ubuntu 、欧拉等系统,提供一键安装命令,安装后需重启初始化;
  3. ISO 镜像安装:基于 Almalinux制作的镜像,刻录到 U 盘或光盘后可自动安装,适合全新服务器部署。

1.att_xfer 简介

att_xfer = Attended Transfer(专人值守转接/协商转接

用于执行"有人值守转接"(Attended Transfer)。该功能允许通话的一方在转接前先与第三方通话,确认无误后,再将原始通话的另一方与用户C桥接。

典型使用场景

A打电话找 C,B是前台/秘书,B 接到 A的来电后,进行协商转移:

A 来电 → 被 B 接听

B 启动 att_xfer 功能,呼叫 C

A被保持(Hold),B与 C 进行私密通话

C 同意接听 A 的电话 → B 挂机 → A 与 C 建立通话

C 拒绝接听 A 的电话 → C 挂机 → A 返回与 B 的通话

B 在私密通话中按功能键可执行不同操作

2. 用法

xml 复制代码
att_xfer <channel_url>

3. 可配置 DTMF 按键(通道变量)

通道变量 默认按键 功能说明
attxfer_cancel_key # 取消转接,挂断 C 端,回到原通话 A
attxfer_hangup_key * 挂断坐席,直接桥接 A ↔ C(完成转接)
attxfer_conf_key 0 转为三方通话,再挂断坐席完成转接

4. 示例

示例 1:拨号计划实现 attended 转接

  1. 定义转接扩展:
xml 复制代码
<extension name="att_xfer">
  <condition field="destination_number" expression="^86$">
    <action application="read" data="3 4 sounds/getdigits.wav attxfer_callthis 30000 #"/>
    <action application="att_xfer" data="sofia/default/${attxfer_callthis}"/>
  </condition>
</extension>
  1. 绑定到 DTMF 快捷键(通话中按 *3 触发):
xml 复制代码
<action application="bind_meta_app" data="3 a a execute_extension::86 XML features"/>

示例 2:带自定义按键的完整配置

主叫拨号计划:

xml 复制代码
<extension name="local_number">
  <condition field="destination_number" expression="^(\d{3})$">
    <action application="set" data="dialed_extension=$1"/>
    <action application="export" data="dialed_extension=$1"/>
    <action application="bind_meta_app" data="1 b s execute_extension::attended_xfer XML features"/>
    <action application="set" data="transfer_ringback=$${hold_music}"/>
    <action application="set" data="call_timeout=10"/>
    <action application="set" data="hangup_after_bridge=true"/>
    <action application="bridge" data="user/${dialed_extension}@${domain_name}"/>
  </condition>
</extension>

features.xml 中的转接逻辑:

xml 复制代码
<extension name="attended_xfer">
  <condition field="destination_number" expression="^attended_xfer$">
    <action application="set" data="continue_on_fail=true"/>
    <action application="read" data="3 4 ivr/ivr-enter_ext.wav attxfer_callthis 30000 #"/>
    <action application="set" data="attxfer_cancel_key=#"/>
    <action application="set" data="attxfer_hangup_key=*"/>
    <action application="set" data="attxfer_conf_key=0"/>
    <action application="att_xfer" data="user/${attxfer_callthis}@${domain_name}"/>
  </condition>
</extension>

示例 3:API/事件套接字调用

复制代码
att_xfer {attxfer_conf_key=3,attxfer_hangup_key=2,attxfer_cancel_key=1}user/1000

示例 4:使用lua脚本进行协商转移

lua 复制代码
#!/usr/bin/env lua

-- 2024年7月2日
-- 功能号码
-- 呼叫协商转移
------------------打印日志-------------------
-- CONSOLE : console loglevel 0
-- ALERT   : console loglevel 1
-- CRIT    : console loglevel 2
-- ERR     : console loglevel 3
-- WARNING : console loglevel 4
-- NOTICE  : console loglevel 5
-- INFO    : console loglevel 6
-- DEBUG   : console loglevel 7
local flag = true
local level = "NOTICE"
local log = function(session, str)
    if (flag == true and session:ready()) then
        session:consoleLog(level, str)
    end
end

if (session:ready()) then
    local caller_id_name = session:getVariable("caller_id_name")
    local caller_id_number = session:getVariable("caller_id_number")
    local destination_number = session:getVariable("destination_number")
    local domain = session:getVariable("domain")
    local context = session:getVariable("context")
    log(session, "#======= start handle-att-xfer.lua =======\r\n")
    log(session, string.format("# 主叫:%s,被叫:%s,开始协商转移.\r\n", caller_id_number, destination_number))

    -- 播放等待音
    session:execute("set", "transfer_ringback=$${hold_music}")
    -- 自定义取消转移、挂机、3方通话按键
    session:execute("set", "attxfer_cancel_key=1")
    session:execute("set", "attxfer_hangup_key=2")
    session:execute("set", "attxfer_conf_key=3")

    -- 获取用户输入(要转移到的号码,C号码)
    local digits = session:playAndGetDigits(1, 12, 3, 5000, "#", "default/att-xfer-start.gsm",
        "default/att-xfer-error.gsm", "\\d+")
    if (digits == '') then
        session:execute("playback", "default/blind-xfer-end.gsm")
    else
        -- 获取转发的分机的相关信息(限制转移的号码,只有被叫号码(C)是内部号码才能盲转,只能转到内部号码)
        fsapi = freeswitch.API();
        local is_local_number = fsapi:executeString(string.format("user_exists id %s %s", digits, domain));
        if (is_local_number == "true") then
            log(session, string.format("# 要转移的号码%s是内部号码,正在转移.\r\n", digits))
            session:execute("set", string.format("caller_id_name=%s",caller_id_name))
            session:execute("set", string.format("caller_id_number=%s",caller_id_number))
            session:execute("set", string.format("destination_number=%s",destination_number))
            -- session:execute("set", string.format("rdnis=%s",destination_number))
            session:execute("export", string.format("nolocal:caller_id_name=%s",caller_id_name))
            session:execute("export", string.format("nolocal:caller_id_number=%s",caller_id_number))
            session:execute("export", string.format("nolocal:destination_number=%s",digits))
            session:execute("set", "process_cdr=true")
            -- 这里执行att_xfer协商转移应用
            session:execute("att_xfer", string.format("user/%s@%s", digits, domain))
        else
            log(session, string.format("# 要转移的号码%s不是内部号码,无法转移.\r\n", digits))
            session:sleep(500)
            session:execute("playback", "default/user-cannot-connected-start.gsm")
            for i = 1, string.len(digits) do
                local digit = string.sub(digits, i, i)
                session:execute("playback", string.format("default/digits/%s.wav", digit))
            end
            session:execute("playback", "default/user-cannot-connected-end.gsm")
            session:execute("playback", "default/blind-xfer-end.gsm")
        end
    end
    log(session, "#=======  end handle-att-xfer.lua  =======\r\n")
    -- return
end

5. 按键行为总结

  • 接通 C 端后按 0 → 进入三方会议
  • 接通 C 端后按 # → 取消转接,回到 A
  • 接通 C 端后按 * → 坐席退出,A 与 C 直接通话
  • C 端未接通时按 # → 取消 C 端呼叫,回到 A

6. 注意事项

  • att_xfer 不支持 loopback 通道:桥接到 C 端时会阻塞;
  • 如果 C 端挂断,会自动回到 A 端;
  • loopback 本身会通过挂断来切换通道,会被 att_xfer 识别并切回 A。

💗 2026年

🐂 祝君成功,好运连连,牛气冲天

相关推荐
wMoqi16 天前
freeswitch接入E1/T1数据中继语音网关配置
freeswitch·数字中继网关
代码浪人1 个月前
Freeswitch-基础配置-wss-api简化-acl
freeswitch
贾宝玉的玉宝贾1 个月前
FreeSWITCH 简单图形化界面52 - 拨号应用 Answer 介绍
python·django·voip·freeswitch·sip·ippbx·jssip
小毅&Nora3 个月前
【人工智能】【AI外呼】 ⑤ FreeSWITCH 深度解析:原理、安装、在智能外呼中的核心地位与未来演进
人工智能·freeswitch·ai外呼
Mike_Zhang3 个月前
FreeSWITCH开启silk编码及转码
voip·freeswitch
加油20193 个月前
音视频处理(四):一文讲清楚VoIP语音通话SIP和RTP协议
音视频·pcm·voip·sip·rtp·g.711·语音通话
Mike_Zhang3 个月前
FreeSWITCH使用mod_fail2ban模块来提升安全
voip·freeswitch
Mike_Zhang3 个月前
FreeSWITCH使用RNNoise进行实时通话降噪
voip·freeswitch·音频技术