+7(921)-851-18-76 Иконка телефона Запросить звонок
Опубликовано:

Спасаем бизнес: Создаём VK-бота на PHP, Python, JS и C#.
Часть 2 — Учим бота отвечать на сообщения

В прошлой части мы подготовили площадку: создали сообщество, получили ключи API, настроили Callback-сервер и подтвердили его. Наш бот уже умеет принимать запросы от VK и честно отвечать ok на все события. Но пока он глух и нем — на message_new он просто кивает, но ничего не делает.
Пришло время это исправить.

Что будем делать

В этой части мы научим бота:

  • Распознавать входящие сообщения (событие message_new).

  • Отправлять ответ обратно пользователю.

  • Обрабатывать базовые команды, например /start или «Привет».

Мы по-прежнему будем давать примеры на четырёх языках, так что вы сможете выбрать свой стек.

Включаем нужные события

Прежде чем писать код, нужно сказать VK, какие события нас интересуют. Зайдите в управление сообществом → «Работа с API» → вкладка «Типы событий».

Убедитесь, что напротив события «Входящее сообщение» стоит галочка:

Разрешение чтения входящих сообщений

Выбираем пока только эту часть, остальные галочки оставляем пустыми

Теперь VK будет присылать на ваш сервер уведомления с типом message_new каждый раз, когда пользователь пишет боту.

Логика обработки

Принцип остаётся тем же, что и в первой части: мы получаем POST-запрос с JSON, смотрим на поле type и, если это message_new, извлекаем из объекта данные (текст сообщения, ID пользователя) и отправляем ответ.
Ответ отправляется отдельным запросом к VK API но об этом позже.

Пример данных, которые отправляет VK API по событию сообщения:


{
  "type": "тип отправленного события",
  "event_id": "идентификатор события",
  "v": "версия API, под которую событие сформировано",
  "object": "объект, вызвавший событие",
  "group_id": "Идентификатор сообщества, в котором произошло событие"
}
        

Вот так могут выглядеть данные при событии нового сообщения:


{               
    "group_id": 111111111,
    "type": "message_new",
    "event_id": "067ba91c03cfd888532208794a257f95a34dad0b",
    "v": "5.199",
    "object": {
        "client_info": {
            "button_actions": [
                "text",
                "vkpay",
                "open_app",
                "location",
                "open_link",
                "open_photo",
                "callback",
                "intent_subscribe",
                "intent_unsubscribe"
            ],
            "keyboard": true,
            "inline_keyboard": true,
            "carousel": true,
            "lang_id": 0
        },
        "message": {
            "date": 1773940815,
            "from_id": 111111111,
            "id": 5,
            "version": 10000020,
            "out": 0,
            "fwd_messages": [],
            "important": false,
            "is_hidden": false,
            "attachments": [],
            "conversation_message_id": 2,
            "text": "А вот здесь будет текст нашего сообщения",
            "peer_id": 111111111,
            "random_id": 0
        }
    },
    "secret": "Об этом поговорим позже"
}
        

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

Так как способы и пути обработки разных событий - разные, начнем с простого, с того что нам точно будет необходимо - составим функцию ответа в чат:


// $peerId мы забираем из данных
// -> object -> message -> peer_id
function sendMessage($peerId, $message)
{
    // Собираем данные
    $params = [
        'peer_id' => $peerId,
        'message' => $message,
        'access_token' => ACCESS_TOKEN,
        'v' => VERSION,
        'random_id' => rand(1, 1000000) // обязательно для отправки 
    ];
    // Формируем URL
    $url = ENDPOINT . 'messages.send?' . http_build_query($params);

    // Отправляем запрос на API
    $response = file_get_contents($url);

    // Возвращаем распаршенный json
    return json_decode($response);
}
      

# peer_id мы забираем из данных
# -> object -> message -> peer_id
def send_message(peer_id, message):
    # Собираем данные
    params = {
        'peer_id': peer_id,
        'message': message,
        'access_token': ACCESS_TOKEN,
        'v': VERSION,
        'random_id': random.randint(1, 1000000)  # обязательно для отправки
    }
    
    # Формируем URL
    url = ENDPOINT + 'messages.send'
    
    # Отправляем запрос на API
    response = requests.get(url, params=params)

    # Возвращаем распаршенный json
    return response.json()
      

