
Compartir:
Michael es un ingeniero de software políglota, empeñado en reducir la complejidad de los sistemas y hacerlos más predecibles. Trabaja con una gran variedad de lenguajes y herramientas, y comparte sus conocimientos técnicos con audiencias de todo el mundo en grupos de usuarios y conferencias. En el día a día, Michael es un antiguo defensor de los desarrolladores en Vonage, donde pasaba su tiempo aprendiendo, enseñando y escribiendo sobre todo tipo de tecnología.
Creación de un temporizador para pavos con Laravel, Facebook Messenger y Vonage
Tiempo de lectura: 6 minutos
Este año mi suegra me ha pedido que le ayude a preparar la cena de Navidad para la familia. Me hace mucha ilusión, pero con un nuevo cachorro en casa y un sobrino pequeño me veo distrayéndome y olvidándome de meter las patatas en el horno en el momento adecuado.
Para ayudar con esto, decidí escribir una pequeña aplicación Laravel que mantiene una colección de recetas. Le envías un mensaje con el nombre de una receta a través de WhatsApp, Facebook o Viber. Recupera la lista de pasos que requiere cada receta y te envía el siguiente paso cuando llega el momento de hacerlo. Puedes relajarte sabiendo que todo está bajo control en la cocina y que cuando llegue el momento de la intervención humana, recibirás una notificación.
Aquí está la aplicación en acción:
Turkey Timer Demo
En este post voy a trabajar con Facebook, pero es fácil de extender a WhatsApp y Viber también como vamos a utilizar el Vonage laravel-notification paquete.
Configuración del proyecto Laravel y Vonage
Hay un montón de configuración necesaria para este proyecto, así que para evitar que te lleve hasta Año Nuevo leer esto, he saltado directamente al código específico de Vonage en este post. Si estás interesado, aquí está el proceso que seguí para configurar la aplicación (cada elemento enlaza a un commit que contiene una descripción más larga del trabajo realizado):
Actualizar el proyecto para utilizar una
sqlitebase de datosCrear un modelo y una migración para
Timingsdentro de unRecipeAñadir datos de siembra a la base de datos para realizar pruebas
Es una aplicación bastante simple que tiene administración de usuarios y la capacidad de mostrar una receta y sus horarios asociados. Es una aplicación independiente sin dependencia de Vonage por el momento. Sin embargo, queremos poder recibir mensajes en nuestra página de Facebook, por lo que necesito hacer un poco más de configuración. Para que la aplicación funcione, necesito vincular mi página de Facebook a una cuenta de Vonage, crear y configurar una aplicación para que Vonage sepa a dónde enviar las solicitudes de webhook y exponer mi aplicación a Internet mediante ngrok para que Vonage pueda acceder a ella.
Si quieres intentar construirlo tú mismo, únete al espacio de trabajo Slack de la comunidad de Vonage y podremos trabajar juntos en los pasos necesarios.
En el resto de este post, vamos a añadir la posibilidad de que los usuarios nos envíen un mensaje con el nombre de una receta y que la aplicación responda con las acciones que hay que realizar en el momento adecuado.
Gestión de los mensajes entrantes de Facebook
Cuando creé mi aplicación de Vonage, tuve que proporcionar dos URL; una que se llamará cuando reciba un mensaje de un usuario y otra que reciba actualizaciones de estado de Vonage. Elegí /webhooks/inbound-message para recibir mensajes y /webhooks/message-status para las actualizaciones de estado. Como Vonage enviará una solicitud desde fuera de la aplicación he tenido que deshabilitar CSRF en app/Http/Middleware/VerifyCsrfToken.php:
protected $except = [
'/webhooks/*'
];Ahora que Vonage puede acceder a mi webhook, es hora de manejar esas solicitudes entrantes. Utilicé make:controller para generar un nuevo WebhooksController y actualizado routes/web.php para apuntar las URLs anteriores a este controlador:
Route::post('/webhooks/inbound-message', 'WebhooksController@inboundMessage')->name('webhooks.inbound');
Route::post('/webhooks/message-status', 'WebhooksController@messageStatus')->name('webhooks.status');
Lo último que hay que hacer es aplicar el método WebhooksController. Por ahora estoy registrando la solicitud entrante para poder ver el formato de la solicitud que Vonage envía:
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());
}
}
Tras realizar estos cambios, envié un mensaje desde mi Account personal a mi página de Facebook y apareció la siguiente entrada en el archivo de registro de 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"
}
}
}Excelente. El usuario me ha enviado un mensaje y mi aplicación lo ha recibido como estaba previsto. Ahora que ya podemos recibir mensajes es hora de empezar a devolver respuestas.
Los cambios realizados en esta sección se muestran en esta confirmación
Creación de una notificación Laravel
Para enviar las actualizaciones al usuario en el momento correcto vamos a utilizar la funcionalidad de colas del sistema Laravel delay de Laravel. Antes de poder hacerlo, necesitamos habilitar la funcionalidad queue en Laravel. Vamos a utilizar el controlador database ya que no va a ser particularmente alto rendimiento y el uso de la base de datos elimina la necesidad de dependencias adicionales como Redis. Podemos configurar esta opción en el archivo .env archivo:
QUEUE_CONNECTION=databaseUna vez hecho esto, he creado la tabla para almacenar los trabajos ejecutando php artisan queue:table && php artisan migrate.
Con toda la administración fuera del camino, es hora de empezar a construir nuestras notificaciones. Necesitaré enviar una simple frase cada vez que se dispare una condición. Podría haber creado una notificación por mensaje, pero en aras de la rapidez he creado una única notificación que acepta una cadena en app/Notifications/FreeText.php con el siguiente contenido:
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 [];
}
}
Esto define una notificación, pero nuestra aplicación aún no sabe cómo enviarla. Tenemos que rellenar el método via e implementar cualquier método to* en la notificación. Para enviar estas notificaciones vamos a utilizar el método nexmo/laravel-notification que nos permite implementar los siguientes métodos en nuestra notificación:
toNexmoWhatsApptoNexmoFacebooktoNexmoViberServiceMessagetoNexmoSms
Vamos a implementar y toNexmoFacebook añadiendo lo siguiente a la notificación:
public function toNexmoFacebook($notifiable)
{
return (new \Nexmo\Notifications\Message\Text)
->content($this->text);
}
Además de implementar estos métodos, necesitamos decirle a Laravel cómo enrutar el mensaje. Normalmente se utiliza la entidad $notifiable pasada al método para determinar cómo el usuario quiere ser contactado. En este caso, vamos a responder en el canal en el que recibimos el mensaje. Este canal se pasa al constructor de la notificación. Este es el aspecto de mi método via con estos cambios:
public function via($notifiable)
{
return [$this->channel];
}
Hay una última cosa que hacer antes de que todo esto comience a funcionar, y es proporcionar algunas credenciales de autenticación de Vonage y la configuración para el envío a través de Messenger. Si quieres hacer lo mismo crea una aplicación en el panel de control de Vonage y agrega lo siguiente a tu archivo .env archivo:
NEXMO_APPLICATION_ID="YOUR_APPLICATION_ID"
NEXMO_PRIVATE_KEY=./private.key
NEXMO_FROM_MESSENGER="FACEBOOK_PAGE_ID"Los cambios realizados en esta sección se muestran en esta confirmación
Enviar actualizaciones a Facebook
Ahora que ya hemos hecho todo el trabajo duro, lo último es enviar las acciones que hay que llevar a cabo para cocinar la receta.
Al recibir un mensaje de un usuario necesitamos extraer el canal por el que se envió el mensaje, y el ID del usuario que nos envió el mensaje. Una vez que tenemos eso, podemos crear una nueva notificación a petición utilizando esta información:
// 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']);
En este punto podemos enviar el texto que queramos al usuario. Lo primero que tenemos que comprobar es si el mensaje que nos han enviado contiene el nombre de una receta. Si no es así, les enviamos un mensaje diciendo que no hemos podido encontrar esa receta.
// 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;
}
Si superamos este bloque de código, tenemos una receta válida y es hora de programar algunas notificaciones. Cada conjunto de tiempos en una receta tiene un action y un start_time en segundos, comenzando en cero. Afortunadamente Laravel nos permite retrasar una notificación por un número de segundos a partir de ahora, por lo que es el ajuste perfecto para nuestro caso de uso.
La parte final de nuestro método inboundMessage debe iterar sobre cada timing y programar una nueva notificación:
foreach ($recipe->timings()->get() as $t) {
$sender->notify((new FreeText(
$t->action,
$channel
))->delay($t->start_time));
}
Si lo juntamos todo, nuestro método inboundMessage tiene el siguiente aspecto:
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));
}
}
Los cambios realizados en esta sección se muestran en esta confirmación
Ejecutar la aplicación
Ahora que todo está construido, ¡es hora de ejecutar la aplicación final! Aquí está una lista rápida de todo lo que tenía que hacer para que las cosas funcionen:
Ejecutar
php artisan serveAsegúrate de que
ngrok http 8000se esté ejecutando para que Vonage pueda realizar llamadas a mi aplicaciónEjecute
php artisan queue:workpara ver si se insertan trabajos en la base de datosVisita mi página de Facebook y envíale una receta (en este caso,
Christmas Dinner!)Sentarse y relajarse, sabiendo que recibiré un mensaje cuando haya algo que tenga que hacer.
Si quieres ver el proyecto completo de este post puedes encontrarlo en Github. Si quieres ejecutarlo tú mismo, necesitarás:
Vincula una página de Facebook a Vonage
Crea una nueva aplicación de Vonage y asocia tu página con esa aplicación
Configure sus webhooks
Clonar el repositorio
Actualiza
.envcon tus credenciales de VonageEjecutar
composer installEjecutar
php artisan migrate && php artisan db:seedEjecute
php artisan serveyphp artisan queue:worken terminales separadosEnvía un mensaje a tu página de Facebook
¿Y ahora qué?
Bueno, ¡ha sido divertido! No sólo pude probar la API de Messages de Vonage sino que aprendí mucho sobre las notificaciones de Laravel (incluyendo cómo crear nuevos canales). Como beneficio adicional, ¡incluso tendré un pequeño ayudante que me recordará cuándo debo hacer las cosas el día de Navidad!
Compartir:
Michael es un ingeniero de software políglota, empeñado en reducir la complejidad de los sistemas y hacerlos más predecibles. Trabaja con una gran variedad de lenguajes y herramientas, y comparte sus conocimientos técnicos con audiencias de todo el mundo en grupos de usuarios y conferencias. En el día a día, Michael es un antiguo defensor de los desarrolladores en Vonage, donde pasaba su tiempo aprendiendo, enseñando y escribiendo sobre todo tipo de tecnología.