Skip to content

Wszystko o WordPressie, tworzeniu stron — i nie tylko

🤖 Viber chatbot w PHP: wysyłanie ustrukturyzowanych wiadomości z menu klawiatury

🤖 Viber chatbot w PHP: wysyłanie ustrukturyzowanych wiadomości z menu klawiatury

Użytkownik klika przycisk na czacie i zamiast surowego tekstu otrzymuje siatkę obrazków, linków i kolorowych klawiszy. Wygląda to jak natywna aplikacja. W rzeczywistości to zwykły JSON, który Pana/Pani skrypt PHP zwraca przez Viber REST API.

Problem większości poradników: uczą odpowiadać tekstem na tekst. Prawdziwy bot biznesowy musi pokazywać menu klawiaturowe z przyciskami-linkami, obrazki i karuzele. W przeciwnym razie użytkownik po prostu nie zrozumie, co bot potrafi, i odejdzie. Od lutego 2024 roku Viber przeniósł tworzenie botów na zasadach komercyjnych, 100 € miesięcznie przez oficjalnych partnerów Rakuten Viber, a uwierzytelnianie przechodzi do nagłówka HTTP X-Viber-Auth-Token, a nie do treści żądania. Fragmenty kodu z auth_token wewnątrz JSON-a już nie działają.

Oto działający bot Viber w PHP od zera: od ustawienia webhooka do wysłania ustrukturyzowanego menu klawiaturowego z przyciskami reply i open-url. Kod jest aktualny dla wersji API 7.3.

💡 Szybki przegląd:

  • Rejestrujemy komercyjnego bota przez partnera Viber i otrzymujemy token uwierzytelniający
  • Konfigurujemy webhook przez set_webhook z nagłówkiem X-Viber-Auth-Token
  • Odbieramy zdarzenia callback od Viber: webhook → subscribed → message
  • Tworzymy menu klawiaturowe, siatkę przycisków z niestandardowymi kolorami, rozmiarami i akcjami
  • Wysyłamy ustrukturyzowaną odpowiedź: tekst, obrazki, linki, karuzele przez send_message

Jak działa Viber Bot API

Viber Bot API działa w modelu REST: Pana/Pani serwer otrzymuje żądania callback na webhook i odpowiada wywołaniami do https://chatapi.viber.com/pa/send_message. Każde żądanie jest uwierzytelniane tokenem w nagłówku X-Viber-Auth-Token.

Podstawowy cykl wygląda następująco:

  • Rejestruje Pan/Pani bota przez partnera Viber i otrzymuje token w panelu „Edit Info".
  • Konfiguruje Pan/Pani webhook, URL Pana/Pani serwera z ważnym SSL (Let's Encrypt jest odpowiedni, znajduje się na liście zaufanych certyfikatów Java Viber; samopodpisane nie są akceptowane).
  • Viber wysyła żądania POST na ten URL przy każdym zdarzeniu: użytkownik zasubskrybował, napisał wiadomość, kliknął przycisk.
  • Pana/Pani skrypt PHP odczytuje przychodzący JSON, analizuje pole event i odpowiada wywołaniem send_message.

Pierwszy krok do zrozumienia to spojrzeć na żywego bota. Proszę otworzyć Viber, w wyszukiwarce kont publicznych znaleźć bota dowolnej znanej marki. Prawie każdy ma chatbota z menu. Na zrzucie ekranu poniżej typowy wynik wyszukiwania konta publicznego.

Wyszukiwanie konta publicznego w Viberze

Po subskrypcji trafia Pan/Pani na czat 1-na-1. Wygląda to mniej więcej tak: awatar, wiadomość powitalna, przycisk do rozpoczęcia dialogu.

Ekran czatu publicznego Viber po subskrypcji

Proszę kliknąć ikonę wiadomości w prawym górnym rogu i wysłać „Cześć". Jeśli bot jest skonfigurowany z menu klawiaturowym, zobaczy Pan/Pani odpowiedź z siatką przycisków:

Menu klawiatury z przyciskami w czacie Viber

To jest właśnie ustrukturyzowana wiadomość. Przyciski są dwóch typów: reply zwracają tekst z powrotem do bota (kliknięcie „Aktualności" lub „Artykuły"), a open-url otwierają link w przeglądarce. Teraz napiszemy kod, który to generuje.

Krok 1: Otrzymujemy token i konfigurujemy webhook

Token znajduje się w panelu administratora Viber: sekcja Edit Info → pole App Key. To ciąg liter i cyfr, który wstawia Pan/Pani do każdego żądania API.

Webhook ustawia się jednym żądaniem POST do https://chatapi.viber.com/pa/set_webhook. Treść żądania to JSON z URL-em Pana/Pani handlera i listą zdarzeń, które Pan/Pani subskrybuje. Nagłówek to X-Viber-Auth-Token z Pana/Pani tokenem.

Skrypt instalacji webhooka (proszę zapisać jako setup.php i uruchomić raz):

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}

