Skip to content

Tudo para WordPress, desenvolvimento web — e não só

🤖 Chatbot Viber em PHP: envio de mensagens estruturadas com menus de teclado

🤖 Chatbot Viber em PHP: envio de mensagens estruturadas com menus de teclado

O utilizador prime um botão no chat e recebe, não texto simples, mas uma grelha de imagens, links e teclas coloridas. Parece uma aplicação nativa. Na realidade, é JSON simples que o seu script PHP devolve através da API REST do Viber.

O problema com a maioria dos tutoriais: ensinam a responder com texto a texto. Um bot de negócios real tem de apresentar um menu de teclado com botões de link, imagens e carrosséis. Caso contrário, o utilizador simplesmente não percebe o que o bot pode fazer e abandona. Desde fevereiro de 2024, o Viber passou a criação de bots para uma base comercial (100 € por mês através de parceiros oficiais Rakuten Viber), e a autenticação passou a ser feita no cabeçalho HTTP X-Viber-Auth-Token em vez de no corpo do pedido. Os trechos de código com auth_token dentro do JSON já não funcionam.

Aqui está um bot Viber funcional em PHP de raiz: desde a configuração do webhook até ao envio de um menu de teclado estruturado com botões de resposta e de abertura de URL. O código está atualizado para a versão 7.3 da API.

💡 Visão geral rápida:

  • Registar um bot comercial através de um parceiro Viber e obter um token de autenticação
  • Configurar o webhook via set_webhook com o cabeçalho X-Viber-Auth-Token
  • Receber eventos de callback do Viber: webhook → subscribed → message
  • Construir um menu de teclado (uma grelha de botões com cores, tamanhos e ações personalizados)
  • Enviar uma resposta estruturada: texto, imagens, links, carrosséis via send_message

Como funciona a API de Bots do Viber

A API de Bots do Viber funciona num modelo REST: o seu servidor recebe pedidos de callback no webhook e responde chamando https://chatapi.viber.com/pa/send_message. Cada pedido é autenticado com um token no cabeçalho X-Viber-Auth-Token.

O ciclo básico é o seguinte:

  • Regista um bot através de um parceiro Viber e recebe um token no painel "Edit Info".
  • Configura um webhook (o URL do seu servidor com um SSL válido; o Let's Encrypt funciona, uma vez que está na lista de certificados Java confiáveis do Viber; certificados autoassinados não são aceites).
  • O Viber envia pedidos POST para este URL em cada evento: um utilizador subscreveu, enviou uma mensagem, pressionou um botão.
  • O seu script PHP lê o JSON recebido, analisa o campo event e responde chamando send_message.

O primeiro passo para compreender é observar um bot ao vivo. Abra o Viber e procure a conta pública de qualquer marca conhecida. Quase todas têm um chatbot com um menu. A captura de ecrã abaixo mostra um resultado típico de pesquisa de uma conta pública.

A pesquisar uma conta pública no Viber

Após subscrever, entra num chat individual. Tem um aspeto aproximado a este: um avatar, uma mensagem de boas-vindas e um botão para iniciar a conversa.

Ecrã de chat público do Viber após subscrição

Toque no ícone de mensagem no canto superior direito e envie "Olá". Se o bot estiver configurado com um menu de teclado, verá uma resposta com uma grelha de botões:

Menu de teclado com botões num chat do Viber

Isto é uma mensagem estruturada. Existem dois tipos de botões: reply envia texto de volta para o bot (ao pressionar "Notícias" ou "Artigos"), enquanto open-url abre um link no navegador. Agora vamos escrever o código que produz isto.

Passo 1: Obter o token e configurar o webhook

O token está localizado no painel de administração do Viber: secção Edit Info → campo App Key. É uma cadeia de letras e números que inclui em cada pedido à API.

O webhook é definido com um único pedido POST para https://chatapi.viber.com/pa/set_webhook. O corpo do pedido é JSON com o URL do seu manipulador e uma lista de eventos a que subscreve. O cabeçalho é X-Viber-Auth-Token com o seu token.

Script de configuração do webhook (guarde como setup.php e execute uma vez):

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}

O que acontece aqui: o cURL envia um POST para set_webhook. O token está estritamente no cabeçalho, não no corpo. O Viber devolve {"status":0} em caso de sucesso e envia imediatamente um callback webhook para o URL especificado para verificar a sua disponibilidade. Responder com {"status":0,"status_message":"ok"} a este callback é obrigatório (vamos abordar isto no próximo passo).

