Freeswitch: mod_callcenter

mod_callcenter - модуль очередей входящих вызовов.

Settings

  • odbc-dsn

Callcenter поддерживает ODBC вместо используемой по умолчанию внутренней БД SQLite.
В таблицах хранятся динамические данные вызывающих абонентов (members), агентов (agents) и уровней (tiers).
Пример из таблицы members (вызывающий абонент в очереди)

queuesystemuuidsession_uuidcid_numbercid_namesystem_epochjoined_epochrejoined_epochbridge_epochabandoned_epochbase_scoreskill_scoreserving_agentserving_systemstate
testcc@defaultsingle_box6f3cbada-ffd6-428d-aa61-ee783ddad25b414a9b82-c8fd-49f0-a97f-3d83d6f3671d+79219981138D.Khlevnoy1556203331155620333100000ring-all Trying

Пример из таблицы agents (Агент вызывается)

namesystemuuidtypecontactstatusstatemax_no_answerwrap_up_timereject_delay_timebusy_delay_timeno_answer_delay_timelast_bridge_startlast_bridge_endlast_offered_calllast_status_changeno_answer_countcalls_answeredtalk_timeready_timeexternal_calls_count
6666@defaultsingle_box callbackuser/6666@132.123.123.123AvailableReceiving310360015562033561556203356155620364315560921610411780

Пример параметра для Postgres:

<param name="odbc-dsn" value="pgsql://hostaddr=127.0.0.1 dbname=DB  user=USER password='PASS'"/> 

Таблицы создаются автоматически при загрузке модуля mod_callcenter

callcenter odbc tables

callcenter odbc tables

fscore=> \dt
          List of relations
 Schema |    Name    | Type  | Owner  
--------+------------+-------+--------
 public | agents     | table | fsuser
 public | members    | table | fsuser
 public | tiers      | table | fsuser
(6 rows)

fscore=> 
fscore=> \d agents
                        Table "public.agents"
        Column        |          Type           |     Modifiers      
----------------------+-------------------------+--------------------
 name                 | character varying(255)  | 
 system               | character varying(255)  | 
 uuid                 | character varying(255)  | 
 type                 | character varying(255)  | 
 contact              | character varying(1024) | 
 status               | character varying(255)  | 
 state                | character varying(255)  | 
 max_no_answer        | integer                 | not null default 0
 wrap_up_time         | integer                 | not null default 0
 reject_delay_time    | integer                 | not null default 0
 busy_delay_time      | integer                 | not null default 0
 no_answer_delay_time | integer                 | not null default 0
 last_bridge_start    | integer                 | not null default 0
 last_bridge_end      | integer                 | not null default 0
 last_offered_call    | integer                 | not null default 0
 last_status_change   | integer                 | not null default 0
 no_answer_count      | integer                 | not null default 0
 calls_answered       | integer                 | not null default 0
 talk_time            | integer                 | not null default 0
 ready_time           | integer                 | not null default 0
 external_calls_count | integer                 | not null default 0

fscore=> \d members
                              Table "public.members"
     Column      |          Type          |               Modifiers                
-----------------+------------------------+----------------------------------------
 queue           | character varying(255) | 
 system          | character varying(255) | 
 uuid            | character varying(255) | not null default ''::character varying
 session_uuid    | character varying(255) | not null default ''::character varying
 cid_number      | character varying(255) | 
 cid_name        | character varying(255) | 
 system_epoch    | integer                | not null default 0
 joined_epoch    | integer                | not null default 0
 rejoined_epoch  | integer                | not null default 0
 bridge_epoch    | integer                | not null default 0
 abandoned_epoch | integer                | not null default 0
 base_score      | integer                | not null default 0
 skill_score     | integer                | not null default 0
 serving_agent   | character varying(255) | 
 serving_system  | character varying(255) | 
 state           | character varying(255) | 

fscore=> \d tiers
                  Table "public.tiers"
  Column  |          Type          |     Modifiers      
----------+------------------------+--------------------
 queue    | character varying(255) | 
 agent    | character varying(255) | 
 state    | character varying(255) | 
 level    | integer                | not null default 1
 position | integer                | not null default 1

  • dbname

Устанавливает путь и имя к внутренней БД SQLite. ( для лучшей производительности рекомендуется помещать на ram disk или использовать ODBC). Если не задано, то будет создано в директории по умолчанию.