Co tu się dzieje: cURL wysyła POST do set_webhook. Token, ściśle w nagłówku, nie w treści. Viber zwraca {"status":0} w przypadku sukcesu i natychmiast wysyła callback webhook na wskazany URL, sprawdzając jego dostępność. Odpowiedź {"status":0,"status_message":"ok"} na ten callback jest obowiązkowa (omówimy to w następnym kroku).

Jeśli otrzymuje Pan/Pani invalidUrl, proszę sprawdzić certyfikat SSL. Let's Encrypt działa, samopodpisany nie. Na swoim serwerze wystarczy wykonać certbot --nginx -d your-domain.com i skonfigurować automatyczne odnawianie przez cron.

Krok 2: Odbieramy zdarzenia callback od Viber

Gdy użytkownik wchodzi w interakcję z botem, Viber wysyła na Pana/Pani webhook żądanie POST z JSON-em. Pana/Pani zadaniem jest odczytanie pola event i zareagowanie.

Minimalny handler webhook.php, który poprawnie odpowiada na wszystkie zdarzenia:

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}

Analiza zdarzeń:

  • webhook, przychodzi raz podczas ustawiania webhooka. Należy obowiązkowo zwrócić {"status":0,"status_message":"ok"} z listą obsługiwanych zdarzeń. Bez tego Viber nie potwierdzi URL-a i bot nie wystartuje.
  • subscribed, użytkownik zasubskrybował bota. Idealny moment na wysłanie menu powitalnego.
  • conversation_started, użytkownik otworzył czat, po raz pierwszy lub ponownie. Również nadaje się do wyświetlenia menu głównego.
  • message, główne zdarzenie robocze. W text znajduje się albo dowolna wiadomość użytkownika, albo ActionBody wciśniętego przycisku (jeśli jego typ to reply). Właśnie w ten sposób bot rozumie, co wybrano: „News", „Articles" czy „Gallery".

Konstrukcja match() jest dostępna od PHP 8. Jeśli korzystają Państwo z PHP 7.4, proszę zastąpić ją switch.

Krok 3: Tworzymy menu klawiaturowe

Klawiaturę dołącza się do dowolnej wiadomości przez pole keyboard w obiekcie JSON send_message. Jest to tablica przycisków z osobnym ustawieniem szerokości (Columns, 1-6), wysokości (Rows, 1-2), koloru tła, tekstu i akcji.

Funkcja budująca menu główne z siedmioma przyciskami reply i jednym przyciskiem-linkiem:

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}

Przyciski z Columns=2 i Rows=2, kwadratowe, po trzy w rzędzie (2+2+2=6). Przycisk-link z Columns=4 zajmuje osobny wiersz. Siatka jest układana od lewej do prawej, z góry na dół. Suma Columns w rzędzie określa układ.

Parametry przycisku:

Parametr

Wartość

Przeznaczenie

Columns

1-6

Szerokość przycisku w umownych kolumnach

