«Напомните через месяц?»: как автоматизировать напоминания клиентам с Golang, SQLite и вебхуками

от автора

Привет, Хабр! Представим ситуацию: вы клиент. Разговор с менеджером завершён, он предложил вам что-то полезное — услугу, продукт или подписку — и, допустим, вы соглашаетесь: «Почему бы и нет, отличная идея». Менеджер записал ваше согласие и обещал напомнить вам через месяц. Звучит просто.

Но вот в реальности ни один менеджер не помнит про сотни обещаний клиентам. И здесь на помощь приходит автоматизация. В этой статье рассмотрим, как построить систему автоматического напоминания, которая избавит менеджеров от лишней работы и увеличит количество сделок, которые могли бы улетучиться.

Для реализации понадобятся:

  • Golang — основная платформа, на которой будем писать все сервисы.

  • Exolve API — для обработки звонков и получения текстовой расшифровки разговоров.

  • SQLite с GORM — база данных для хранения согласий клиентов, анализа и отчётности.

  • Webhook — для автоматического запуска цепочки при готовности транскрибации.

  • HTTP и JSON — для работы с API и передачи данных в запросах.

Активация транскрибации

Чтобы система могла анализировать согласие клиента, нужна текстовая расшифровка звонков.

Для начала активируем транскрибацию на номере, с которого идут звонки.

package main  import (     "bytes"     "encoding/json"     "fmt"     "log"     "net/http"     "os" )  // Загружаем API-ключ из переменных окружения var apiKey = os.Getenv("EXOLVE_API_KEY")  // enableTranscription включает транскрибацию на указанном номере func enableTranscription(numberCode uint64) error {     endpoint := "https://api.exolve.ru/number/v1/SetCallTranscribationState"     payload := map[string]interface{}{         "number_code":        numberCode,         "call_transcribation": true,     }     body, err := json.Marshal(payload)     if err != nil {         return fmt.Errorf("ошибка сериализации: %w", err)     }      req, err := http.NewRequest("POST", endpoint, bytes.NewBuffer(body))     if err != nil {         return fmt.Errorf("ошибка создания запроса: %w", err)     }     req.Header.Set("Authorization", "Bearer "+apiKey)     req.Header.Set("Content-Type", "application/json")      client := &http.Client{}     resp, err := client.Do(req)     if err != nil {         return fmt.Errorf("ошибка отправки запроса: %w", err)     }     defer resp.Body.Close()      if resp.StatusCode != http.StatusOK {         return fmt.Errorf("не удалось включить транскрибацию, статус: %d", resp.StatusCode)     }      log.Println("Транскрибация включена успешно.")     return nil }  func main() {     err := enableTranscription(79991112233)     if err != nil {         log.Fatalf("Ошибка активации транскрибации: %v", err)     } }

Функция enableTranscription отправляет запрос на активацию транскрибации для указанного номера. Передаём API-ключ в заголовке авторизации, чтобы подтвердить права. Если что-то пошло не так — код выведет понятное сообщение об ошибке. Основное здесь — отлавливать ошибки на каждом этапе, чтобы, если что-то пошло не так, система не просто «глохла», а давала обратную связь.

Получение текста разговора и анализ согласия

Теперь есть возможность получать текстовую расшифровку каждого звонка. Настало время разобраться, сказал ли клиент «да» или «нет». Для этого напишем функцию getTranscription, которая извлекает текст, и функцию analyzeConsent, проверяющую наличие согласия клиента.

func getTranscription(uid uint64) (string, error) {     endpoint := "https://api.exolve.ru/statistics/call-record/v1/GetTranscribation"     payload := map[string]interface{}{         "uid": uid,     }     body, err := json.Marshal(payload)     if err != nil {         return "", fmt.Errorf("ошибка сериализации: %w", err)     }      req, err := http.NewRequest("POST", endpoint, bytes.NewBuffer(body))     if err != nil {         return "", fmt.Errorf("ошибка создания запроса: %w", err)     }     req.Header.Set("Authorization", "Bearer "+apiKey)     req.Header.Set("Content-Type", "application/json")      client := &http.Client{}     resp, err := client.Do(req)     if err != nil {         return "", fmt.Errorf("ошибка получения транскрибации: %w", err)     }     defer resp.Body.Close()      var result map[string]interface{}     if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {         return "", fmt.Errorf("ошибка декодирования ответа: %w", err)     }      chunks := result["transcribation"].([]interface{})[0].(map[string]interface{})["chunks"].(map[string]interface{})     text := chunks["text"].(string)     return text, nil }  func analyzeConsent(text string) bool {     consentKeywords := []string{"да", "согласен", "конечно"}     for _, keyword := range consentKeywords {         if strings.Contains(strings.ToLower(text), keyword) {             return true         }     }     return false }