// peer_id мы забираем из данных
// -> object -> message -> peer_id
async function sendMessage(peerId, message) {
  // Собираем данные
  const params = {
    peer_id: peerId,
    message: message,
    access_token: ACCESS_TOKEN,
    v: VERSION,
    random_id: Math.floor(Math.random() * 1000000) + 1, // обязательно для отправки
  };

  // Формируем URL
  const url = ENDPOINT + "messages.send?" + new URLSearchParams(params).toString();

  // Отправляем запрос на API
  const response = await fetch(url);

  // Возвращаем распаршенный json
  return response.json();
}
      

public static async Task<VkResponse?> SendMessage(long peerId, string message)
{
    // Собираем данные
    var parameters = new Dictionary<string, string>
    {
        ["peer_id"] = peerId.ToString(),
        ["message"] = message,
        ["access_token"] = ACCESS_TOKEN,
        ["v"] = VERSION,
        ["random_id"] = new Random().Next(1, 1000000).ToString() // обязательно для отправки
    };

    // Формируем URL
    string url = ENDPOINT + "messages.send?" + string.Join("&",
        parameters.Select(p => $"{HttpUtility.UrlEncode(p.Key)}={HttpUtility.UrlEncode(p.Value)}"));

    // Отправляем запрос на API
    HttpClient httpClient = new HttpClient();
    HttpResponseMessage response = await httpClient.GetAsync(url);
    
    // Возвращаем распаршенный json
    return await response.Content.ReadFromJsonAsync<VkResponse>();
}
// И так как это C#, нам понадобится куча классов для типизации с промаппленными полями:
public class VkRequest
{
    public string? Type { get; set; }
    [JsonPropertyName("event_id")]
    public string? EventId { get; set; }
    [JsonPropertyName("group_id")]
    public long? GroupId { get; set; }
    [JsonPropertyName("v")]
    public string? V { get; set; }
    [JsonPropertyName("object")]
    public VkRequestObject? Object { get; set; }
}

public class VkRequestObject
{
    [JsonPropertyName("client_info")]
    public VkRequestObjectClientInfo? ClientInfo { get; set; }
    [JsonPropertyName("message")]
    public VkRequestObjectMessage? Message { get; set; }
}
public class VkRequestObjectClientInfo
{
    [JsonPropertyName("button_actions")]
    public string[]? ButtonActions { get; set; }
    [JsonPropertyName("keyboard")]
    public bool Keyboard { get; set; }
    [JsonPropertyName("inline_keyboard")]
    public bool InlineKeyboard { get; set; }
    [JsonPropertyName("carousel")]
    public bool Carousel { get; set; }
    [JsonPropertyName("lang_id")]
    public long LangId { get; set; }
}
public class VkRequestObjectMessage
{
    [JsonPropertyName("date")]
    public long Date { get; set; }
    [JsonPropertyName("from_id")]
    public long FromId { get; set; }
    [JsonPropertyName("id")]
    public long Id { get; set; }
    [JsonPropertyName("version")]
    public long MessageVersion { get; set; }
    [JsonPropertyName("out")]
    public long Outgoing { get; set; }
    [JsonPropertyName("fwd_messages")]
    public string[]? FwdMessages { get; set; }
    [JsonPropertyName("important")]
    public bool Important { get; set; }
    [JsonPropertyName("is_hidden")]
    public bool IsHidden { get; set; }
    [JsonPropertyName("attachments")]
    public string[]? Attachments { get; set; }
    [JsonPropertyName("conversation_message_id")]
    public long ConversationMessageId { get; set; }
    [JsonPropertyName("text")]
    public string? Text { get; set; }
    [JsonPropertyName("peer_id")]
    public long PeerId { get; set; }
    [JsonPropertyName("random_id")]
    public long RandomId { get; set; }
}

public class VkResponse
{
    [JsonPropertyName("response")]
    public long Response { get; set; }
}
      

Вызываем функцию отправки

