Skip to content

Всё для WordPress, веб-разработки — и не только

🤖 Viber чат-бот на PHP: отправка структурированных сообщений с клавиатурным меню

🤖 Viber чат-бот на PHP: отправка структурированных сообщений с клавиатурным меню

Пользователь жмёт кнопку в чате и получает не голый текст, а сетку из картинок, ссылок и цветных клавиш. Выглядит как нативное приложение. На деле, обычный JSON, который ваш PHP-скрипт отдаёт через Viber REST API.

Проблема большинства руководств: они учат отвечать текстом на текст. Реальный бизнес-бот должен показывать клавиатурное меню с кнопками-ссылками, картинки и карусели. Иначе пользователь просто не поймёт, что бот умеет, и уйдёт. С февраля 2024 года Viber перевёл создание ботов на коммерческую основу, 100 € в месяц через официальных партнёров Rakuten Viber, а аутентификация уходит в HTTP-заголовок X-Viber-Auth-Token, а не в тело запроса. Сниппеты с auth_token внутри JSON больше не работают.

Здесь, работающий Viber-бот на PHP с нуля: от установки вебхука до отправки структурированного клавиатурного меню с кнопками reply и open-url. Код актуален под версию API 7.3.

💡 Быстрый обзор:

  • Регистрируем коммерческого бота через партнёра Viber и получаем токен аутентификации
  • Настраиваем вебхук через set_webhook с заголовком X-Viber-Auth-Token
  • Принимаем callback-события от Viber: webhook → subscribed → message
  • Формируем клавиатурное меню, сетка кнопок с кастомными цветами, размерами и действиями
  • Отправляем структурированный ответ: текст, картинки, ссылки, карусели через send_message

Как устроен Viber Bot API

Viber Bot API работает по REST-модели: ваш сервер получает callback-запросы на вебхук и отвечает вызовами к https://chatapi.viber.com/pa/send_message. Каждый запрос аутентифицируется токеном в заголовке X-Viber-Auth-Token.

