#!/bin/bash # AGI-сценарий автодозвона SuperSam. # Аргументы: $1 = сценарий (A|B|C), $2 = тип (dlv|pkp) # A выбор дня + времени (звоним в день готовности) # B день фиксирован (завтра), только время # C слот уже выбран — подтверждение (молчание = подтвердил) # После подтверждения выбора предлагается изменить его (кнопка 2). # Результат: /tmp/ss-result-.txt. Конфиги Asterisk не меняет. RES=/tmp/ss-result LOG=/tmp/ss-dialog.log UNIQ="" while read -r line; do line=${line%$'\r'} [ -z "$line" ] && break case "$line" in agi_uniqueid:*) UNIQ=$(echo "$line" | cut -d' ' -f2) ;; agi_arg_1:*) SCENARIO=$(echo "$line" | cut -d' ' -f2) ;; agi_arg_2:*) TYPE=$(echo "$line" | cut -d' ' -f2) ;; esac done SCENARIO=${SCENARIO:-A} TYPE=${TYPE:-dlv} OUT="${RES}-${UNIQ}.txt" say () { echo "$1"; read -r resp; echo "$(date +%T) $1 -> $resp" >> "$LOG"; RESP="$resp"; } digit () { # $1=озвучка, $2=таймаут мс -> DIGIT say "GET DATA $1 ${2:-9000} 1" DIGIT=$(echo "$RESP" | sed -n 's/.*result=\([0-9]*\).*/\1/p') } # Чтение channel-переменной из call-файла (Setvar) через AGI. getvar () { # $1=имя -> VAR say "GET VARIABLE $1" VAR=$(echo "$RESP" | sed -n 's/^200 result=1 (\(.*\))$/\1/p') } play () { say "STREAM FILE $1 \"\""; } # Channel-переменные из call-файла (Setvar). Читаем ДО диалога, т.к. AGI # не видит их как bash-переменные — только через GET VARIABLE. getvar API_URL; API_URL=$VAR getvar API_KEY; API_KEY=$VAR getvar GROUP_ID; GROUP_ID=$VAR getvar ATTEMPT; ATTEMPT=$VAR getvar TEST_MODE; TEST_MODE=$VAR getvar CUSTOMER_PHONE; CUSTOMER_PHONE=$VAR # Пишем состояние ПОСЛЕ КАЖДОГО шага: если клиент положит трубку, # выбор всё равно сохранён (AGI умирает молча, финальной записи не будет). save () { { echo "uniqueid=$UNIQ" echo "scenario=$SCENARIO" echo "outcome=${OUTCOME:-in_progress}" echo "dtmf_day=$CONF_DAY" echo "dtmf_half=$CONF_HALF" echo "in_progress_day=$DAY" echo "in_progress_half=$HALF" echo "changed=$CHANGED" echo "edits=$EDITS" echo "updated_at=$(date '+%F %T')" } > "$OUT" # Отправляем в БД сразу при финальном исходе (клиент может бросить трубку # в любой момент — результат должен успеть уйти). Идемпотентность по uniqueid. case "$OUTCOME" in confirmed|confirmed_kept_previous|confirmed_silent|no_answer|no_input_day|no_input_half|operator|opt_out|invalid_number|error) send_result ;; esac } # Отправка результата в БД (edge function autocall-result). # Переменные приходят из call-файла (Setvar) и прочитаны через getvar выше. # При потере сети звонок не валится: curl с таймаутом, результат в $OUT. send_result () { [ -n "$API_URL" ] && [ -n "$API_KEY" ] || return 0 curl -s --max-time 5 -X POST "$API_URL" \ -H "Content-Type: application/json" \ -H "x-webhook-secret: $API_KEY" \ -d "{ \"asterisk_uniqueid\":\"$UNIQ\", \"order_group_id\":\"${GROUP_ID:-}\", \"campaign_type\":\"autocall_slot\", \"customer_phone\":\"${CUSTOMER_PHONE:-}\", \"attempt_no\":${ATTEMPT:-1}, \"scenario\":\"$SCENARIO\", \"call_started_at\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\", \"answered_at\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\", \"duration_sec\":0, \"sip_code\":200, \"channel_state\":\"Up\", \"dtmf_day\":${CONF_DAY:-null}, \"dtmf_half\":${CONF_HALF:-null}, \"changed\":\"$CHANGED\", \"outcome\":\"$OUTCOME\", \"was_test_mode\":${TEST_MODE:-0} }" >/dev/null 2>&1 || echo "curl result failed (network?)" >> "$LOG" } echo "=== $(date) uniq=$UNIQ scenario=$SCENARIO type=$TYPE ===" >> "$LOG" DAY=""; HALF=""; OUTCOME=""; CHANGED=no; EDITS=0 CONF_DAY=""; CONF_HALF="" # согласованный слот: только он идёт в БД save say "ANSWER" say "EXEC Wait 1" # ─── C: подтверждение уже выбранного слота ─────────────────────────── if [ "$SCENARIO" = "C" ]; then play "ru/ss_c_intro_${TYPE}" play "ru/ss_c_day_tomorrow" play "ru/ss_ok_first" digit "ru/ss_c_ask" 8000 if [ "$DIGIT" = "1" ]; then play "ru/ss_c_change" CHANGED=yes SCENARIO=A else play "ru/ss_c_confirmed" OUTCOME=confirmed_silent DAY=1; HALF=1 fi fi # ─── выбор дня и времени, с возможностью переделать ────────────────── while [ -z "$OUTCOME" ]; do # шаг 1: день if [ "$SCENARIO" = "A" ]; then digit "ru/ss_a_day_${TYPE}" DAY=$DIGIT if [ "$DAY" != "1" ] && [ "$DAY" != "2" ] && [ "$DAY" != "0" ] && [ "$DAY" != "9" ]; then digit "ru/ss_a_day_${TYPE}" DAY=$DIGIT fi else DAY=1 # сценарий B: день фиксирован = завтра fi save if [ "$DAY" = "9" ]; then play "ru/ss_optout"; OUTCOME=opt_out; break elif [ "$DAY" = "0" ]; then play "ru/ss_operator"; OUTCOME=operator; break elif [ "$DAY" != "1" ] && [ "$DAY" != "2" ]; then play "ru/ss_no_input"; OUTCOME=no_input_day; break fi # шаг 2: время if [ "$SCENARIO" = "B" ]; then digit "ru/ss_b_half_${TYPE}" else digit "ru/ss_half_${TYPE}" fi HALF=$DIGIT if [ "$HALF" != "1" ] && [ "$HALF" != "2" ]; then if [ "$SCENARIO" = "B" ]; then digit "ru/ss_b_half_${TYPE}" else digit "ru/ss_half_${TYPE}" fi HALF=$DIGIT fi if [ "$HALF" != "1" ] && [ "$HALF" != "2" ]; then play "ru/ss_no_input"; OUTCOME=no_input_half; break fi # ЕДИНАЯ ТОЧКА ФИКСАЦИИ: оба шага пройдены — только теперь слот становится # согласованным. Обрыв на любом шаге раньше не портит уже согласованный слот. CONF_DAY=$DAY; CONF_HALF=$HALF OUTCOME=confirmed save # шаг 3: озвучиваем выбор [ "$DAY" = "1" ] && play "ru/ss_ok_tomorrow" || play "ru/ss_ok_dayafter" [ "$HALF" = "1" ] && play "ru/ss_ok_first" || play "ru/ss_ok_second" # шаг 4: предложить изменить (один раз) if [ "$EDITS" -lt 1 ]; then digit "ru/ss_change_hint" 6000 if [ "$DIGIT" = "2" ]; then # CONF_* НЕ трогаем: пока новый выбор не сделан целиком, в силе прежний. # Рабочие DAY/HALF обнуляем, чтобы не склеить новый день со старым временем. # ВАЖНО: OUTCOME к этому моменту = confirmed (выставлен на шаге фиксации). # Сбрасываем его в пусто, иначе цикл while [ -z OUTCOME ] завершится и день # не переспросит. Контракт: пустой OUTCOME = диалог ещё идёт. DAY=""; HALF=""; OUTCOME="" EDITS=$((EDITS+1)); CHANGED=yes; SCENARIO=A save continue # заново день и время fi fi # подтверждено if [ "$EDITS" -gt 0 ]; then play "ru/ss_tail_final_${TYPE}" else play "ru/ss_tail_${TYPE}" fi OUTCOME=confirmed done # Правка начата, но не доведена (тишина/сброс): согласованный слот остаётся в силе if [ -n "$CONF_DAY" ] && [ "$OUTCOME" != "confirmed" ]; then OUTCOME=confirmed_kept_previous fi save echo "finished_at=$(date '+%F %T')" >> "$OUT" say "HANGUP" exit 0