getTranscription извлекает текстовую расшифровку по uid звонка. Ошибки обрабатываются на каждом этапе, так что если произойдет сбой — точно узнаем об этом.

analyzeConsent проверяет, есть ли в тексте слова, указывающие на согласие. Функция возвращает true, если одно из слов найдено.

Теперь есть текст разговора, и можно понять, хочет ли клиент напоминание. Дальше подключаем БД.

Сохраняем данные о согласии в базе данных

Если клиент согласился на напоминание, хочется сохранить это в базе данных для последующего анализа. Это позволит отслеживать конверсию, считать успешные напоминания и вести отчётность.

import (     "gorm.io/driver/sqlite"     "gorm.io/gorm"     "log" )  type Consent struct {     ID       uint   `gorm:"primaryKey"`     UID      uint64 `gorm:"unique;not null"`     Text     string `gorm:"not null"`     Agreed   bool   `gorm:"not null"` }  func initDB() (*gorm.DB, error) {     db, err := gorm.Open(sqlite.Open("consents.db"), &gorm.Config{})     if err != nil {         return nil, err     }     db.AutoMigrate(&Consent{})     return db, nil }  func saveConsent(db *gorm.DB, uid uint64, text string, agreed bool) error {     consent := Consent{UID: uid, Text: text, Agreed: agreed}     return db.Create(&consent).Error }

Используем SQLite для простоты, но можно использовать любую базу данных. Функция saveConsent сохраняет UID звонка, текст и результат анализа (согласие или отказ) в базу. Это пригодится, если нужно строить аналитику по клиентам.

Отправка SMS с поддержкой повторных попыток

Допустим, клиент согласился на напоминание. Теперь задача — отправить ему SMS через месяц. Но, как известно, отправка SMS может быть не всегда надёжной: сбои сети, проблемы на стороне оператора. Поэтому добавим механизм повторных попыток.

import "time"  func scheduleSMS(db *gorm.DB, uid uint64, to, message string) {     delay := 30 * 24 * time.Hour     time.AfterFunc(delay, func() {         retryCount := 3         for i := 0; i < retryCount; i++ {             if sendSMS(to, message) {                 log.Printf("SMS отправлено для UID: %d", uid)                 break             } else {                 log.Printf("Ошибка отправки SMS для UID: %d, попытка %d", uid, i+1)                 time.Sleep(5 * time.Second)             }         }     }) }  func sendSMS(to, message   string) bool {     // Здесь будет реальная логика отправки SMS     return true }

Функция scheduleSMS планирует отправку SMS через месяц, а если попытка отправки не удалась, повторяет её до трёх раз. Если все попытки провалены, система просто зафиксирует ошибку в логе.

Используем webhook для полной автоматизации

Чтобы сделать систему полностью автономной, добавим webhook. Он будет автоматом запускать анализ текста и отправку SMS, когда расшифровка разговора готова. Это избавляет от необходимости вручную запускать проверку для каждого звонка.

import (     "github.com/gin-gonic/gin"     "net/http" )  func handleWebhook(db *gorm.DB) func(c *gin.Context) {     return func(c *gin.Context) {         var payload struct {             UID uint64 `json:"uid"`         }         if err := c.BindJSON(&payload); err != nil {             c.JSON(http.StatusBadRequest, gin.H{"error": "неверный формат данных"})             return         }          text, err := getTranscription(payload.UID)         if err != nil {             log.Printf("Ошибка получения транскрибации: %v", err)             return         }          agreed := analyzeConsent(text)         if err := saveConsent(db, payload.UID, text, agreed); err != nil {             log.Printf("Ошибка сохранения согласия: %v", err)         }          if agreed {             scheduleSMS(db, payload.UID, "+79991112233", "Напоминаем о нашем предложении!")         }     } }  func main() {     db, err := initDB()     if err != nil {         log.Fatalf("Ошибка инициализации базы данных: %v", err)     }      r := gin.Default()     r.POST("/webhook", handleWebhook(db))     r.Run(":8080") }

Webhook позволяет Exolve автоматически уведомлять сервер, когда расшифровка готова.


Заключение

Вот и всё — создали простую автоматическую систему напоминаний, которая берёт на себя процесс от звонка до SMS. Эту систему легко расширить и улучшить: подключить аналитику для отслеживания конверсий, добавить улучшенный NLP для анализа сложных ответов, а также интегрировать дополнительные каналы связи.

Если хотите углубиться в детали API и узнать больше о возможностях МТС Exolve, рекомендую ознакомиться с документацией Exolve.


ссылка на оригинал статьи https://habr.com/ru/articles/861346/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *