Войти

API для создания ботов в мессенджере InterChat

Bot API платформы InterChat позволяет создавать мощные, автоматизированные интеграции для ваших пользователей без необходимости понимать базовую криптографию или управлять ей. Поскольку InterChat использует архитектуру сквозного шифрования (E2EE) с нулевым разглашением, основной узел InterChat выступает в качестве безопасного криптографического прокси для вашего бота.

Когда ваш сервер отправляет стандартный ответ обычным текстом в формате JSON, узел InterChat автоматически шифрует его с использованием выделенного мастер-ключа вашего бота перед отправкой клиенту.

1. Регистрация и настройка

Перед написанием кода вам необходимо зарегистрировать своего бота в клиентском приложении InterChat:

  1. Откройте InterChat и перейдите в Settings (Настройки) > My Bots (Мои боты).
  2. Нажмите Create Bot (Создать бота) и заполните Имя бота (Name), Имя пользователя (Username) и Описание (Description).
  3. В поле Webhook URL введите HTTPS-адрес вашего внешнего сервера (например, https://scripts.yourdomain.com/webhook).
  4. Нажмите Generate New Token (Сгенерировать новый токен). Скопируйте этот API-токен; вы будете использовать его как Bearer-токен для аутентификации ваших исходящих сообщений.
  5. Сохраните вашего бота.

2. Получение сообщений (Webhook)

Каждый раз, когда пользователь отправляет сообщение вашему боту, InterChat немедленно отправляет HTTP-запрос POST на настроенный вами URL вебхука.

Входящая полезная нагрузка (JSON Payload):

{
  "userId": "019f3c79-763a-7e60-a6ed-535febd4d0bd",
  "text": "Покажите информацию о моем заказе",
  "command": "order", 
  "timestamp": 1732049182394
}

Примечание: Поле command заполняется только в том случае, если сообщение пользователя начинается с косой черты (например, /order). В противном случае оно отсутствует.

Ваш сервер должен обработать эту полезную нагрузку и немедленно вернуть статус HTTP 200 OK для подтверждения получения.

3. Отправка ответов (API)

Чтобы отправить сообщение обратно пользователю, выполните HTTP-запрос POST на конечную точку Bot API InterChat.

Исходящая полезная нагрузка (JSON Payload):

{
  "targetUserId": "019f3c79-763a-7e60-a6ed-535febd4d0bd",
  "text": "Ваш заказ передан курьеру!",
  "inlineKeyboard": "" 
}

Примечание: inlineKeyboard принимает преобразованный в строку массив JSON, если вы хотите прикрепить к вашему сообщению интерактивные кнопки.

4. Пример сервера:

Представленный файл main.go — это автономный веб-сервер на Go без внешних зависимостей, который реализует полноценного тестового логистического бота. Он управляет состояниями бесед в оперативной памяти и демонстрирует, как анализировать вебхуки и отправлять аутентифицированные ответы.

Чтобы запустить сервер:

# Настройте переменные окружения
export INTERCHAT_API_URL="https://gobackend.interchat.co.za/bot/send-message"
export BOT_API_TOKEN="ваш_сгенерированный_токен"
export PORT="8080"

# Запустите бота
go run main.go


./main.go

package main

import (
    "bytes"
    "encoding/json"
    "io"
    "log"
    "net/http"
    "os"
    "strings"
    "sync"
    "time"
)

// InterChatWebhookPayload представляет входящие данные HTTP POST от InterChat.
type InterChatWebhookPayload struct {
    UserID    string `json:"userId"`
    Text      string `json:"text"`
    Command   string `json:"command,omitempty"`
    Timestamp int64  `json:"timestamp"`
}

// DevMessageRequest представляет необходимую структуру JSON для отправки сообщения обратно.
type DevMessageRequest struct {
    TargetUserID   string `json:"targetUserId"`
    Text           string `json:"text"`
    InlineKeyboard string `json:"inlineKeyboard"`
}

var (
    apiToken     string
    interchatURL string

    // Потокобезопасное хранилище в оперативной памяти для отслеживания сценариев общения с пользователями
    stateMutex sync.RWMutex
    userStates = make(map[string]string)
)

func main() {
    // 1. Загрузка конфигурации
    apiToken = os.Getenv("BOT_API_TOKEN")
    if apiToken == "" {
        log.Fatal("КРИТИЧЕСКАЯ ОШИБКА: переменная окружения BOT_API_TOKEN не установлена.")
    }

    interchatURL = os.Getenv("INTERCHAT_API_URL")
    if interchatURL == "" {
        // Возврат к стандартному рабочему URL, если он не задан явно
        interchatURL = "https://gobackend.interchat.co.za/bot/send-message"
    }

    port := os.Getenv("PORT")
    if port == "" {
        port = "8080"
    }

    // 2. Настройка маршрутов
    mux := http.NewServeMux()
    mux.HandleFunc("POST /webhook", handleWebhook)

    // Базовая конечная точка проверки работоспособности (health check)
    mux.HandleFunc("GET /health", func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusOK)
        w.Write([]byte("Песочница бота работает (Bot Sandbox is Online)"))
    })

    // 3. Запуск сервера
    log.Printf("🚀 Запуск песочницы бота InterChat на порту %s", port)
    log.Printf("Целевой API: %s", interchatURL)

    server := &http.Server{
        Addr:         ":" + port,
        Handler:      mux,
        ReadTimeout:  10 * time.Second,
        WriteTimeout: 10 * time.Second,
    }

    log.Fatal(server.ListenAndServe())
}

