https://a.storyblok.com/f/270183/23117/86aa51bec6/blog_timer-lavavel-fb_1200x600.png

Criando um temporizador para o peru com Laravel, Facebook Messenger e Vonage

Publicado em May 21, 2021

Tempo de leitura: 6 minutos

Este ano, minha sogra me pediu para ajudá-la a preparar o jantar de Natal para a família. Estou muito animada com isso, mas, com um filhote de cachorro novo em casa e um sobrinho pequeno, já posso imaginar que vou me distrair e esquecer de colocar as batatas no forno na hora certa!

Para ajudar nisso, decidi criar um pequeno aplicativo em Laravel que mantém uma coleção de receitas. Você envia uma mensagem com o nome de uma receita pelo WhatsApp, Facebook ou Viber. Ele busca a lista de etapas necessárias para cada receita e envia a próxima etapa quando chegar a hora de realizá-la. Você pode ficar tranquilo, sabendo que tudo na cozinha está sob controle e que, quando for hora de você intervir, receberá uma notificação!

Veja o aplicativo em ação:

Turkey Timer DemoTurkey Timer Demo

Nesta postagem, vou trabalhar com o Facebook, mas é fácil adaptar isso também para o WhatsApp e o Viber, já que vamos usar o pacote laravel-notification da Vonage.

Configuração do projeto Laravel e Vonage

Esse projeto exige bastante configuração; por isso, para evitar que você demore até o Ano Novo para ler este post, fui direto para o código específico do Vonage. Se você estiver interessado, eis o processo que segui para configurar o aplicativo (cada item tem um link para um commit que contém uma descrição mais detalhada do trabalho realizado):

É um aplicativo bem simples que conta com gerenciamento de usuários e a capacidade de exibir uma receita e os tempos associados a ela. É um aplicativo independente, sem dependência do Vonage no momento. No entanto, queremos poder receber mensagens em nossa página do Facebook, então preciso fazer mais algumas configurações. Para que o aplicativo funcione, preciso vincular minha página do Facebook a um Account da Vonage, criar e configurar um aplicativo para que o Vonage saiba para onde enviar as solicitações de webhook e expor meu aplicativo à internet usando o ngrok para que a Vonage possa acessá-la.

Se você quiser tentar criar isso por conta própria, participe da Slack da Comunidade Vonage e podemos seguir juntos as etapas necessárias

No restante deste post, vamos adicionar a funcionalidade para que os usuários possam nos enviar uma mensagem com o nome de uma receita e fazer com que o aplicativo responda indicando as ações que precisam ser realizadas no momento certo.

Como lidar com mensagens recebidas no Facebook

Quando criei meu aplicativo Vonage, tive que fornecer dois URLs: um que será chamado quando eu receber uma mensagem de um usuário e outro que recebe atualizações de status da Vonage. Escolhi /webhooks/inbound-message para receber mensagens e /webhooks/message-status para as atualizações de status. Como o Vonage enviará uma solicitação de fora do aplicativo, tive que desativar a verificação de no app/Http/Middleware/VerifyCsrfToken.php:

protected $except = [
    '/webhooks/*'
];

Agora que a Vonage consegue acessar meu webhook, é hora de processar essas solicitações recebidas. Usei make:controller para gerar um novo WebhooksController e atualizei routes/web.php para direcionar as URLs acima para este controlador:

Route::post('/webhooks/inbound-message', 'WebhooksController@inboundMessage')->name('webhooks.inbound');
Route::post('/webhooks/message-status', 'WebhooksController@messageStatus')->name('webhooks.status');

A última coisa a fazer é implementar o WebhooksController. Por enquanto, estou registrando a solicitação recebida para poder ver o formato da solicitação que a Vonage envia:

namespace App\Http\Controllers;
use Illuminate\Http\Request;

class WebhooksController extends Controller
{
    public function inboundMessage(Request $request) {
        \Log::debug('Inbound Message', $request->all());
    }

    public function messageStatus(Request $request) {
        \Log::debug('Message Status', $request->all());
    }
}

Depois de fazer essas alterações, enviei uma mensagem da minha conta pessoal para a minha página do Facebook e a seguinte entrada apareceu no arquivo de log do Laravel:

{
  "message_uuid": "f4fcc665-7b71-4291-a079-505154e28c36",
  "to": {
    "id": "987654210987654",
    "type": "messenger"
  },
  "from": {
    "id": "123456789012345",
    "type": "messenger"
  },
  "timestamp": "2018-12-12T11:36:44.663Z",
  "direction": "inbound",
  "message": {
    "content": {
      "type": "text",
      "text": "Christmas Dinner"
    }
  }
}

Ótimo! O usuário me enviou uma mensagem e meu aplicativo a recebeu como esperado. Agora que já conseguimos receber mensagens, é hora de começar a enviar respostas.

As alterações feitas nesta seção estão descritas em este commit

Criação de uma notificação no Laravel

Para enviar atualizações de volta ao usuário no momento certo, vamos usar a delay . Antes disso, precisamos habilitar a queue essa funcionalidade no Laravel. Vamos usar o database driver, já que a taxa de processamento não será particularmente alta e o uso do banco de dados elimina a necessidade de dependências adicionais, como o Redis. Podemos definir essa configuração no .env arquivo:

QUEUE_CONNECTION=database

Depois de fazer isso, criei a tabela para armazenar os trabalhos executando php artisan queue:table && php artisan migrate.

Agora que toda a parte administrativa já está resolvida, é hora de começar a criar nossas notificações. Precisarei enviar uma frase simples sempre que uma condição for acionada. Eu poderia ter criado uma notificação para cada mensagem, mas, para agilizar o processo, criei uma única notificação que aceita uma string em app/Notifications/FreeText.php com o seguinte conteúdo:

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;

class FreeText extends Notification implements ShouldQueue
{
    use Queueable, ShouldQueue;

    protected $text;
    protected $channel;

    public function __construct($text, $channel)
    {
        $this->text = $text;
        $this->channel = $channel;
    }

    public function via($notifiable)
    {
        return [];
    }
}

Isso define uma notificação, mas nosso aplicativo ainda não sabe como enviá-la. Precisamos preencher o via método e implementar quaisquer to* métodos da notificação. Para enviar essas notificações, vamos usar o nexmo/laravel-notification , que nos permite implementar os seguintes métodos em nossa notificação:

  • toNexmoWhatsApp

  • toNexmoFacebook

  • toNexmoViberServiceMessage

  • toNexmoSms

Vamos implementar e toNexmoFacebook adicionando o seguinte à notificação:

public function toNexmoFacebook($notifiable)
{
    return (new \Nexmo\Notifications\Message\Text)
        ->content($this->text);
}

Além de implementar esses métodos, precisamos informar ao Laravel como encaminhar a mensagem. Normalmente, você usaria a $notifiable entidade passada para o método para determinar como o usuário deseja ser contatado. Neste caso, vamos responder no canal pelo qual recebemos a mensagem. Esse canal é passado para o construtor da notificação. É assim que meu via método fica com essas alterações:

public function via($notifiable)
{
    return [$this->channel];
}

Há uma última coisa a fazer antes que tudo isso comece a funcionar, que é fornecer algumas credenciais de autenticação da Vonage e a configuração para o envio pelo Messenger. Se você quiser fazer o mesmo, crie um aplicativo no painel do Vonage e adicione o seguinte ao seu .env arquivo:

NEXMO_APPLICATION_ID="YOUR_APPLICATION_ID"
NEXMO_PRIVATE_KEY=./private.key
NEXMO_FROM_MESSENGER="FACEBOOK_PAGE_ID"

As alterações feitas nesta seção estão descritas em este commit

Envio de atualizações para o Facebook

Agora que já fizemos todo o trabalho pesado, a última coisa a fazer é enviar as ações que precisam ser realizadas para preparar a receita.

Ao receber uma mensagem de um usuário, precisamos extrair o canal pelo qual a mensagem foi enviada e o ID do usuário que nos enviou a mensagem. Assim que tivermos essas informações, podemos criar uma nova Notificação sob demanda usando essas informações:

// The incoming message contains the platform + contact details that
// we need to reply with, so configure a notification route with those
// details
$from = $request->input('from');

// The Vonage Messages API returns messenger, but our channel names are all prefixed with nexmo-
$channel = 'nexmo-' . $from['type'];
$sender = Notification::route($channel, $from['id']);