Se receber invalidUrl, verifique o seu certificado SSL. O Let's Encrypt funciona; autoassinado não. No seu servidor, basta executar certbot --nginx -d your-domain.com e configurar a renovação automática via cron.

Passo 2: Receber eventos de callback do Viber

Quando um utilizador interage com o bot, o Viber envia um pedido POST com JSON para o seu webhook. A sua tarefa é ler o campo event e reagir.

Um manipulador mínimo webhook.php que responde corretamente a todos os eventos:

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}

Desagregação dos eventos:

  • webhook chega uma vez, quando o webhook é configurado. Deve devolver {"status":0,"status_message":"ok"} com uma lista de eventos suportados. Sem isto, o Viber não confirma o URL e o bot não arranca.
  • subscribed significa que um utilizador subscreveu o bot. É o momento ideal para enviar um menu de boas-vindas.
  • conversation_started significa que um utilizador abriu o chat (pela primeira vez ou novamente). Também adequado para mostrar o menu principal.
  • message é o evento principal de trabalho. O campo text contém ou uma mensagem arbitrária do utilizador ou o ActionBody de um botão premido (se o seu tipo for reply). É assim que o bot percebe o que foi selecionado: «News», «Articles» ou «Gallery».

A construção match() está disponível a partir do PHP 8. Se tiver PHP 7.4, substitua-a por switch.

Passo 3: Construir o menu de teclado

Um teclado é anexado a qualquer mensagem através do campo keyboard no objeto JSON send_message. É um array de botões com definições separadas para largura (Columns, 1-6), altura (Rows, 1-2), cor de fundo, texto e ação.

Uma função que monta um menu principal a partir de sete botões de resposta e um botão de link:

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}

Os botões com Columns=2 e Rows=2 são quadrados, três por linha (2+2+2=6). O botão de link com Columns=4 ocupa a sua própria linha. A grelha é montada da esquerda para a direita, de cima para baixo. A soma de Columns numa linha determina o layout.

Parâmetros dos botões:

Parâmetro

Valores

Finalidade

Columns

1-6

Largura do botão em colunas condicionais

Rows

1-2

Altura do botão em linhas

ActionType

reply ou open-url

reply devolve texto ao bot, open-url abre o navegador

ActionBody

string

Para reply, o texto que o bot receberá de volta; para open-url, o URL

Text

string

Rótulo no botão, suporta as tags HTML b, i e cor de fonte

BgColor

HEX

Cor de fundo do botão

Image

URL

Imagem no topo do botão, JPEG, opcional

Passo 4: Enviar dados via send_message

Uma função de envio que se chama a partir do gestor de eventos:

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}

A diferença fundamental em relação a tutoriais antigos: o token vai no cabeçalho, não no corpo do JSON. O corpo contém apenas os campos receiver, type, text, keyboard e, opcionalmente, sender.name, sender.avatar, tracking_data. O tamanho máximo do JSON é 30 KB. Se o exceder, o Viber descarta a mensagem silenciosamente; verifique strlen(json_encode($data)) antes de enviar.

Passo 5: Montar conteúdo para botões

Quando um utilizador prime um botão de resposta, o seu ActionBody é enviado para o webhook como texto da mensagem. Analisa-o no match() e chama a função apropriada. Eis um exemplo para uma lista de notícias com imagens:

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}

Do lado do utilizador, isto parece uma grelha de cartões: imagem à esquerda, título à direita. Tocar em qualquer parte abre o link no navegador.

Mensagem rica com imagens e links no Viber

Uma galeria, lista de artigos ou sondagens são montadas exatamente da mesma forma; apenas mudam o conteúdo do array $buttons e o texto da mensagem. Se houver muito conteúdo e o JSON se aproximar dos 30 KB, divida-o em páginas com um botão «Seguinte».

Se preferir trabalhar com uma biblioteca PHP já pronta em vez da API pura, veja o viber-bot-php do Bogdaan. Trata da validação de assinatura, encaminhamento de eventos e geração de JSON por si.

Resumo: sequência completa de lançamento

  • Obtenha o token no Painel de Administração do Viber: secção Edit Info → App Key.
  • Implemente o handler num servidor com HTTPS. O Let's Encrypt funciona; um certificado autoassinado não. O Viber valida os certificados contra a lista de Autoridades de Certificação Raiz Java fidedignas.
  • Configure o webhook com um pedido POST para https://chatapi.viber.com/pa/set_webhook com o corpo {"url":"https://your-domain/webhook.php","event_types":[...]} e o cabeçalho X-Viber-Auth-Token.
  • Verifique a resposta: {"status":0} significa sucesso. O Viber enviará de imediato um callback webhook para o seu URL. Certifique-se de que o script devolve {"status":0,"status_message":"ok"}.
  • Envie o primeiro menu: no evento conversation_started chame getMainMenu() e passe o resultado a sendToViber().