Agent options

  • name

Имя агента, в первую очередь обеспечивает логическую связь с соответствующим tiers.

  • type

На данный момент поддерживаются 2 типа callback и uuid-standby.

  • callback пытается вызвать агента на основании данных из поля contact
  • uuid-standby пытается соединиться с агентом напрямую используя его uuid.
  • contact

Строка для вызова агента, как в команде bridge: user/1000@default. Или, например, для verto: ${verto_contact(1000@default)}.
Перед строкой вызова в квадратных скобках можно задать переменные per-leg, например время вызова агента при серийной стратегии (round-robin): [leg_timeout=10]sofia/gateway/fs210/2668

  • status

Назначает статус агента. Смотри подробнее ниже>>

  • max-no-answer

Кол-во неотвеченных агентом вызовов, после которого ему будет автоматически присвоен статус On Break.

  • wrap-up-time

Пауза после завершения вызова для того чтобы оператор мог закончить работу (внести данные по звонку в базу, например), прежде чем, он будет возвращен в обслуживание очереди.

  • reject-delay-time

Если агент отклоняет вызов, выждать назначенное время, прежде чем пытаться вызвать его снова.

  • busy-delay-time

Если агент недоступен (DND) выждать указанное время, прежде чем пытаться вызвать его снова.

  • no-answer-delay-time

Если агент не ответил на вызов, выждать указанное время, прежде чем пытаться вызвать его снова.

  • reserve-agents

Если задано true, состояние (state) агента изменяется на Reserved, после предыдущего состояния Receiving вызовы снова будут поступать на него, только после того как состояние будет изменено. Это используется, если вы манипулируете состоянием агента извне, через API mod_callcenter. По умолчанию false.

  • truncate-agents-on-load

Если задано true, при загрузке модуля все агенты удаляются из очереди. По умолчанию false.

  • truncate-tiers-on-load

Если задано true, при загрузке модуля все правила (tiers) удаляются. По умолчанию false.

Queue options

  • strategy

Стратегия обзвона агентов в очереди смотрите подробнее ниже>>

  • moh-sound