Базовый цикл выглядит так:

  • Вы регистрируете бота через партнёра Viber и получаете токен в панели «Edit Info».
  • Настраиваете вебхук, URL вашего сервера с валидным SSL (Let's Encrypt подходит, он в доверенном списке Java-сертификатов Viber; самоподписанные не принимаются).
  • Viber шлёт POST-запросы на этот URL при каждом событии: пользователь подписался, написал сообщение, нажал кнопку.
  • Ваш PHP-скрипт читает входящий JSON, разбирает поле event и отвечает вызовом send_message.

Первый шаг для понимания, посмотреть на живого бота. Откройте Viber, в поиске публичных аккаунтов найдите бота любого известного бренда. Почти у каждого есть чат-бот с меню. На скриншоте ниже, типичный результат поиска публичного аккаунта.

Поиск публичного аккаунта в Viber

После подписки вы попадаете в чат 1-на-1. Выглядит примерно так: аватар, приветственное сообщение, кнопка для старта диалога.

Экран публичного чата Viber после подписки

Нажмите иконку сообщения в правом верхнем углу и отправьте «Привет». Если бот настроен на клавиатурное меню, увидите ответ с сеткой кнопок:

Клавиатурное меню с кнопками в чате Viber

Это и есть структурированное сообщение. Кнопки бывают двух типов: reply возвращают текст обратно боту (нажатие «Новости» или «Статьи»), а open-url открывают ссылку в браузере. Теперь напишем код, который такое отдаёт.

Крок 1: Получаем токен и настраиваем вебхук

Токен лежит в панели администратора Viber: раздел Edit Info → поле App Key. Это строка из букв и цифр, её вы подставляете в каждый запрос к API.

Вебхук устанавливается одним POST-запросом к https://chatapi.viber.com/pa/set_webhook. Тело запроса, JSON с URL вашего обработчика и списком событий, на которые вы подписываетесь. Заголовок, X-Viber-Auth-Token с вашим токеном.

Скрипт установки вебхука (сохраните как setup.php и запустите один раз):

1<?php
2$token = 'ВАШ_X_VIBER_AUTH_TOKEN';
3
4$data = json_encode([
5 'url' => 'https://your-domain.com/webhook.php',
6 'event_types' => ['message', 'subscribed', 'conversation_started'],
7]);
8
9$ch = curl_init('https://chatapi.viber.com/pa/set_webhook');
10curl_setopt($ch, CURLOPT_POST, 1);
11curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
12curl_setopt($ch, CURLOPT_HTTPHEADER, [
13 'Content-Type: application/json',
14 'X-Viber-Auth-Token: ' . $token,
15]);
16curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
17
18$result = curl_exec($ch);
19$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
20curl_close($ch);
21
22echo "HTTP {$httpCode}: {$result}\n";
23
24if ($httpCode === 200) {
25 $response = json_decode($result, true);
26 if (($response['status'] ?? -1) === 0) {
27 echo "Вебхук установлен успешно.\n";
28 } else {
29 echo "Ошибка Viber: " . ($response['status_message'] ?? 'неизвестная') . "\n";
30 }
31}

Что здесь происходит: cURL отправляет POST на set_webhook. Токен, строго в заголовке, не в теле. Viber возвращает {"status":0} при успехе и сразу же шлёт callback webhook на указанный URL, проверяя его доступность. Ответ {"status":0,"status_message":"ok"} на этот callback обязателен (разберём в следующем шаге).

Если получаете invalidUrl, проверьте SSL-сертификат. Let's Encrypt работает, самоподписанный нет. На своём сервере достаточно выполнить certbot --nginx -d your-domain.com и настроить автообновление через cron.

Крок 2: Принимаем callback-события от Viber

Когда пользователь взаимодействует с ботом, Viber отправляет на ваш вебхук POST-запрос с JSON. Ваша задача, прочитать поле event и отреагировать.

Минимальный обработчик webhook.php, который корректно отвечает на все события:

1<?php
2$request = file_get_contents("php://input");
3$input = json_decode($request, true);
4
5if ($input['event'] === 'webhook') {
6 $response = [
7 'status' => 0,
8 'status_message' => 'ok',
9 'event_types' => ['delivered', 'seen', 'message', 'subscribed', 'conversation_started'],
10 ];
11 echo json_encode($response);
12 exit;
13}
14
15if ($input['event'] === 'subscribed') {
16 $sender_id = $input['sender']['id'];
17 sendWelcomeMessage($sender_id);
18}
19
20if ($input['event'] === 'conversation_started') {
21 $sender_id = $input['sender']['id'];
22 sendMainMenu($sender_id);
23}
24
25if ($input['event'] === 'message') {
26 $type = $input['message']['type'];
27 $text = $input['message']['text'];
28 $sender_id = $input['sender']['id'];
29 $sender_name = $input['sender']['name'];
30
31 $data = match ($text) {
32 'News' => getNewsList($sender_id),
33 'Articles' => getArticleList($sender_id),
34 'Gallery' => getGalleryList($sender_id),
35 default => getMainMenu($sender_id),
36 };
37
38 sendToViber($data);
39}

Разбор событий:

  • webhook, прилетает один раз при установке вебхука. Обязательно вернуть {"status":0,"status_message":"ok"} с перечнем поддерживаемых событий. Без этого Viber не подтвердит URL, и бот не запустится.
  • subscribed, пользователь подписался на бота. Идеальный момент для отправки приветственного меню.
  • conversation_started, пользователь открыл чат, в первый раз или повторно. Тоже подходит для показа главного меню.
  • message, основное рабочее событие. В text лежит либо произвольное сообщение пользователя, либо ActionBody нажатой кнопки (если её тип reply). Именно так бот понимает, что выбрали: «News», «Articles» или «Gallery».

Конструкция match() доступна с PHP 8. Если у вас PHP 7.4, замените на switch.

Крок 3: Формируем клавиатурное меню

Клавиатура прикрепляется к любому сообщению через поле keyboard в JSON-объекте send_message. Это массив кнопок с раздельной настройкой ширины (Columns, 1-6), высоты (Rows, 1-2), цвета фона, текста и действия.

Функция, собирающая главное меню из семи reply-кнопок и одной кнопки-ссылки:

1<?php
2
3function getMainMenu(string $user_id): array
4{
5 $buttons = [];
6
7 $addReplyButton = function (string $label, string $actionBody, int $cols = 2, int $rows = 2) use (&$buttons) {
8 $buttons[] = [
9 'Columns' => $cols,
10 'Rows' => $rows,
11 'Text' => $label,
12 'TextSize' => 'regular',
13 'TextVAlign' => 'bottom',
14 'TextHAlign' => 'center',
15 'TextOpacity' => 100,
16 'ActionType' => 'reply',
17 'ActionBody' => $actionBody,
18 'BgColor' => '#FFFFFF',
19 ];
20 };
21
22 $addReplyButton('NEWS', 'News');
23 $addReplyButton('ARTICLES', 'Articles');
24 $addReplyButton('INTERVIEWS', 'Interviews');
25 $addReplyButton('GALLERY', 'Gallery');
26 $addReplyButton('POLL', 'Poll');
27 $addReplyButton('PLAYER OF THE MONTH','POTM');
28 $addReplyButton('QUOTE OF THE DAY', 'Quote');
29
30 $buttons[] = [
31 'Columns' => 4,
32 'Rows' => 2,
33 'Text' => 'VISIT OUR WEBSITE',
34 'TextSize' => 'regular',
35 'TextVAlign' => 'bottom',
36 'TextHAlign' => 'center',
37 'TextOpacity' => 100,
38 'ActionType' => 'open-url',
39 'ActionBody' => 'https://your-site.com',
40 'BgColor' => '#FFFFFF',
41 ];
42
43 return [
44 'receiver' => $user_id,
45 'type' => 'text',
46 'text' => 'Please select one of the options below:',
47 'keyboard' => [
48 'Type' => 'keyboard',
49 'BgColor' => '#FFFFFF',
50 'Buttons' => $buttons,
51 ],
52 ];
53}

Кнопки с Columns=2 и Rows=2, квадратные, по три в ряд (2+2+2=6). Кнопка-ссылка с Columns=4 занимает отдельную строку. Сетка собирается слева направо, сверху вниз. Сумма Columns в ряду определяет раскладку.

Параметры кнопки:

Параметр

Значения

Назначение

Columns

1-6

Ширина кнопки в условных колонках

Rows

1-2

Высота кнопки в рядах

ActionType

reply или open-url

reply - вернуть текст боту, open-url - открыть браузер

ActionBody

строка

Для reply - текст, который бот получит обратно; для open-url - URL

Text

строка

Надпись на кнопке, поддерживает HTML-теги b, i и font color

BgColor

HEX

Фоновый цвет кнопки

Image

URL

Картинка поверх кнопки, JPEG, опционально

Крок 4: Отправляем данные через send_message

Функция-отправитель, которую вы вызываете из обработчика событий:

1<?php
2
3function sendToViber(array $data): void
4{
5 $token = 'ВАШ_X_VIBER_AUTH_TOKEN';
6
7 $ch = curl_init('https://chatapi.viber.com/pa/send_message');
8 curl_setopt($ch, CURLOPT_POST, 1);
9 curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
10 curl_setopt($ch, CURLOPT_HTTPHEADER, [
11 'Content-Type: application/json',
12 'X-Viber-Auth-Token: ' . $token,
13 ]);
14 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
15
16 $result = curl_exec($ch);
17 $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
18 curl_close($ch);
19
20 if ($httpCode !== 200) {
21 error_log("Viber API error: HTTP {$httpCode} - {$result}");
22 }
23}

Ключевое отличие от старых руководств: токен в заголовке, а не в теле JSON. В теле только поля receiver, type, text, keyboard и опциональные sender.name, sender.avatar, tracking_data. Максимальный размер JSON: 30 КБ. Превысили, и Viber молча отбрасывает сообщение, проверяйте strlen(json_encode($data)) перед отправкой.

Крок 5: Собираем контент для кнопок

Когда пользователь нажимает reply-кнопку, её ActionBody улетает на вебхук как текст сообщения. Вы парсите его в match() и вызываете нужную функцию. Вот пример для списка новостей с картинками:

1<?php
2
3function getNewsList(string $user_id): array
4{
5 $newsItems = [
6 [
7 'title' => 'Your Article Title',
8 'link' => 'https://your-site.com/article-1',
9 'image' => 'https://your-site.com/images/article-1.jpg',
10 ],
11 [
12 'title' => 'Another Article',
13 'link' => 'https://your-site.com/article-2',
14 'image' => 'https://your-site.com/images/article-2.jpg',
15 ],
16 ];
17
18 $buttons = [];
19 foreach ($newsItems as $item) {
20 $buttons[] = [
21 'Columns' => 2,
22 'Rows' => 2,
23 'ActionType' => 'open-url',
24 'ActionBody' => $item['link'],
25 'BgColor' => '#FFFFFF',
26 'Image' => $item['image'],
27 ];
28 $buttons[] = [
29 'Columns' => 4,
30 'Rows' => 2,
31 'Text' => $item['title'],
32 'TextSize' => 'regular',
33 'TextHAlign' => 'left',
34 'TextVAlign' => 'top',
35 'ActionType' => 'open-url',
36 'ActionBody' => $item['link'],
37 'BgColor' => '#F5F5F5',
38 ];
39 }
40
41 return [
42 'receiver' => $user_id,
43 'type' => 'text',
44 'text' => 'Here are the latest updates:',
45 'keyboard' => [
46 'Type' => 'keyboard',
47 'BgColor' => '#DDDDDD',
48 'Buttons' => $buttons,
49 ],
50 ];
51}

На стороне пользователя это выглядит как сетка карточек: слева картинка, справа заголовок. Нажатие на любую часть открывает ссылку в браузере.

Rich-сообщение с картинками и ссылками в Viber

Точно так же собираются галерея, список статей или опросы, меняется только содержимое массива $buttons и текст сообщения. Если контента много и JSON приближается к 30 КБ, разбивайте на страницы с кнопкой «Next».

Если вам удобнее работать не с чистым API, а с готовой PHP-библиотекой, посмотрите viber-bot-php от Bogdaan, она берёт на себя валидацию подписи, роутинг событий и формирование JSON.

Коротко: полный порядок запуска

  • Получите токен в панели Viber Admin Panel: раздел Edit Info → App Key.
  • Выложите обработчик на сервер с HTTPS. Let's Encrypt подходит, самоподписанный сертификат, нет. Viber сверяет сертификат с доверенным списком Java Root CA.
  • Установите вебхук POST-запросом к https://chatapi.viber.com/pa/set_webhook с телом {"url":"https://ваш-домен/webhook.php","event_types":[...]} и заголовком X-Viber-Auth-Token.
  • Проверьте ответ: {"status":0}, успех. Viber тут же пришлёт callback webhook на ваш URL. Убедитесь, что скрипт возвращает {"status":0,"status_message":"ok"}.
  • Отправьте первое меню: при событии conversation_started вызовите getMainMenu() и передайте результат в sendToViber().

⁉️🤔 Частые вопросы

Можно ли использовать самоподписанный SSL-сертификат для вебхука?

Нет. Viber проверяет сертификат по списку доверенных корневых центров сертификации Java. Самоподписанный сертификат вызывает ошибку invalidUrl при установке вебхука. Let's Encrypt в этом списке есть и бесплатен. На своём сервере выполните certbot --nginx -d your-domain.com, добавьте автообновление в cron, и Viber примет сертификат без проблем.

Чем отличается reply от open-url в ActionType?

reply отправляет ActionBody обратно на вебхук как текст сообщения. PHP-скрипт видит его в $input['message']['text'] и маршрутизирует через match(). Используйте для навигации внутри бота. open-url открывает ссылку во внешнем браузере, вебхук при этом не дёргается. На практике меню комбинирует оба типа: 5-6 reply-кнопок для разделов и одну широкую open-url-кнопку для перехода на сайт.

Какие типы сообщений поддерживает Viber Bot API?

Viber Bot API поддерживает девять типов поля type в send_message: text (обычный текст), picture (JPEG-изображение), video (видеофайл), file (любой файл до 50 МБ), location (геоточка), contact (визитка), sticker (стикер), rich_media (карусель карточек) и url (превью ссылки). Актуальный список с обязательными полями смотрите в документации Viber Developers Hub. Самый ходовой сценарий: text + keyboard для меню и picture для отправки изображений. Карусели хороши для витрин и каталогов, но требуют больше кода.

Что делать, если пользователь не видит клавиатуру?

Три частые причины. Первая: JSON с клавиатурой превысил 30 КБ, Viber молча отбрасывает такие сообщения. Проверяйте strlen(json_encode($data)) перед отправкой. Вторая: в ответе на set_webhook не было "status":0, вебхук не установлен, бот не получает сообщения. Третья: используется старый формат с auth_token в теле JSON. С API 7.0 это игнорируется, нужен заголовок X-Viber-Auth-Token. Для отладки включите логирование ответа Viber API: он возвращает читаемый JSON с кодом ошибки invalidAuthToken, badData или missingData.

Можно ли поменять клавиатуру после отправки?

Да. При каждом новом сообщении можно отправить другую клавиатуру. Клиент Viber всегда показывает последнюю полученную. Это позволяет строить многоуровневые меню: главное меню → подменю раздела → конкретный контент. Каждый уровень со своим набором кнопок, и пользователь перемещается между ними нажатиями reply-клавиш.

Сколько стоит запуск Viber-бота в 2026 году?

С 5 февраля 2024 года создание нового бота, 100 € в месяц через официальных партнёров Rakuten Viber. Боты, созданные до этой даты, работают по старым условиям. В стоимость входит выделенный аккаунт с панелью администратора, аналитика и доступ ко всем типам сообщений, включая rich_media и Viber Pay. Техническая часть API для коммерческих и старых бесплатных ботов идентична.

Бот готов: что дальше

Клавиатурное меню, только входная точка. Когда базовая механика отработана, добавляйте карусели контента через rich_media, платежи через Viber Pay и сегментацию пользователей по тому, какие кнопки они нажимают чаще всего. Каждый новый уровень не меняет основу: вы так же читаете event, так же шлёте send_message с заголовком X-Viber-Auth-Token.

Если нужен живой пример работающего кода, посмотрите репозиторий viber-bot-php на GitHub, там разобраны все события, подпись запросов и формирование клавиатуры с картинками. А для визуального старта вот получасовой туториал по созданию Viber-бота с нуля:

Главное, что стоит запомнить: Viber Bot API прост на старте, но требователен к деталям. Правильный заголовок аутентификации, доверенный SSL, валидный JSON до 30 КБ и чёткая маршрутизация ActionBody, этого достаточно, чтобы бот работал без сбоев. Берите сниппеты выше, подставляйте свой токен и домен, и запускайте первое меню сегодня.