⁉️🤔 Perguntas frequentes

Posso usar um certificado SSL autoassinado para o webhook?

Não. O Viber valida o certificado contra a lista de autoridades de certificação raiz Java fidedignas. Um certificado autoassinado provoca um erro invalidUrl ao configurar o webhook. O Let's Encrypt está nessa lista e é gratuito. No seu servidor, execute certbot --nginx -d your-domain.com, adicione a renovação automática ao cron e o Viber aceitará o certificado sem problemas.

Qual é a diferença entre reply e open-url no ActionType?

reply reenvia o ActionBody para o webhook como texto de mensagem. O script PHP vê-o em $input['message']['text'] e encaminha-o através de match(). Use isto para navegação dentro do bot. open-url abre um link num navegador externo e não aciona o webhook. Na prática, os menus combinam ambos os tipos: cinco ou seis botões de reply para secções e um botão largo de open-url para navegar para o website.

Que tipos de mensagem suporta a Viber Bot API?

A Viber Bot API suporta nove valores de type em send_message: text (texto simples), picture (imagem JPEG), video (ficheiro de vídeo), file (qualquer ficheiro até 50 MB), location (ponto geográfico), contact (cartão de contacto), sticker (sticker), rich_media (carrossel de cartões) e url (pré-visualização de link). Para a lista atual com os campos obrigatórios, consulte a documentação do Viber Developers Hub. O cenário mais comum é text + keyboard para menus e picture para envio de imagens. Os carrosséis são ótimos para montras e catálogos, mas exigem mais código.

O que devo fazer se o utilizador não vir o teclado?

Há três causas comuns. Primeira: o JSON com o teclado excedeu 30 KB e o Viber descarta silenciosamente essas mensagens. Verifique strlen(json_encode($data)) antes de enviar. Segunda: a resposta ao set_webhook não continha "status":0, o webhook não foi configurado e o bot não está a receber mensagens. Terceira: está a usar o formato antigo com auth_token no corpo do JSON. Desde a API 7.0 que isto é ignorado; precisa do cabeçalho X-Viber-Auth-Token. Para depuração, ative o registo da resposta da API do Viber: ela devolve JSON legível com códigos de erro invalidAuthToken, badData ou missingData.

Posso alterar o teclado depois de enviar?

Sim. A cada nova mensagem pode enviar um teclado diferente. O cliente Viber mostra sempre o último recebido. Isto permite-lhe construir menus de vários níveis: menu principal → submenu de secção → conteúdo específico. Cada nível tem o seu próprio conjunto de botões e o utilizador move-se entre eles premindo as teclas de reply.

Quanto custa lançar um bot Viber em 2026?

Desde 5 de fevereiro de 2024, criar um novo bot custa 100 € por mês através dos parceiros oficiais da Rakuten Viber. Os bots criados antes dessa data continuam sob os termos antigos. O custo inclui uma conta dedicada com painel de administração, analítica e acesso a todos os tipos de mensagem, incluindo rich_media e Viber Pay. A parte técnica da API é idêntica para bots comerciais e bots gratuitos legados.

O bot está pronto: o que vem a seguir

Um menu de teclado é apenas um ponto de entrada. Quando a mecânica básica estiver a funcionar, adicione carrosséis de conteúdo via rich_media, pagamentos via Viber Pay e segmentação de utilizadores com base nos botões que pressionam com mais frequência. Cada novo nível não altera a base: continua a ler event e a enviar send_message com o cabeçalho X-Viber-Auth-Token.

Se precisar de um exemplo real de código funcional, consulte o repositório viber-bot-php no GitHub. Ele cobre todos os eventos, assinatura de pedidos e construção de um teclado com imagens. E para um início visual, aqui está um tutorial de meia hora sobre como criar um bot Viber de raiz:

O principal a reter: a Viber Bot API é simples no início, mas exigente nos detalhes. Um cabeçalho de autenticação correto, SSL fidedigno, JSON válido com menos de 30 KB e um encaminhamento claro de ActionBody são suficientes para manter o bot a funcionar sem falhas. Pegue nos excertos acima, substitua o seu token e domínio e lance o seu primeiro menu hoje.