Воспроизведение медиа вызывающему абоненту в ожидании ответа.
Вы можете использовать любой из типов воспроизведения поддерживаемый FreeSWITCH:

  1. прямое воспроизведение файла, например с бесконечной петлей.
  2. local srtream (local_stream://moh) или $${hold_music} заданное в //vars.xml//
  3. FreeSWITCH phrase system (phrase:my-special-phrase) (например, чтобы воспроизвести несколько сообщений друг за другом)
  4. tone stream т.е. КПВ, (tone_stream://${ru-ring};loops=-1)
  • record-template

Задает шаблон имени файла для записи вызова и путь к директории в локальной файловой системе например:

/var/spool/freeswitch/${strftime(%Y/%m/%d/%H-%M-%S)}_${destination_number}_${caller_id_number}_${uuid}.mp3
  • time-base-score

Может принимать значения queue или system (По умолчанию queue).
Если установлено system, ко времени проведенному вызывающим абонентом в очереди прибавляется время которое вызов мог провести в системе до попадания в очередь. Таким образом такой вызов может получить приоритет над другими вызовами уже находящимися в очереди. Если установлено queue, то все вызовы имеют равные условия и для позиции в очереди учитывается только время проведенное в ней.

  • tier-rules-apply

Может быть true или false.
Указывает применять или нет перечисленные далее tier-rule… для продвижения по уровням очереди.
Если установлено false используются все уровни без ожидания.

  • tier-rule-wait-second

Время ожидания в секундах до перехода абонента на следующий уровень. Умножается на номер уровня, если tier-rule-wait-multiply-level = true. Если же tier-rule-wait-multiply-level = false, тогда по истечении заданного времени все уровни становятся открытыми для вызовов в порядке перечисления и дополнительные критерии ожидания не действуют.

  • tier-rule-wait-multiply-level

Может быть true или false.
Если false то по истечении времени заданного в tier-rule-wait-second вызывающему абоненту становятся доступны все уровни в заданном порядке (level/position). Если значение true, то абонент ожидает время заданное в tier-rule-wait-second умноженное на номер уровня до перехода на следующий уровень.

  • tier-rule-no-agent-no-wait

Может быть true или false.
Если false абонент может перескакивать уровни без доступных агентов. Иначе придется ждать по всем правилам. Агенты должны иметь статус Logged Out, чтобы считаться недоступными.

  • discard-abandoned-after

Время в секундах (таймаут), по истечению которого вызывающий абонент будет удален из очереди (abandoned).
Но если abandoned-resume-allowed = true, то он будет возвращен в очередь на предыдущую позицию.

  • abandoned-resume-allowed

Может быть true или false.
true позволяет вернуться в очередь на прежнюю позицию если он был удален из нее по истечению таймаута (discard-abandoned-after) In order to maintain their position in the queue, they must not abandoned it for longer than the number of seconds defined in 'discard-abandoned-after'.

  • max-wait-time

По умолчанию 0, т.е. выключено
Любое значение в секундах, ограничивающее время пребывания в очереди (абсолютный таймаут). По идее должно быть больше чем discard-abandoned-after иначе не будет учитываться.

  • max-wait-time-with-no-agent

По умолчанию 0, т.е. выключено
Максимальное время пребывания в очереди без агентов. Призвано защитить абонентов от массового исключения из очереди, если все агенты по какой-то причине отвалились.

  • max-wait-time-with-no-agent-time-reached

По умолчанию 5
Любое значение в секундах. По достижению лимита max-wait-time-with-no-agent отвергать новые вызовы в течении заданного времени. Затем в течении этого же времени будет дан шанс подключиться к очереди.
(но если агенты не появятся, будет ли задан новый лимит?)

  • ring-progressively-delay

По умолчанию 10
Значение в секундах, задает время до вызова следующего агента при стратегии ring-progressively.

Tiers options

Tiers или уровни связывают агента с обслуживаемой очередью.

  • agent

Имя агента из параметра name настроек агентов соответственно

  • queue

Очередь с которой связан агент. Если агент обслуживает несколько очередей, для каждой создается tiers

  • level

Уровень используемый для tier-rule…

  • position

Номер позиции используется при стратегиях обзвона очереди

Пример таблицы динамических данных из таблицы tiers:
Вызывается очередь со стратегией round-robin и агент 2668:

     queue      |    agent     |   state   | level | position 
----------------+--------------+-----------+-------+----------
 testcc@default | 6666@default | No Answer |     1 |        1
 testcc@default | 2668@default | Offering  |     1 |        2

Вызов перешел на агента 6666:

fscore=> select * from tiers;
     queue      |    agent     |   state   | level | position 
----------------+--------------+-----------+-------+----------
 testcc@default | 2668@default | No Answer |     1 |        2
 testcc@default | 6666@default | Offering  |     1 |        1
<configuration name="callcenter.conf" description="CallCenter">
 
  <settings>
      <!--<param name="odbc-dsn" value="dsn:user:pass"/>-->
      <!--<param name="dbname" value="/dev/shm/callcenter.db"/>-->
  </settings>
 
  <queues>
    <queue name="sales@default">
      <param name="strategy" value="agent-with-least-talk-time"/>
      <param name="moh-sound" value="$${hold_music}"/>
      <!--<param name="record-template" value="$${base_dir}/recordings/sales/${strftime(%Y-%m-%d-%H-%M-%S)}.${destination_number}.${caller_id_number}.${uuid}.wav"/>-->
      <param name="time-base-score" value="queue"/>
      <param name="tier-rules-apply" value="false"/>
      <param name="tier-rule-wait-second" value="300"/>
      <param name="tier-rule-wait-multiply-level" value="true"/>
      <param name="tier-rule-no-agent-no-wait" value="false"/>
      <param name="discard-abandoned-after" value="14400"/>
      <param name="max-wait-time" value="0"/>
      <param name="max-wait-time-with-no-agent" value="120"/>
 
    </queue>
    <queue name="support@default">
      <param name="strategy" value="longest-idle-agent"/>
      <param name="moh-sound" value="$${hold_music}"/>
      <!--<param name="record-template" value="$${base_dir}/recordings/support/${strftime(%Y-%m-%d-%H-%M-%S)}.${destination_number}.${caller_id_number}.${uuid}.wav"/>-->
      <param name="time-base-score" value="system"/>
      <param name="tier-rules-apply" value="false"/>
      <param name="tier-rule-wait-second" value="300"/>
      <param name="tier-rule-wait-multiply-level" value="true"/>
      <param name="tier-rule-no-agent-no-wait" value="false"/>
      <param name="discard-abandoned-after" value="60"/>
      <param name="abandoned-resume-allowed" value="false"/>
      <param name="max-wait-time" value="0"/>
      <param name="max-wait-time-with-no-agent" value="120"/>
    </queue>
  </queues>
 
<!-- WARNING: Configuration of XML Agents will be updated into the DB upon restart. -->
<!-- WARNING: Configuration of XML Tiers will reset the level and position if those were supplied. -->
<!-- WARNING: Agents and Tiers XML config shouldn't be used in a multi FS shared DB setup. -->
 
  <agents>
    <agent name="1000@default" type="callback" contact="[leg_timeout=10]user/1000@default" status="Available" max-no-answer="3" wrap-up-time="10" reject-delay-time="10" busy-delay-time="60" />
    <!-- If you would like to set the Caller ID name, for whatever reason notice below. -->
    <agent name="1001@default" type="callback" contact="[origination_caller_id_name='Queue Caller',leg_timeout=10]user/1001@default" status="Available" max-no-answer="3" wrap-up-time="10" reject-delay-time="10" busy-delay-time="60" />
  </agents>
 
  <tiers>
    <!-- If no level or position is provided, they will default to 1. You should do this to keep db value on restart. -->
    <!-- agent 1000 will be in both the sales and support queues -->
    <tier agent="1000@default" queue="sales@default" level="1" position="1"/>
    <tier agent="1000@default" queue="support@default" level="1" position="1"/>
    <!-- agent 1001 will only be in the support queue -->
    <tier agent="1001@default" queue="support@default" level="1" position="1"/>
  </tiers>
 
</configuration>

Queues Strategy

Стратегии обзвона

StringDescription
ring-allВсе агенты вызываются одновременно.
longest-idle-agentВызывается агент с наибольшим временем простоя с учетом уровня (tier level).
round-robinАгенты вызываются по кругу после агента принявшего последний вызов (round-robin memory).
top-downАгенты вызываются в порядке перечисления сверху вниз.
agent-with-least-talk-timeВызывается агент с наименьшим временем разговора.
agent-with-fewest-callsВызывается агент принявший наименьшее кол-во звонков.
sequentially-by-agent-orderАгенты вызываются по очереди в соответствии с уровнем (tier level) и порядком.
randomАгенты вызываются в случайном порядке.
ring-progressivelyПохоже на top-down, но предыдущий агент продолжает вызываться, т.е. в конце концов будет ring-all.

Agents Status & States

Агенты имеют Status (статус) и States (состояние).

  • Status - ключевое состояние агента.
  • Status - не изменяется системой автоматически, но может быть изменен вручную, если нужно.
  • Status - может быть изменен во время разговора, это не повлияет на текущий вызов, а только на следующий.
  • States - специфическое состояние агента, устанавливается автоматически системой, в зависимости от фазы участия агента в обслуживании вызова.
  • Таким образом статистическое состояние агента (например в разговоре или вызывается) отделено от логического (принимает вызовы или нет).

Agent Status и States могут принимать следующие значения:

Agent Status:

StringDescription
Logged OutНе принимать вызовы из очереди.
AvailableПринимать вызовы.
Available(On Demand)После завершения вызова состояние будет установлено «Idle» (автоматически не устанавливается на «Waiting»).. *
On BreakВ очереди, но временно не принимает вызовы.

* Изменение статуса применяется только к следующему звонку. Так, например, если вы измените пользователя с Available на Available(On Demand), когда он находится в состоянии вызова или ожидания вызова, он получит еще один вызов и только затем перейдет в состояние Idle.

Agent State:

StringDescription
IdleНичего не делает, звонки не принимаются.
WaitingГотов принять вызов.
ReceivingОчередь вызывает агента.
In a queue callВ разговоре из очереди.

Variables

cc_export_vars

Экспортировать переменные в b-leg(s) после вызова приложения callcenter.

Это необходимо потому-что mod_callcenter производит вызов агента отдельным потоком, т.е. екстеншн в котором задано
<action application="callcenter" data="9000@callcenter"/>
является a-leg, а вызов агента по отношению к вызову приложения callcenter является b-leg.

Пример использования:

<action application="set" data="hold_music=local_stream://example_moh"/>
<action application="set" data="origination_caller_id_name=Call Center"/>
<action application="set" data="origination_caller_id_number=9000"/>
<action application="set" data="cc_export_vars=hold_music,origination_caller_id_name,origination_caller_id_number"/>
<action application="callcenter" data="9000@callcenter"/>

Иначе (без использования cc_export_vars) придется делать так:

<action application="bridge_export" data="nolocal:verto_h_pr=${pr}"/>
<action application="callcenter" data="foo"/>

Но иногда бывает, что заранее (в a-leg) нужной переменной еще нет, а она будет инициализированна только после вызова приложения callcenter. Например cc_queue_joined_epoch, тогда можно задать переменные в параметрах вызова агента в поле Contact:

[execute_on_answer=record_session::$${recordings_dir}/${strftime(%Y/%m/%d)}/call_project_${accountcode}/q_${cname}_${cnum}_${cc_queue_joined_epoch}]user/AGENT_ID@DOMAIN

В данном примере, мы получаем дополнительный бонус, активируя запись вызова, только после ответа агента очереди колл-центра.

Нажмите, чтобы отобразить

Нажмите, чтобы скрыть

    local xml = {}
    table.insert(xml, [[<?xml version="1.0" encoding="UTF-8" standalone="no"?>]]);
    table.insert(xml, [[<document type="freeswitch/xml">]]);
    table.insert(xml, [[    <section name="configuration">]]);
    table.insert(xml, [[        <configuration name="callcenter.conf" description="Callcenter">]]);
    table.insert(xml, [[            <settings>]]);
        -- assert (dbh:query("select * from cc_settings", function (qs)
        --           table.insert(xml, [[ <param name="odbc-dsn" value="]] .. qs.odbc_dsn .. [["/>]])
        -- end))
    table.insert(xml, [[              <param name="odbc-dsn" value="pgsql://hostaddr=127.0.0.1 dbname=DB  user=USER password='PASS'"/> -->]]);    
    table.insert(xml, [[          	</settings>]]);
    table.insert(xml, [[          	<queues>]]);
        assert (dbh:query("select * from cc_queues", function(qp)
            table.insert(xml, [[               <queue name="]].. qp.name ..[[">]]);
            table.insert(xml, [[                   <param name="strategy" value="]] .. qp.strategy .. [["/>]]);
            table.insert(xml, [[                   <param name="moh-sound" value="]] .. qp.moh_sound .. [["/>]]);
            table.insert(xml, [[                   <param name="record-template" value="]] .. qp.record_template .. [["/>]]);
            table.insert(xml, [[                   <param name="time-base-score" value="]] .. qp.time_base_score .. [["/>]]);
            table.insert(xml, [[                   <param name="tier-rules-apply" value="]] .. qp.tier_rules_apply .. [["/>]]);
            table.insert(xml, [[                   <param name="tier-rule-wait-second" value="]] .. qp.tier_rule_wait_second .. [["/>]]);
            table.insert(xml, [[                   <param name="tier-rule-wait-multiply-level" value="]] .. qp.tier_rule_wait_ml .. [["/>]]);
            table.insert(xml, [[                   <param name="tier-rule-no-agent-no-wait" value="]] .. qp.tier_rule_no_agent_no_wait .. [["/>]]);
            table.insert(xml, [[                   <param name="discard-abandoned-after" value="]] .. qp.discard_abandoned_after .. [["/>]]);
            table.insert(xml, [[                   <param name="abandoned-resume-allowed" value="]] .. qp.abandoned_resume_allowed .. [["/>]]);
            table.insert(xml, [[                   <param name="max-wait-time" value="]] .. qp.max_wait_time .. [["/>]]);
            table.insert(xml, [[                   <param name="max-wait-time-with-no-agent" value="]] .. qp.max_wait_time_wna .. [["/>]]);
            table.insert(xml, [[                   <param name="max-wait-time-with-no-agent-time-reached" value="]] .. qp.max_wait_time_wna_tr .. [["/>]]);
            table.insert(xml, [[                   <param name="ring-progressively-delay" value="]] .. qp.ring_progressively_delay .. [["/>]]);
            table.insert(xml, [[               </queue>]]);
        end))
    table.insert(xml, [[            </queues>]]);
    table.insert(xml, [[            <agents>]]); 
        assert (dbh:query("select * from cc_agents", function(qa)
                table.insert(xml, [[         <agent name="]] .. qa.name .. [[" type="]] .. qa.type .. [[" contact="]] .. qa.contact .. [[" status="]] .. qa.status .. [[" max-no-answer="]] .. qa.max_no_answer .. [[" wrap-up-time="]] .. qa.wrap_up_time .. [[" reject-delay-time="]] .. qa.reject_delay_time .. [[" busy-delay-time="]] .. qa.busy_delay_time .. [[" />]]);
        end))
    table.insert(xml, [[            </agents>]]); 
    table.insert(xml, [[            <tiers>]]); 
        assert (dbh:query("select * from cc_tiers", function(qt)
                table.insert(xml, [[         <tier agent="]] .. qt.agent .. [[" queue="]] .. qt.queue .. [[" level="]] .. qt.level .. [[" position="]] .. qt.position .. [["/>]]);
        end))
    table.insert(xml, [[            </tiers>]]); 
    table.insert(xml, [[        </configuration>]]);
    table.insert(xml, [[    </section>]]);
    table.insert(xml, [[</document>]]);
 
dbh:release();
XML_STRING = table.concat(xml, "\n")
CREATE TABLE fifo (
    id SERIAL PRIMARY KEY,
    name character varying,
    importance integer DEFAULT 0,
    timeout integer DEFAULT 20,
    simo integer DEFAULT 1,
    lag integer DEFAULT 5,
    member_wait character varying DEFAULT 'nowait',
    members character varying
);


CREATE TYPE queue_strategy AS ENUM ('ring-all','longest-idle-agent','round-robin','top-down','agent-with-least-talk-time','agent-with-fewest-calls','sequentially-by-agent-order','random','ring-progressively');

CREATE TABLE cc_queues (
    id SERIAL PRIMARY KEY,
    name character varying,
    strategy queue_strategy,
    moh_sound character varying NOT NULL DEFAULT '$${hold_music}',
    record_template character varying,
    time_base_score character varying NOT NULL DEFAULT 'queue',
    max_wait_time integer NOT NULL DEFAULT 0,
    max_wait_time_wna integer NOT NULL DEFAULT 0,
    max_wait_time_wna_tr integer NOT NULL DEFAULT 5,
    tier_rules_apply character varying NOT NULL DEFAULT 'false',
    tier_rule_wait_second integer NOT NULL DEFAULT 300,
    tier_rule_wait_ml character varying NOT NULL DEFAULT 'true',
    tier_rule_no_agent_no_wait character varying NOT NULL DEFAULT 'false',
    discard_abandoned_after integer NOT NULL DEFAULT 60,
    abandoned_resume_allowed character varying NOT NULL DEFAULT 'false',
    ring_progressively_delay integer NOT NULL DEFAULT 10
);

CREATE TABLE cc_settings(
	id SERIAL PRIMARY KEY,
	odbc_dsn character varying(1024),
	dbname character varying
);

CREATE TYPE agent_type AS ENUM ('callback','uuid-standby');
CREATE TYPE agent_status AS ENUM ('Logged Out','Available','Available (On Demand)','On Break');

CREATE TABLE cc_agents(
	id SERIAL PRIMARY KEY,
	name character varying,
	type agent_type,
	contact character varying NOT NULL,
	status agent_status,
	max_no_answer integer NOT NULL DEFAULT 3,
	wrap_up_time integer NOT NULL DEFAULT 10,
	reject_delay_time integer NOT NULL DEFAULT 3,	
	busy_delay_time integer NOT NULL DEFAULT 60,
	no_answer_delay_time integer NOT NULL DEFAULT 5,
	reserve_agents character varying NOT NULL DEFAULT 'false',
	truncate_agents_on_load character varying NOT NULL DEFAULT 'false',
	truncate_tiers_on_load character varying NOT NULL DEFAULT 'false'
);

CREATE TABLE cc_tiers(
	id SERIAL PRIMARY KEY,
	agent character varying NOT NULL,
	queue character varying NOT NULL,
	level integer NOT NULL DEFAULT 1,
	position integer NOT NULL DEFAULT 1
);
[execute_on_answer=bind_meta_app 8 ab s log::ERR FOOTEST]
  • freeswitch/mod/mod_callcenter.txt
  • Последние изменения: 2023/07/24