// handleWebhook перехватывает JSON-данные из маршрутизатора DispatchIfBot платформы InterChat.
func handleWebhook(w http.ResponseWriter, r *http.Request) {
    bodyBytes, err := io.ReadAll(r.Body)
    if err != nil {
        log.Printf("Ошибка чтения тела вебхука: %v", err)
        http.Error(w, "Не удалось прочитать тело запроса", http.StatusBadRequest)
        return
    }
    defer r.Body.Close()

    var payload InterChatWebhookPayload
    if err := json.Unmarshal(bodyBytes, &payload); err != nil {
        log.Printf("Ошибка распаковки (unmarshaling) JSON-данных: %v", err)
        http.Error(w, "Неверный JSON", http.StatusBadRequest)
        return
    }

    log.Printf("Получено сообщение от пользователя [%s]. Команда: [%s], Текст: [%s]", payload.UserID, payload.Command, payload.Text)

    // Немедленно подтверждаем получение, чтобы InterChat не держал соединение открытым
    w.WriteHeader(http.StatusOK)

    // Обработка бизнес-логики в асинхронном режиме
    go processConversationFlow(payload)
}

// processConversationFlow оценивает ввод пользователя и определяет подходящий ответ.
func processConversationFlow(payload InterChatWebhookPayload) {
    userText := strings.ToLower(strings.TrimSpace(payload.Text))
    var replyText string

    stateMutex.RLock()
    currentState := userStates[payload.UserID]
    stateMutex.RUnlock()

    // Бизнес-логика: Сценарий отслеживания заказа
    if currentState == "waiting_for_order_id" {
        // Имитация поиска в базе данных
        replyText = "📦 Заказ ID " + userText + " найден.\nСтатус: В пути и будет доставлен сегодня!"
        log.Printf("Сценарий завершен для пользователя %s, очистка состояния.", payload.UserID)

        stateMutex.Lock()
        delete(userStates, payload.UserID)
        stateMutex.Unlock()

    } else {
        // Оценка начальных команд
        if payload.Command == "order" {
            replyText = "Конечно! Пожалуйста, введите ваш 5-значный номер (ID) заказа:"
            log.Printf("Запуск сценария заказа для пользователя %s.", payload.UserID)

            stateMutex.Lock()
            userStates[payload.UserID] = "waiting_for_order_id"
            stateMutex.Unlock()

        } else if payload.Command == "start" {
            replyText = "Добро пожаловать в Logistics Corp! 🚚\n\nОтправьте /order, чтобы проверить статус отслеживания."
        } else {
            replyText = "Я не совсем понял. Отправьте /order для отслеживания посылки."
        }
    }

    // Отправка ответа через InterChat API
    sendReply(payload.UserID, replyText)
}

// sendReply формирует HTTP POST к InterChat, используя API-токен бота.
func sendReply(targetUserID, text string) {
    reqBody := DevMessageRequest{
        TargetUserID:   targetUserID,
        Text:           text,
        InlineKeyboard: "", // Строка в формате JSON-массива, если требуются интерактивные кнопки
    }

    jsonPayload, err := json.Marshal(reqBody)
    if err != nil {
        log.Printf("Ошибка упаковки (marshaling) исходящего сообщения: %v", err)
        return
    }

    req, err := http.NewRequest("POST", interchatURL, bytes.NewBuffer(jsonPayload))
    if err != nil {
        log.Printf("Ошибка создания исходящего HTTP-запроса: %v", err)
        return
    }

    // Безопасное добавление заголовков, требуемых функцией SendMessageAsBot
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("Authorization", "Bearer "+apiToken)

    client := &http.Client{Timeout: 10 * time.Second}
    resp, err := client.Do(req)
    if err != nil {
        log.Printf("Сетевая ошибка при отправке ответа в InterChat: %v", err)
        return
    }
    defer resp.Body.Close()

    if resp.StatusCode != http.StatusOK {
        respBytes, _ := io.ReadAll(resp.Body)
        log.Printf("Ошибка API: InterChat отклонил сообщение (Статус %d): %s", resp.StatusCode, string(respBytes))
    } else {
        log.Printf("Ответ успешно отправлен пользователю %s", targetUserID)
    }
}