Rows

1-2

Wysokość przycisku w rzędach

ActionType

reply lub open-url

reply, zwraca tekst do bota, open-url, otwiera przeglądarkę

ActionBody

ciąg znaków

Dla reply, tekst, który bot otrzyma z powrotem; dla open-url, URL

Text

ciąg znaków

Napis na przycisku, obsługuje znaczniki HTML b, i oraz font color

BgColor

HEX

Kolor tła przycisku

Image

URL

Obrazek na przycisku, JPEG, opcjonalnie

Krok 4: Wysyłamy dane przez send_message

Funkcja wysyłająca, którą wywołują Państwo z handlera zdarzeń:

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}

Kluczowa różnica w stosunku do starszych poradników: token w nagłówku, a nie w treści JSON. W treści tylko pola receiver, type, text, keyboard oraz opcjonalne sender.name, sender.avatar, tracking_data. Maksymalny rozmiar JSON-a: 30 KB. Po przekroczeniu Viber po cichu odrzuca wiadomość, proszę sprawdzać strlen(json_encode($data)) przed wysłaniem.

Krok 5: Budujemy treść dla przycisków

Gdy użytkownik naciśnie przycisk reply, jego ActionBody trafia na webhook jako tekst wiadomości. Parsują go Państwo w match() i wywołują odpowiednią funkcję. Oto przykład dla listy newsów z obrazkami:

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}

Z perspektywy użytkownika wygląda to jak siatka kart: po lewej obrazek, po prawej nagłówek. Kliknięcie w dowolną część otwiera link w przeglądarce.

Wiadomość rich z obrazkami i linkami w Viberze

W ten sam sposób buduje się galerię, listę artykułów czy ankietę, zmienia się tylko zawartość tablicy $buttons i tekst wiadomości. Jeśli treści jest dużo i JSON zbliża się do 30 KB, proszę podzielić ją na strony z przyciskiem „Next".

Jeśli wygodniej pracuje się Państwu nie z czystym API, a z gotową biblioteką PHP, proszę spojrzeć na viber-bot-php od Bogdaan, bierze ona na siebie walidację podpisu, routing zdarzeń i tworzenie JSON-a.

Krótko: pełna kolejność uruchamiania

  • Proszę uzyskać token w panelu Viber Admin Panel: sekcja Edit Info → App Key.
  • Proszę umieścić handler na serwerze z HTTPS. Let's Encrypt jest odpowiedni, certyfikat z podpisem własnym nie. Viber weryfikuje certyfikat z zaufaną listą Java Root CA.
  • Proszę ustawić webhook żądaniem POST do https://chatapi.viber.com/pa/set_webhook z treścią {"url":"https://ваш-домен/webhook.php","event_types":[...]} i nagłówkiem X-Viber-Auth-Token.
  • Proszę sprawdzić odpowiedź: {"status":0}, sukces. Viber natychmiast wyśle callback webhook na Pana/Pani URL. Proszę upewnić się, że skrypt zwraca {"status":0,"status_message":"ok"}.
  • Proszę wysłać pierwsze menu: przy zdarzeniu conversation_started proszę wywołać getMainMenu() i przekazać wynik do sendToViber().

⁉️🤔 Często zadawane pytania

Czy można użyć certyfikatu SSL z podpisem własnym dla webhooka?

Nie. Viber sprawdza certyfikat według listy zaufanych głównych urzędów certyfikacji Java. Certyfikat z podpisem własnym powoduje błąd invalidUrl podczas ustawiania webhooka. Let's Encrypt znajduje się na tej liście i jest bezpłatny. Na swoim serwerze proszę wykonać certbot --nginx -d your-domain.com, dodać automatyczne odnowienie w cronie, a Viber zaakceptuje certyfikat bez problemów.

Czym różni się reply od open-url w ActionType?