Теперь, когда у нас есть готовая функция sendMessage, которая умеет отправлять ответы через VK API, осталось лишь применить её в нужном месте — внутри обработчика события message_new.

Вспомним логику: VK присылает POST-запрос с JSON, в котором поле type указывает тип события. Если это message_new, мы должны:

  1. Извлечь peer_id — идентификатор чата или пользователя, от которого пришло сообщение (он лежит в object.message.peer_id).

  2. Вызвать нашу функцию sendMessage, передав ей этот peer_id и текст ответа.

  3. Вернуть VK правильный HTTP-ответ: как и обозначили в первой части, для события confirmation — только токен, для всех остальных — "ok" с кодом 200.

Важно не перепутать: функция отправки сообщения — это отдельный запрос к API VK, а ответ на callback-запрос должен быть быстрым и всегда возвращать "ok" (кроме confirmation). Это гарантирует, что VK не будет повторно присылать одно и то же событие.

Теперь переходим к полным примерам обработчиков, в которые уже встроен вызов sendMessage:


switch ($data['type']) {
    case 'confirmation':
        echo CONFIRMATION_TOKEN;
        exit();
        
        
    // Добавляем обработку нового сообщения
    case 'message_new':
        sendMessage($data['object']['message']['peer_id'], 'Сообщение с PHP сервера');

        http_response_code(200);
        exit('ok');
        
        
    default:
        http_response_code(200);
        exit('ok');
}
      

@app.route('/', methods=['POST'])
def callback():
    data = request.get_json(force=True, silent=True)
    if data is None or 'type' not in data:
        return 'ok', 200

    if data['type'] == 'confirmation':
        return CONFIRMATION_TOKEN, 200
        
        
    # Добавляем обработку нового сообщения
    if data['type'] == 'message_new':
        send_message(data['object']['message']['peer_id'], 'Сообщение с Python сервера')
        return 'ok', 200
        
        
    return 'ok', 200
      

app.post("/", (req, res) => {
  const data = req.body;

  if (!data || !data.type) {
    return res.status(200).send("ok");
  }

  switch (data.type) {
    case "confirmation":
      return res.send(CONFIRMATION_TOKEN);
        
        
    // Добавляем обработку нового сообщения
    case "message_new":
      sendMessage(data.object.message["peer_id"], "Сообщение с JS сервера");
      return res.status(200).send("ok");
        
        
    default:
      return res.status(200).send("ok");
  }
});
      

app.MapPost("/", async (HttpContext context) =>
{
    VkRequest? data = null;
    try
    {
        data = await context.Request.ReadFromJsonAsync<VkResponse?>();
    }
    catch (Exception)
    {
        return Results.Ok("ok");
    }
    if (data is null) return Results.Ok("ok");

    switch (data.Type)
    {
        case "confirmation":
            return Results.Ok(CONFIRMATION_TOKEN);
        
        
        // Добавляем обработку нового сообщения
        case "message_new":
            await SendMessage(data.Object.Message.PeerId, "Сообщение с C# сервера");
            return Results.Ok("ok");
        
        
        default:
            return Results.Ok("ok");
    }
});
      

База готова, теперь бот получает тексты сообщений в чатах и может на них ответить:

Текстовые сообщения от ботов

Реакция бота на разных серверах на сообщение в чате

Заключение: фундамент заложен — переходим к логике

В этой части мы реализовали ключевую функцию любого диалогового бота - отправку ответных сообщений. Теперь ваше приложение корректно обрабатывает входящие события message_new, извлекает идентификатор диалога и отправляет через API VK осмысленный ответ.

В следующей, третьей части мы сосредоточимся на построении диалоговой логики и повышении безопасности взаимодействия с VK API. Оставайтесь с нами, чтобы превратить базовую заготовку в полноценный инструмент для бизнеса.

Начни свой проект -который изметит все

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

Оставьтезаявку
Кнопка закрытия формы
Стрелка вверх
Все получилось Заявкауспешно
отправлена
Спасибо, что доверяете нам! Наш специалист уже получил вашу заявку
и скоро свяжется с вами.
Кнопка закрытия формы