Nesse momento, podemos enviar qualquer texto que quisermos ao usuário. A primeira coisa que precisamos verificar é se a mensagem que ele nos enviou contém o nome de uma receita. Se não contiver, enviamos uma mensagem informando que não foi possível encontrar essa receita.

// Try and find the recipe name that was sent to us
$recipeName = $request->input('message.content.text');
$recipe = \App\Recipe::where('name', $recipeName)->first();
if (!$recipe) {
    $sender->notify(new FreeText(
        "I couldn't find that recipe",
        $channel
    ));
    return;
}

Se passarmos por esse bloco de código, significa que temos uma receita válida, e é hora de programar algumas notificações! Cada conjunto de horários em uma receita tem um action e um start_time em segundos, começando em zero. Felizmente, o Laravel nos permite adiar uma notificação por um determinado número de segundos a partir de agora, o que o torna a opção perfeita para o nosso caso de uso.

A parte final do nosso inboundMessage método precisa percorrer cada um timing e agendar uma nova notificação:

foreach ($recipe->timings()->get() as $t) {
    $sender->notify((new FreeText(
        $t->action,
        $channel
    ))->delay($t->start_time));
}

Resumindo, nosso inboundMessage método fica assim:

public function inboundMessage(Request $request) {
    \Log::debug('Inbound Message', $request->all());

    $from = $request->input('from');

    // The Vonage API returns messenger, but our channel names are all prefixed with nexmo-
    $channel = 'nexmo-' . $from['type'];
    $sender = Notification::route($channel, $from['id']);

    // Try and find the recipe name that was sent to us
    $recipeName = $request->input('message.content.text');
    $recipe = \App\Recipe::where('name', $recipeName)->first();
    if (!$recipe) {
        $sender->notify(new FreeText(
            "I couldn't find that recipe",
            $channel
        ));
        return;
    }

    // If we get this far, we have a recipe! Time to schedule some notifications
    foreach ($recipe->timings()->get() as $t) {
        $sender->notify((new FreeText(
            $t->action,
            $channel
        ))->delay($t->start_time));
    }
}

As alterações feitas nesta seção estão descritas em este commit

Executando o aplicativo

Agora que tudo já está pronto, é hora de rodar a aplicação final! Aqui está uma lista rápida de tudo o que precisei fazer para que tudo funcionasse:

  1. Correr php artisan serve

  2. Certifique-se de que ngrok http 8000 de que ele esteja em execução para que a Vonage possa fazer chamadas para meu aplicativo

  3. Execute php artisan queue:work para monitorar as tarefas que estão sendo inseridas no banco de dados

  4. Visite minha página no Facebook e envie uma receita (nesse caso, Christmas Dinner!)

  5. Sente-se e relaxe, sabendo que receberei uma mensagem quando houver algo que eu precise fazer

Se você quiser ver o projeto completo desta postagem, pode encontrá-lo no GitHub. Se quiser executá-lo por conta própria, você precisará:

  1. Vincular uma página do Facebook ao Vonage

  2. Crie uma nova aplicação da Vonage e associe sua página a essa aplicação

  3. Configure seus webhooks

  4. Clone o repositório

  5. Atualizar .env com suas credenciais da Vonage

  6. Correr composer install

  7. Correr php artisan migrate && php artisan db:seed

  8. Executar php artisan serve e php artisan queue:work em terminais separados

  9. Envie uma mensagem para a sua página do Facebook

E agora, para onde vamos?

Bem, foi divertido! Não só pude experimentar a Messages API da Vonage , como também aprendi muito sobre notificações no Laravel (incluindo como criar novos canais). Como bônus extra, vou até ter um pequeno auxiliar para me lembrar quando as coisas precisam acontecer no dia de Natal!

Compartilhar:

https://a.storyblok.com/f/270183/384x384/1c8825919c/mheap.png
Michael HeapEx-funcionários da Vonage

Michael é um engenheiro de software poliglota, empenhado em reduzir a complexidade dos sistemas e torná-los mais previsíveis. Trabalhando com diversas linguagens e ferramentas, ele compartilha seus conhecimentos técnicos com públicos de todo o mundo em grupos de usuários e conferências. No dia a dia, Michael é ex-representante de desenvolvedores da Vonage, onde dedicava seu tempo a aprender, ensinar e escrever sobre todos os tipos de tecnologia.