reply wysyła ActionBody z powrotem na webhook jako tekst wiadomości. Skrypt PHP widzi go w $input['message']['text'] i routuje przez match(). Proszę używać do nawigacji wewnątrz bota. open-url otwiera link w zewnętrznej przeglądarce, webhook nie jest przy tym wywoływany. W praktyce menu łączy oba typy: 5-6 przycisków reply do sekcji i jeden szeroki przycisk open-url do przejścia na stronę.

Jakie typy wiadomości obsługuje Viber Bot API?

Viber Bot API obsługuje dziewięć typów pola type w send_message: text (zwykły tekst), picture (obraz JPEG), video (plik wideo), file (dowolny plik do 50 MB), location (punkt geograficzny), contact (wizytówka), sticker (naklejka), rich_media (karuzela kart) i url (podgląd linku). Aktualną listę z wymaganymi polami można znaleźć w dokumentacji Viber Developers Hub. Najczęstszy scenariusz: text + keyboard do menu oraz picture do wysyłania obrazów. Karuzele są dobre do witryn sklepowych i katalogów, ale wymagają więcej kodu.

Co zrobić, jeśli użytkownik nie widzi klawiatury?

Trzy częste przyczyny. Pierwsza: JSON z klawiaturą przekroczył 30 KB, Viber po cichu odrzuca takie wiadomości. Proszę sprawdzać strlen(json_encode($data)) przed wysłaniem. Druga: w odpowiedzi na set_webhook nie było "status":0, webhook nie jest ustawiony, bot nie otrzymuje wiadomości. Trzecia: używany jest stary format z auth_token w treści JSON. Od API 7.0 jest to ignorowane, wymagany jest nagłówek X-Viber-Auth-Token. W celu debugowania proszę włączyć logowanie odpowiedzi Viber API: zwraca on czytelny JSON z kodem błędu invalidAuthToken, badData lub missingData.

Czy można zmienić klawiaturę po wysłaniu?

Tak. Z każdą nową wiadomością można wysłać inną klawiaturę. Klient Viber zawsze pokazuje ostatnią otrzymaną. Pozwala to budować wielopoziomowe menu: menu główne → podmenu sekcji → konkretna treść. Każdy poziom z własnym zestawem przycisków, a użytkownik przemieszcza się między nimi, naciskając klawisze reply.

Ile kosztuje uruchomienie bota Viber w 2026 roku?

Od 5 lutego 2024 roku utworzenie nowego bota to 100 € miesięcznie za pośrednictwem oficjalnych partnerów Rakuten Viber. Boty utworzone przed tą datą działają na starych warunkach. W koszt wchodzi dedykowane konto z panelem administratora, analityka oraz dostęp do wszystkich typów wiadomości, w tym rich_media i Viber Pay. Część techniczna API dla botów komercyjnych i starych bezpłatnych jest identyczna.

Bot gotowy: co dalej

Menu klawiaturowe to tylko punkt wejścia. Gdy podstawowa mechanika jest już opanowana, proszę dodawać karuzele treści przez rich_media, płatności przez Viber Pay i segmentację użytkowników według tego, które przyciski naciskają najczęściej. Każdy nowy poziom nie zmienia podstawy: nadal czyta Pan/Pani event, nadal wysyła send_message z nagłówkiem X-Viber-Auth-Token.

Jeśli potrzebny jest działający przykład kodu, proszę zajrzeć do repozytorium viber-bot-php na GitHub, gdzie rozpisane są wszystkie zdarzenia, podpisywanie żądań i tworzenie klawiatury z obrazkami. A na wizualny start, oto półgodzinny tutorial z tworzenia bota Viber od podstaw:

Najważniejsze do zapamiętania: Viber Bot API jest prosty na starcie, ale wymagający co do szczegółów. Prawidłowy nagłówek uwierzytelniania, zaufany SSL, poprawny JSON do 30 KB i przejrzysty routing ActionBody, to wystarczy, aby bot działał bez zakłóceń. Proszę wziąć snippety powyżej, podstawić swój token i domenę i uruchomić pierwsze menu już dziś.