Инструменты пользователя

Инструменты сайта


configure_freepbx_for_cloud_version

Это старая версия документа.


Инструкция по подключению FreePBX к облачному сервису системы статистики ViStep.RU

Вводная

  • Данная инструкция написана используя FreePBX версии 13.0.191.11 с Asterisk 13.14.0 на борту.
    При возникновении каких-либо сложностей/вопросов с более ранними/поздними версиями системы, просьба написать нам на support@vistep.ru.
  • Для выполнения описанных ниже действий понадобится:
    - умение подключиться к серверу по ssh
    - умение редактировать файлы в текстовом редакторе (nano/vim/emacs/etc)
    - ясная голова и хорошее настроение ;-)

Настройка

В поставке по умолчанию (что называется «из коробки») FreePBX ведет БД asteriskcdrdb, а точнее табличку cdr, не совсем так, как это нужно для правильной работы системы статистики ViStep.RU. Также, она совсем не ведет записи в таблицу queue_log, что является необходимым.
Поэтому нам потребуется выполнить несколько шагов настройки, для достижения нужного поведения FreePBX.

  1. Начнем с таблицы cdr.
    Подключимся к серверу по ssh, а затем к консоли MySQL-сервера
    [root@localhost ~]# mysql asteriskcdrdb 
    Welcome to the MySQL monitor.  Commands end with ; or \g.
    Your MySQL connection id is 1686
    Server version: 5.1.73 Source distribution
     
    Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
     
    Oracle is a registered trademark of Oracle Corporation and/or its
    affiliates. Other names may be trademarks of their respective
    owners.
     
    Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
  2. Добавим табличке cdr новые поля id и filename и повесим триггер на INSERT для заполнения filename
    ALTER TABLE cdr ADD COLUMN filename VARCHAR(120) DEFAULT 'none';
    UPDATE cdr SET filename=recordingfile;
    ALTER TABLE cdr ADD COLUMN id INT(11) AUTO_INCREMENT PRIMARY KEY;
    DELIMITER $$
    CREATE TRIGGER `before_insert_cdr` BEFORE INSERT ON `cdr` FOR EACH ROW
    BEGIN
    SET NEW.filename=NEW.recordingfile;
    END $$
    DELIMITER ;
     

    При успешном выполнении запросов все должно выглядеть примерно вот так:

  3. Совершим вызов и проверим, что поле filename заполняется
  4. Теперь нам необходимо изменить логику заполнения полей dst и src в таблице cdr так, чтобы в них всегда попадали те номера, на которые/с которых поступил вызов (например, не номер оператора очереди, а наш городской номер, на который пришел вызов извне для dst или не номер транка, а номер внутреннего абонента для src).
    Для этого отредактируем файл cdr_adaptive_odbc.conf (FreePBX не перетрет его после рестарта, поэтому можно смело его редактировать), добавив в самом низу строку
    alias realdst => dst
    alias realsrc => src

    В итоге он примет вид:

  5. Дабы наши действия возымели силу, модулю cdr_adaptive_mysql.so нужно подсказать, что его конфиг изменился. Подключаемся к консоли asterisk и релоадим модуль вот так:
    [root@localhost asterisk]# asterisk -r
    localhost*CLI> module reload cdr_adaptive_odbc.so
    Module 'cdr_adaptive_odbc.so' reloaded successfully.
    localhost*CLI>  
  6. Пришло время последнего, но отнюдь не маловажного шага в разрезе конфигурирования таблицы cdr.
    В web-интерфейсе FreePBX отправимся по пути «Admin→ Config Edit», где в extensions_custom.conf внесем:
    [from-pstn-custom]
    exten => _X.,1,Set(__FROMPSTNCALL=1)
     

    А вот и скрин, для наглядности также внесем изменения в файл extensions_override_freepbx.conf

    [sub-record-check]
    include => sub-record-check-custom
    exten => s,1,GotoIf($[${LEN(${FROMEXTEN})}]?initialized)
    exten => s,n,Set(__REC_STATUS=INITIALIZED)
    exten => s,n,Set(NOW=${EPOCH})
    exten => s,n,Set(__DAY=${STRFTIME(${NOW},,%d)})
    exten => s,n,Set(__MONTH=${STRFTIME(${NOW},,%m)})
    exten => s,n,Set(__YEAR=${STRFTIME(${NOW},,%Y)})
    exten => s,n,Set(__TIMESTR=${YEAR}${MONTH}${DAY}-${STRFTIME(${NOW},,%H%M%S)})
    exten => s,n,Set(__FROMEXTEN=${IF($[${LEN(${AMPUSER})}]?${AMPUSER}:${IF($[${LEN(${REALCALLERIDNUM})}]?${REALCALLERIDNUM}:unknown)})})
    exten => s,n,Set(__MON_FMT=${IF($["${MIXMON_FORMAT}"="wav49"]?WAV:${MIXMON_FORMAT})})
    exten => s,n(initialized),Noop(Recordings initialized)
    exten => s,n,ExecIf($[!${LEN(${ARG3})}]?Set(ARG3=dontcare))
    exten => s,n,Set(REC_POLICY_MODE_SAVE=${REC_POLICY_MODE})
    exten => s,n,ExecIf($["${BLINDTRANSFER}${ATTENDEDTRANSFER}" != ""]?Set(REC_STATUS=NO))
    exten => s,n(next),GotoIf($[${LEN(${ARG1})}]?checkaction)
    exten => s,n(recorderror),Playback(something-terribly-wrong,error)
    exten => s,n,Hangup
    exten => s,n(checkaction),GotoIf($[${DIALPLAN_EXISTS(sub-record-check,${ARG1})}]?sub-record-check,${ARG1},1)
    exten => s,n,Noop(Generic ${ARG1} Recording Check - ${FROMEXTEN} ${ARG2})
    exten => s,n,Gosub(recordcheck,1(${ARG3},${ARG1},${ARG2}))
    exten => s,n,Return()
    
    exten => recordcheck,1,Noop(Starting recording check against ${ARG1})
    exten => recordcheck,n,Goto(${ARG1})
    exten => recordcheck,n(dontcare),Return()
    exten => recordcheck,n(always),Noop(Detected legacy "always" entry. Mapping to "force")
    exten => recordcheck,n(force),Set(__REC_POLICY_MODE=FORCE)
    exten => recordcheck,n,GotoIf($["${REC_STATUS}"!="RECORDING"]?startrec)
    exten => recordcheck,n,Return()
    exten => recordcheck,n(delayed),Noop(Detected legacy "delayed" entry. Mapping to "yes")
    exten => recordcheck,n(yes),ExecIf($["${REC_POLICY_MODE}" = "NEVER" | "${REC_POLICY_MODE}" = "NO" | "${REC_STATUS}" = "RECORDING"]?Return())
    exten => recordcheck,n,Set(__REC_POLICY_MODE=YES)
    exten => recordcheck,n,Goto(startrec)
    exten => recordcheck,n(no),Set(__REC_POLICY_MODE=NO)
    exten => recordcheck,n,Return()
    exten => recordcheck,n(never),Set(__REC_POLICY_MODE=NEVER)
    exten => recordcheck,n,Goto(stoprec)
    exten => recordcheck,n(startrec),Noop(Starting recording: ${ARG2}, ${ARG3})
    exten => recordcheck,n,Set(AUDIOHOOK_INHERIT(MixMonitor)=yes)
    exten => recordcheck,n,Set(__CALLFILENAME=${ARG2}-${ARG3}-${FROMEXTEN}-${TIMESTR}-${UNIQUEID})
    exten => recordcheck,n,MixMonitor(${MIXMON_DIR}${YEAR}/${MONTH}/${DAY}/${CALLFILENAME}.${MON_FMT},abi(LOCAL_MIXMON_ID)${MIXMON_BEEP},${MIXMON_POST})
    exten => recordcheck,n,Set(__MIXMON_ID=${LOCAL_MIXMON_ID})
    exten => recordcheck,n,Set(__RECORD_ID=${CHANNEL(name)})
    exten => recordcheck,n,Set(__REC_STATUS=RECORDING)
    exten => recordcheck,n,Set(CDR(recordingfile)=${CALLFILENAME}.${MON_FMT})
    exten => recordcheck,n,Return()
    exten => recordcheck,n(stoprec),Noop(Stopping recording: ${ARG2}, ${ARG3})
    exten => recordcheck,n,Set(__REC_STATUS=STOPPED)
    exten => recordcheck,n,System(${AMPBIN}/stoprecording.php "${CHANNEL(name)}")
    exten => recordcheck,n,Return()
    
    exten => out,1,Noop(Outbound Recording Check from ${FROMEXTEN} to ${ARG2})
    exten => out,n,Set(CDR(realdst)=${ARG2})
    exten => out,n,Set(CDR(realsrc)=${FROMEXTEN})
    exten => out,n,Set(RECMODE=${DB(AMPUSER/${FROMEXTEN}/recording/out/external)})
    exten => out,n,ExecIf($[!${LEN(${RECMODE})} | "${RECMODE}" = "dontcare"]?Goto(routewins))
    exten => out,n,ExecIf($["${ARG3}" = "never" | "${ARG3}" = "force"]?Goto(routewins))
    exten => out,n(extenwins),Gosub(recordcheck,1(${RECMODE},out,${ARG2}))
    exten => out,n,Return()
    exten => out,n(routewins),Gosub(recordcheck,1(${ARG3},out,${ARG2}))
    exten => out,n,Return()
    
    exten => in,1,Noop(Inbound Recording Check to ${ARG2})
    exten => in,n,Set(CDR(realdst)=${ARG2})
    exten => in,n,Set(CDR(realsrc)=${CALLERID(number)})
    exten => in,n,Set(FROMEXTEN=unknown)
    exten => in,n,ExecIf($[${LEN(${CALLERID(num)})}]?Set(FROMEXTEN=${CALLERID(num)}))
    exten => in,n,Gosub(recordcheck,1(${ARG3},in,${ARG2}))
    exten => in,n,Return()
    
    exten => exten,1,Noop(Exten Recording Check between ${FROMEXTEN} and ${ARG2})
    exten => exten,n,Set(CDR(realdst)=${ARG2})
    exten => exten,n,Set(CDR(realsrc)=${FROMEXTEN})
    exten => exten,n,Set(CALLTYPE=${IF($[${LEN(${FROM_DID})}]?external:internal)})
    exten => exten,n,ExecIf(${LEN(${CALLTYPE_OVERRIDE})}?Set(CALLTYPE=${CALLTYPE_OVERRIDE}))
    exten => exten,n,Set(CALLEE=${DB(AMPUSER/${ARG2}/recording/in/${CALLTYPE})})
    exten => exten,n,ExecIf($[!${LEN(${CALLEE})}]?Set(CALLEE=dontcare))
    exten => exten,n,GotoIf($["${CALLTYPE}"="external"]?callee)
    exten => exten,n,GotoIf($["${CALLEE}"="dontcare"]?caller)
    exten => exten,n,ExecIf($[${LEN(${DB(AMPUSER/${FROMEXTEN}/recording/priority)})}]?Set(CALLER_PRI=${DB(AMPUSER/${FROMEXTEN}/recording/priority)}):Set(CALLER_PRI=0))
    exten => exten,n,ExecIf($[${LEN(${DB(AMPUSER/${ARG2}/recording/priority)})}]?Set(CALLEE_PRI=${DB(AMPUSER/${ARG2}/recording/priority)}):Set(CALLEE_PRI=0))
    exten => exten,n,GotoIf($["${CALLER_PRI}"="${CALLEE_PRI}"]?${REC_POLICY}:${IF($[${CALLER_PRI}>${CALLEE_PRI}]?caller:callee)})
    exten => exten,n(callee),Gosub(recordcheck,1(${CALLEE},${CALLTYPE},${ARG2}))
    exten => exten,n,Return()
    exten => exten,n(caller),Set(RECMODE=${DB(AMPUSER/${FROMEXTEN}/recording/out/internal)})
    exten => exten,n,ExecIf($[!${LEN(${RECMODE})}]?Set(RECMODE=dontcare))
    exten => exten,n,ExecIf($["${RECMODE}"="dontcare"]?Set(RECMODE=${CALLEE}))
    exten => exten,n,Gosub(recordcheck,1(${RECMODE},${CALLTYPE},${ARG2}))
    exten => exten,n,Return()
    
    exten => conf,1,Noop(Conference Recording Check ${FROMEXTEN} to ${ARG2})
    exten => conf,n,Gosub(recconf,1(${ARG2},${ARG2},${ARG3}))
    exten => conf,n,Return()
    
    exten => page,1,Noop(Paging Recording Check ${FROMEXTEN} to ${ARG2})
    exten => page,n,GosubIf($["${REC_POLICY_MODE}"="always"]?recconf,1(${ARG2},${FROMEXTEN},${ARG3}))
    exten => page,n,Return()
    
    exten => recconf,1,Noop(Setting up recording: ${ARG1}, ${ARG2}, ${ARG3})
    exten => recconf,n,Set(__CALLFILENAME=${IF($[${CONFBRIDGE_INFO(parties,${ARG2})}]?${DB(RECCONF/${ARG2})}:${ARG1}-${ARG2}-${ARG3}-${TIMESTR}-${UNIQUEID})})
    exten => recconf,n,ExecIf($[!${CONFBRIDGE_INFO(parties,${ARG2})}]?Set(DB(RECCONF/${ARG2})=${CALLFILENAME}))
    exten => recconf,n,Set(CONFBRIDGE(bridge,record_file)=${MIXMON_DIR}${YEAR}/${MONTH}/${DAY}/${CALLFILENAME}.${MON_FMT})
    exten => recconf,n,ExecIf($["${ARG3}"!="always"]?Return())
    exten => recconf,n,Set(CONFBRIDGE(bridge,record_conference)=yes)
    exten => recconf,n,Set(__REC_STATUS=RECORDING)
    exten => recconf,n,Set(CDR(recordingfile)=${IF($[${CONFBRIDGE_INFO(parties,${ARG2})}]?${CALLFILENAME}.${MON_FMT}:${CALLFILENAME}.${MON_FMT})})
    exten => recconf,n,Noop(${MIXMONITOR_FILENAME})
    exten => recconf,n,Set(CHANNEL(hangup_handler_push)=sub-record-hh-check,s,1)
    exten => recconf,n,Return()
    
    exten => recq,1,Noop(Setting up recording: ${ARG1}, ${ARG2}, ${ARG3})
    exten => recq,n,Set(AUDIOHOOK_INHERIT(MixMonitor)=yes)
    exten => recq,n,Set(MONITOR_FILENAME=${MIXMON_DIR}${YEAR}/${MONTH}/${DAY}/${CALLFILENAME})
    exten => recq,n,MixMonitor(${MONITOR_FILENAME}.${MON_FMT},${MONITOR_OPTIONS}${MIXMON_BEEP},${MIXMON_POST})
    exten => recq,n,Set(__REC_STATUS=RECORDING)
    exten => recq,n,Set(CDR(recordingfile)=${CALLFILENAME}.${MON_FMT})
    exten => recq,n,Return()
    
    exten => parking,1,Noop(User ${ARG2} picked up a parked call)
    exten => parking,n,Set(USER=${ARG2})
    exten => parking,n,ExecIf($[!${LEN(${ARG2})}]?Set(USER=unknown))
    exten => parking,n,Set(RECMODE=${DB(AMPUSER/${ARG2}/recording/out/internal)})
    exten => parking,n,ExecIf($[!${LEN(${RECMODE})}]?Set(RECMODE=dontcare))
    exten => parking,n,Gosub(recordcheck,1(${RECMODE},parked,${USER}))
    exten => parking,n,Return()
    
    ;--== end of [sub-record-check] ==--;
    
    
    
    
    [macro-exten-vm]
    include => macro-exten-vm-custom
    exten => s,1,Macro(user-callerid,)
    exten => s,n,ExecIF(${FROMPSTNCALL}?Set(CDR_PROP(disable)=true):Set(CDR(realdst)=${EXTEN}))
    exten => s,n,Set(RingGroupMethod=none)
    exten => s,n,Set(__EXTTOCALL=${ARG2})
    exten => s,n,Set(__PICKUPMARK=${ARG2})
    exten => s,n,Set(RT=${IF($["${ARG1}"!="novm" | "${DB(CFU/${EXTTOCALL})}"!="" | "${DB(CFB/${EXTTOCALL})}"!="" | "${ARG3}"="1" | "${ARG4}"="1" | "${ARG5}"="1"]?${RINGTIMER}:)})
    exten => s,n,ExecIf($[$["${REDIRECTING(reason)}" = "send_to_vm" | "${SIP_HEADER(X-Digium-Call-Feature)}" = "feature_send_to_vm"] & "${ARG1}" != "novm"]?Macro(vm,${ARG1},DIRECTDIAL,${IVR_RETVM}))
    exten => s,n,ExecIf($[$["${REDIRECTING(reason)}" = "send_to_vm" | "${SIP_HEADER(X-Digium-Call-Feature)}" = "feature_send_to_vm"] & "${ARG1}" != "novm"]?MacroExit())
    exten => s,n(checkrecord),Gosub(sub-record-check,s,1(exten,${EXTTOCALL},dontcare))
    exten => s,n,GotoIf($["${CUT(CHANNEL,@,2):5:5}"="queue"|"${AMPUSER}"=""|${LEN(${FROM_DID})}|"${DB(AMPUSER/${EXTTOCALL}/answermode)}"!="intercom"|${LEN(${BLINDTRANSFER})}|"${EXTENSION_STATE(${EXTTOCALL})}"!="NOT_INUSE"|"${CC_RECALL}"!=""]?macrodial)
    exten => s,n,Set(INTERCOM_EXT_DOPTIONS=${DIAL_OPTIONS})
    exten => s,n,Set(INTERCOM_RETURN=TRUE)
    exten => s,n,Gosub(ext-intercom,*80${EXTTOCALL},1())
    exten => s,n,Set(INTERCOM_RETURN=)
    exten => s,n,Set(INTERCOM_EXT_DOPTIONS=)
    exten => s,n(macrodial),GosubIf($["${INTERCOM_CALL}"="TRUE" & ${LEN(${BLINDTRANSFER})}]?clrheader,1())
    exten => s,n,Macro(dial-one,${RT},${DIAL_OPTIONS},${EXTTOCALL})
    exten => s,n,Set(SV_DIALSTATUS=${DIALSTATUS})
    exten => s,n(calldocfu),GosubIf($[("${SV_DIALSTATUS}"="NOANSWER"|"${SV_DIALSTATUS}"="CHANUNAVAIL") & "${DB(CFU/${EXTTOCALL})}"!="" & "${SCREEN}"=""]?docfu,1())
    exten => s,n(calldocfb),GosubIf($["${SV_DIALSTATUS}"="BUSY" & "${DB(CFB/${EXTTOCALL})}"!="" & "${SCREEN}"=""]?docfb,1())
    exten => s,n,Set(DIALSTATUS=${SV_DIALSTATUS})
    exten => s,n,ExecIf($[("${DIALSTATUS}"="NOANSWER"&"${ARG3}"="1")|("${DIALSTATUS}"="BUSY"&"${ARG4}"="1")|("${DIALSTATUS}"="CHANUNAVAIL"&"${ARG5}"="1")]?MacroExit())
    exten => s,n,GotoIf($["${ARG1}"="novm"]?s-${DIALSTATUS},1)
    exten => s,n,Macro(vm,${ARG1},${DIALSTATUS},${IVR_RETVM})
    
    exten => docfu,1(docfu),ExecIf($["${DB(AMPUSER/${EXTTOCALL}/cfringtimer)}"="-1"|("${ARG1}"="novm"&"${ARG3}"="1")]?StackPop())
    exten => docfu,n,GotoIf($["${DB(AMPUSER/${EXTTOCALL}/cfringtimer)}"="-1"|("${ARG1}"="novm"&"${ARG3}"="1")]?from-internal,${DB(CFU/${EXTTOCALL})},1)
    exten => docfu,n,Set(RTCF=${IF($["${DB(AMPUSER/${EXTTOCALL}/cfringtimer)}"="0"]?${RT}:${DB(AMPUSER/${EXTTOCALL}/cfringtimer)})})
    exten => docfu,n,ExecIf($["${DIRECTION}" = "INBOUND"]?Set(DIAL_OPTIONS=${STRREPLACE(DIAL_OPTIONS,T)}I))
    exten => docfu,n,Dial(Local/${DB(CFU/${EXTTOCALL})}@from-internal/n,${RTCF},${DIAL_OPTIONS})
    exten => docfu,n,Return()
    
    exten => docfb,1(docfu),ExecIf($["${DB(AMPUSER/${EXTTOCALL}/cfringtimer)}"="-1"|("${ARG1}"="novm"&"${ARG4}"="1")]?StackPop())
    exten => docfb,n,GotoIf($["${DB(AMPUSER/${EXTTOCALL}/cfringtimer)}"="-1"|("${ARG1}"="novm"&"${ARG4}"="1")]?from-internal,${DB(CFB/${EXTTOCALL})},1)
    exten => docfb,n,Set(RTCF=${IF($["${DB(AMPUSER/${EXTTOCALL}/cfringtimer)}"="0"]?${RT}:${DB(AMPUSER/${EXTTOCALL}/cfringtimer)})})
    exten => docfb,n,ExecIf($["${DIRECTION}" = "INBOUND"]?Set(DIAL_OPTIONS=${STRREPLACE(DIAL_OPTIONS,T)}I))
    exten => docfb,n,Dial(Local/${DB(CFB/${EXTTOCALL})}@from-internal/n,${RTCF},${DIAL_OPTIONS})
    exten => docfb,n,Return()
    
    exten => clrheader,1,ExecIf($[${LEN(${SIPURI})}&"${SIPURI}"="${SIP_URI_OPTIONS}"]?Set(SIP_URI_OPTIONS=))
    exten => clrheader,n,ExecIf($[${LEN(${ALERTINFO})}]?SIPRemoveHeader(${ALERTINFO}))
    exten => clrheader,n,ExecIf($[${LEN(${CALLINFO})}]?SIPRemoveHeader(${CALLINFO}))
    exten => clrheader,n,Return()
    
    exten => s-BUSY,1,GotoIf($["${IVR_RETVM}"="RETURN" & "${IVR_CONTEXT}"!=""]?exit,1)
    exten => s-BUSY,n,Playtones(busy)
    exten => s-BUSY,n,Busy(20)
    
    exten => _s-!,1,GotoIf($["${IVR_RETVM}"="RETURN" & "${IVR_CONTEXT}"!=""]?exit,1)
    exten => _s-!,n,Playtones(congestion)
    exten => _s-!,n,Congestion(10)
    
    exten => exit,1,Playback(beep&line-busy-transfer-menu&silence/1)
    exten => exit,n,MacroExit()
    
    ;--== end of [macro-exten-vm] ==--;

    В завершении кликнем Save, затем Apply Config, позвоним на АТС, с АТС вовне и проверим корректность заполнения поля dst.

  7. С cdr разобрались, дело за queue_log. Эта таблица нужна нам для того, чтобы понимать какие события происходили в очередях Asterisk. Перво-наперво создадим ее
    CREATE TABLE `queue_log` (
     `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
     `time` TIMESTAMP NULL DEFAULT '0000-00-00 00:00:00',
     `callid` VARCHAR(32) NOT NULL DEFAULT '',
     `queuename` VARCHAR(32) NOT NULL DEFAULT '',
     `agent` VARCHAR(32) NOT NULL DEFAULT '',
     `event` VARCHAR(32) NOT NULL DEFAULT '',
     `data1` VARCHAR(100) NOT NULL DEFAULT '',
     `data2` VARCHAR(100) NOT NULL DEFAULT '',
     `data3` VARCHAR(100) NOT NULL DEFAULT '',
     `data4` VARCHAR(100) NOT NULL DEFAULT '',
     `data5` VARCHAR(100) NOT NULL DEFAULT '',
     PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
  8. Научим FreePBX(читай Asterisk) писать туда все то, что нам нужно.
    Отправляемся в web-интерфейс по знакомой дорожке и создаем новый файл под именем extconfig.conf, кликнув Add New File.
    Внести в него нужно лишь одну строчку
    [settings]
    queue_log => odbc,asteriskcdrdb

    Скрин: *не забываем про Save и Apply Configs
    После выполнения данных действий и совершения звонков в очереди, запрос из консоли mysql

    SELECT * FROM queue_log;

    должен вернуть вам нечто подобное *на запрос в скрине не ориентируйтесь, там я специально добавил условие, чтобы сократить выхлоп

  9. С препарированием нашей АТС по части БД закончили!
    Переходим к настройке синхронизации БД и файлов записей разговоров в облако ViStep.RU.
    После того, как вы получите ссылку для входа в систему, логин и пароль, вам необходимо авторизироваться и кликнуть меню «Настройки→Основные»
    В скринах это будет выглядеть так:
  10. Укажите путь к файлам записей разговоров (в FreePBX это обычно /var/spool/asterisk/monitor/) и нажмите кнопку «Сохранить», после чего появится возможность скачать архив со скриптом синхронизации
  11. Заугрузите на сервер скачанный архив в папку /opt (например, используя scp или FileZilla)
  12. Далее просто листинг команд, дабы не перегружать текст
    [root@localhost ~]# cd /opt/
    [root@localhost opt]# mkdir -p stat.vistep.ru
    [root@localhost opt]# mv Fb38f1218c6.zip stat.vistep.ru/
    [root@localhost opt]# cd stat.vistep.ru/
    [root@localhost stat.vistep.ru]# unzip Fb38f1218c6.zip
    [root@localhost stat.vistep.ru]# rm -f Fb38f1218c6.zip

    по окончании у нас должно получиться так

  13. В скрипте stat.vistep.ru.js уже практически все учтено, нам остается только вбить данные доступа к БД. Откроем файл stat.vistep.ru.js в любимом текстовом редакторе и внесем нужные изменения в строках 393-397
    // Исправляем все что ниже под себя
    "dbhost":"localhost",
    "dbuser":"freepbxuser",
    "dbpassword":"1230239dkl8c5ef2rf497aeb523f1d117cdb",
    "db":"asteriskcdrdb", // <--- имя БД
    "timezone":"Europe/Moscow", // <--- часовой пояс
    "fileMask":  /\.*/  // <--- регулярка, если нужно синкать все файлы
     
    //  далее ничего не трогаем

    *Логин и пароль для доступа к БД вы найдете в файле /etc/asterisk/res_odbc_additional.conf

  14. Мы почти у цели, остался последний штрих.
    Скрипт нужно запустить, но не только запустить, а еще и форкнуть его (говоря по-русски нужно чтобы скрипт остался в работе после того, как мы отключимся от сервера, т.е. работал бы как любой другой демон в ОС).
    Такой функционал обеспечит замечательный proccess meneger pm2.
    Установим pm2 и отдадим в его руки управление скриптом
    npm install pm2 -g 
    pm2 start /opt/stat.vistep.ru/stat.vistep.ru.js --name "ViStep.RU stat"
    pm2 startup centos6
    pm2 save

На этом все настройки завершены.
Если вы все сделали правильно, то через какое-то время (зависит от размера БД) сможете воспользоваться всеми отчетами системы статистики ViStep.RU в своем личном кабинете.
Повторюсь, если у вас остались вопросы и/или есть предложения/замечания, пишите нам на support@vistep.ru.

configure_freepbx_for_cloud_version.1504683625.txt.gz · Последние изменения: 2017/09/06 14:40 — Евгений Романенко