https://d226lax1qjow5r.cloudfront.net/blog/blogposts/build-a-whatsapp-and-messenger-graphql-bot-to-find-hospital-beds/whatsapp_messenger_graphql_1200x600.png

Construye un bot GraphQL de WhatsApp y Messenger para encontrar camas de hospital

Publicado el March 25, 2021

Tiempo de lectura: 13 minutos

COVID nos ha afectado a todos, pero a unos más que a otros. Uno de los principales problemas a los que la gente se enfrentaba y sigue enfrentándose es la escasez de camas hospitalarias. Hace unos meses creé una API GraphQL que devuelve la disponibilidad de camas en varias ciudades y distritos de la India. Los datos se extraen de sitios web oficiales y se normalizan en un formato uniforme que luego se expone en forma de GraphQL API.

Tenga en cuenta que los datos sólo están disponibles para algunas ciudades de la India.

También puede explorar la API en el Campo de juegos GraphQL.

Usando la API de Bedav y la API de Messages de Vonage, vamos a construir un chatbot que funcione con WhatsApp y Facebook Messenger usando JavaScript. La función de este chatbot es ayudar a los usuarios a buscar hospitales en una ciudad, encontrar un hospital con camas disponibles y proporcionar información adicional sobre el hospital, como su número de teléfono y dirección. También crearemos un comando con el que el usuario podrá obtener indicaciones para llegar a un hospital.

Requisitos previos

  1. Node y npm

  2. Una Account de Vonage

    Vonage API Account

    To complete this tutorial, you will need a Vonage API account. If you don’t have one already, you can sign up today and start building with free credit. Once you have an account, you can find your API Key and API Secret at the top of the Vonage API Dashboard.

Configuración del proyecto

Aunque el código para esto se puede encontrar en el repositorio GitHubse recomienda seguir este tutorial y utilizar el repositorio sólo como referencia.

Inicializar NPM e instalar dependencias

Comience creando una nueva carpeta, inicializando npm y creando una carpeta src dentro de ella:

mkdir chatbot && cd chatbot mkdir src npm init -y

Crearemos nuestros webhooks y servidor usando Express. Axios se utilizará para enviar peticiones a la Messages API y graphql-request se utilizará para enviar peticiones a la API GraphQL de Bedav. Puedes instalarlos junto con utilidades adicionales ejecutando el siguiente comando:

npm i express axios graphql-request js-base64 outdent dotenv

Y por último, instala Nodemon como una dependencia dev para que no tengamos que reiniciar nuestro servidor node cada vez que hagamos un cambio:

npm i -D nodemon

Añadamos también un script a nuestro package.json para iniciar nuestro servidor Nodemon dev:

// package.json

{
  "scripts": {
    "dev": "nodemon src/app.js"
  }
}

Crear los Webhooks

Antes de entrar en el código, ¿qué son los webhooks? Webhooks son esencialmente una URL que se llama cada vez que se realiza una determinada acción o evento. Los webhooks pueden ser considerados similares a los callbacks, sin embargo, se utilizan más a menudo para interactuar entre aplicaciones separadas e independientes. En nuestro caso, necesitamos exponer dos webhooks: uno que se activa cuando el usuario envía un mensaje y otro cuando hay una actualización de estado en un mensaje de la API (por ejemplo, cuando el usuario lee un mensaje que usted envió).

En primer lugar, vamos a configurar Express en src/app.jsdonde tenemos nuestra aplicación Express principal y las funciones principales responsables de manejar los comandos:

// src/app.js

const express = require("express")

const app = express()
app.use(express.json())

A continuación, vamos a añadir dos puntos finales, uno para el webhook de mensaje y otro para el webhook de estado:

// src/app.js

const { fixedMessages, sendMessage } = require("./utils")

app.post("/webhooks/inbound", (request, response) => {
  console.log(request.body)
)

app.post("/webhooks/status", (request, response) => {
  console.log(request.body)
  response.status(200).end()
})

app.listen(3000, () => {
  console.log("Listening on port 3000")
})

Instalación y configuración de Ngrok

Ngrok te permite probar tus webhooks exponiéndolos a Internet mediante una URL temporal. Para empezar con ngrok, puedes ir a su sitio y crear una Account. Una vez que lo hayas hecho y te hayas conectado, deberías ver las instrucciones para instalar y conectar tu Account en su dashboard.

Ahora que tenemos ngrok instalado y configurado vamos a iniciar nuestro servidor de desarrollo ejecutando npm run dev. Usa ngrok para crear una URL pública para nuestro servidor local ejecutando ./ngrok http 3000. Este comando le dice a ngrok que cree un proxy HTTP para el servidor que se ejecuta en localhost:3000.

Configurar el Sandbox de Mensajes

Inicia sesión en el panel de control de Vonage y dirígete al Sandbox en Mensajes y envío.

Vonage Dashboard Menu Bar with the Sandbox section highlighted

Para crear el sandbox de WhatsApp, haga clic en Añadir a sandbox en la sección WhatsApp. El Vonage Sandbox necesita verificar tu identidad y para hacerlo puedes seguir los pasos que se indican en el panel de control. Puedes seguir pasos similares para crear el Sandbox para Facebook Messenger.

Ya has activado correctamente el sandbox de WhatsApp. Pero espera, aún no hemos terminado. Todavía tenemos que proporcionar a Vonage la URL donde se encuentran nuestros webhooks. Si te desplazas hacia abajo en la página Sandbox, deberías ver la sección Webhooks que se parece a la siguiente:

Webhooks section on the Sandbox page of the Vonage Dashboard

El primero es el inbound webhook que estará en la ruta webhooks/inboundasí que configure ese campo como https://<your-ngrok-https-url>/webhooks/inbound. Del mismo modo, establezca la URL del status webhook a https://<your-ngrok-https-url>/webhooks/status. Y ya hemos terminado de configurar nuestro entorno de pruebas.

Nota: cada vez que reinicies ngrok, se te proporcionará una URL nueva y diferente que deberás cambiar manualmente en el panel de Vonage.

Enviar un mensaje al usuario

Necesitaremos una función de ayuda que se utilizará para enviar mensajes al usuario.

En primer lugar, tendremos que configurar nuestras credenciales de la API de Vonage para poder utilizar la API de Messages. Estas credenciales se almacenarán en variables de entorno. Crea un archivo .env en la raíz de la carpeta. Para cargar las variables de entorno en el entorno del nodo, utilizaremos el paquete dotenv paquete. Añadir los campos como se indica a continuación:

VONAGE_API_KEY = // Your Vonage API key
VONAGE_API_SECRET = // Your Vonage API Secret
WHATSAPP_NUMBER = // The from number in the case of the WhatsApp Sandbox
MESSENGER_ID = // The from ID of the Messenger Sandbox

Una vez hecho esto, añade el siguiente código al principio de app.js para añadir las variables de entorno:

// src/app.js
require("dotenv").config()

Cree un nuevo archivo llamado utils.js en la carpeta src carpeta. Esto va a contener nuestras funciones de utilidad, tales como el formato y el envío de un mensaje al usuario.

Haremos una petición a la API REST de Messages para enviar un mensaje. En el caso de Messages Sandbox, la URL de la API será https://messages-sandbox.nexmo.com/v0.1/messages. Utilizaremos Axios para enviar las solicitudes. Podemos utilizar la opción auth para añadir nuestras credenciales API como se muestra a continuación:

// src/utils.js

const axios = require("axios")

// default message type will be `text`
const sendMessage = async (to, message, type = "text") => {  let from = {
    // either 'WhatsApp' or 'messenger'
    type: to.type,
  }

  if (to.type === "whatsapp") {
    // the WhatsApp Sandbox number    
    from.number = proccess.env.WHATSAPP_NUMBER
  } else if (to.type === "messenger") {
    // the Messenger Sandbox ID
    from.id = process.env.MESSENGER_ID
  }

  await axios.post(
    "https://messages-sandbox.nexmo.com/v0.1/messages",
    {
      to,
      from,
      message: {
        content: {
          type,
          [type]: message,
        },
      },
    },
    {
      auth: {
        username: process.env.VONAGE_API_KEY,
        password: process.env.VONAGE_API_SECRET,
      },
    }
  )
}

module.exports = {
  sendMessage,
}

El tipo de mensaje es dinámico, ya que enviaremos un mensaje de tipo location cuando el usuario solicite indicaciones para llegar a un hospital. En WhatsApp, un mensaje de tipo location adjuntará una miniatura de la ubicación en Google Maps junto con el nombre y la dirección proporcionados.

Crear los comandos

Ahora podemos trabajar en la creación de nuestros comandos:

  1. help - Obtén un menú con todos los comandos que puedes utilizar

  2. cities - Obtenga una lista de todas las ciudades disponibles

  3. search <hospital-name> in <location> - Busque un hospital en un lugar determinado. Por ejemplo, search sakra in bangalore busca hospitales con el nombre Sakra en Bangalore.

  4. get directions to <hospital-id> - Obtener indicaciones para llegar a un hospital con una identificación determinada. Por ejemplo, get directions to 87 enviará la ubicación del Hospital con ID 87.

El gestor de mensajes entrantes

Vamos a crear una función que gestione las peticiones al inbound webhook en /webhooks/inbound. Esta función analiza el mensaje enviado por el usuario, y pasa el mensaje al manejador del comando que el usuario está tratando de usar. Envía de vuelta el mensaje apropiado y devuelve un código de estado de 200.

Nota, el webhook debe responder con un código de estado 200, de lo contrario el Messages API seguirá enviando una solicitud al webhook hasta que obtenga una respuesta 200.

Usamos expresiones regulares para comprobar si el usuario está intentando usar el botón search o directions y luego pasamos el mensaje al manejador apropiado. Si el mensaje es help, hio hellose envía el mensaje de ayuda al usuario. Si el mensaje es citiesse envía al usuario la lista de ciudades disponibles. Y por último, si el mensaje no coincide con ningún comando, se envía un mensaje inválido al usuario.

// src/app.js

const handleInbound = async (request, response) => {
  const content = request.body.message.content

  const text = content.text.toLowerCase().trim()

  // whom we have to reply to
  const to = request.body.from

  const searchRegex = /^search .* in .*$/i
  const directionsRegex = /^get directions to .*/i

  if (text.match(searchRegex)) {
    handleSearch(text, to)
  } else if (text.match(directionsRegex)) {
    handleDirections(text, to)
  } else if (["help", "hi", "hello"].includes(text)) {
    sendMessage(to, fixedMessages.help)
  } else if (text === "cities") {
    sendMessage(to, fixedMessages.cities)
  } else {
    sendMessage(to, `Sorry, invalid message. Type *help* to get a list of all commands.`)
  }

  response.status(200).end()
}

Ahora, modifiquemos el manejador del punto final del webhook entrante para utilizar la función handleInbound para utilizar la función

// src/app.js
app.post("/webhooks/inbound", handleInbound)

Los comandos Ayuda y Bienvenida

La primera vez que el usuario envíe un mensaje a nuestro servicio, necesitaremos un mensaje de bienvenida. Este mensaje debe contener información sobre lo que hace nuestro bot y los diferentes comandos que están disponibles para su uso. También necesitaremos un mensaje de ayuda que se enviará siempre que el usuario no sepa qué hacer y escriba help. Estos mensajes pueden ser similares.

La API de Bedav sólo dispone de información sobre hospitales en determinadas regiones. También necesitaremos un mensaje que ofrezca una lista de las regiones en las que la información está disponible.

Tal y como se implementa en la función handleInbound el mensaje de ayuda se envía cuando el usuario escribe hola, holao ayuda y el mensaje sobre las ciudades disponibles se envía cuando el usuario escribe ciudades.

Como estos dos mensajes serán constantes, podemos crear un objeto constante en el nivel superior para definir todos los mensajes fijos. También usamos outdent para eliminar los espacios extra en nuestra cadena que se supone que son sangrías en nuestro código. Añade el código a utils.js:

// src/utils.js

const outdent = require("outdent")

const fixedMessages = {
  help: outdent`
    The Bedav Bot gives you information on the availability of beds in hospitals and the contact information and location of those hospitals as well.
    
    You can use the following commands:
    1. *help* - Get this menu and all the commands you can use
    2. *cities* - Get a list of all the cities available
    2. *search* _<hospital-name>_ *in* _<location>_ - Search for a hospital in a particular location. For example, "search sakra in bangalore" searches for hospitals with the name Sakra in Bangalore
    3. *get directions to* _<hospital-id>_ - Get directions to a hospital with a particular ID. You can get the hospital ID from the search results. The serial number preceding the Hospital name is the Hospital ID. For example, if the search result has _(87) Sakra Hospital_, send _get directions to 87_ to get directions to Sakra Hospital.
  `,
  cities: outdent`
    The cities/districts currently available are:

    *Karnataka*
      1. Bangalore/Bengaluru

    *Maharashtra*
      2. Pune
      3. Kohlapur
      4. Sangli
      5. Satara
      6. Solapur

    *Andhra Pradesh*
      7. Anantapur
      8. Chittoor
      9. East Godavari
      10. Guntur
      11. Krishna
      12. Kurnool
      13. Prakasam
      14. Nellore
      15. Srikakulam
      16. Vishakapatanam
      17. Vizianagaram
      18. West Godavari
      19. Kadapa
  `,
}

module.exports = {
  ...
  fixedMessages,
}

Configurar el cliente GraphQL

Para simplificar las consultas a la API GraphQL, utilizaremos la librería graphql-request de GraphQL. Para configurar el cliente GraphQL, creamos una instancia de GraphQLClient y proporcionamos la URL de la API GraphQL.

// app.js

const { GraphQLClient } = require("graphql-request")
const client = new GraphQLClient("https://bedav.org/graphql")

Crear algunas funciones de utilidad

Como se menciona en la descripción del campo hospitalId en la documentación de la API, se trata de una cadena codificada en Base64 que puede descodificarse como Hospital:<hospitalId> donde hospitalId es un número entero exclusivo del hospital. Por razones que verá en la siguiente sección, vamos a crear dos funciones de utilidad: una para obtener el número del ID del hospital y otra para codificar un número entero en una cadena Base64 de la forma Hospital:<hospitalId>. Utilizaremos la librería js-base64 que añadimos antes a nuestro proyecto para trabajar con las cadenas codificadas. base64 cadenas codificadas.

// src/utils.js

const { encode, decode } = require("js-base64")

const getHospitalId = (encodedId) => {
  return decode(encodedId).slice(9)
}

const getEncodedString = (hospitalId) => {
  return encode(`Hospital:${hospitalId}`)
}

module.exports = {
  ...
  getEncodedString,
}

El comando de búsqueda

Funciones de utilidad para formatear cadenas

Vamos a crear otras dos funciones de utilidad para formatear los datos de los hospitales, una para formatear un hospital individual y otra que utiliza esa función para formatear un grupo de hospitales.

// src/utils.js

const getFormattedHospital = (hospital) => {
  const index = getHospitalId(hospital.id)

  const roundedString = (occupied, total) => {
    return `${Math.floor((occupied * 100) / total)}% Occupied`
  }

  const h = hospital

  // Percentages of beds available
  const percentages = {
    icu: roundedString(h.icuOccupied, h.icuTotal),
    hdu: roundedString(h.hduOccupied, h.icuTotal),
    oxygen: roundedString(h.oxygenOccupied, h.icuTotal),
    general: roundedString(h.generalOccupied, h.icuTotal),
    ventilators: roundedString(h.ventilatorsOccupied, h.icuTotal),
  }

  const formatted = outdent`
    *(${index}) ${hospital.name}*
      ${h.icuTotal !== 0 && h.icuAvailable !== null ? `_ICU Available_: ${h.icuAvailable} (${percentages.icu})` : ""}
      ${h.hduTotal !== 0 && h.icuAvailable !== null ? `_HDU Avalable_: ${h.hduAvailable} (${percentages.hdu})` : ""}
      ${h.oxygenTotal !== 0 && h.oxygenAvailable !== null ? `_Oxygen Available_: ${h.oxygenAvailable} (${percentages.oxygen}})` : ""}
      ${h.generalTotal !== 0 && h.generalAvailable !== null ? `_General Available_: ${h.generalAvailable} (${percentages.general})` : ""}
      ${
        h.ventilatorsTotal !== 0 && h.ventilatorsAvailable !== null
          ? `_Ventilators Available_: ${h.ventilatorsAvailable} (${percentages.ventilators})`
          : ""
      }
      ${h.phone !== null ? `_Phone_: ${h.phone}` : ""}
      ${h.phone !== null ? `_Website_: ${h.website}` : ""}
  `

  return removeEmptyLines(formatted)
}

No todos los hospitales tienen camas disponibles en la UCI, la UHD y la Sala General, ni todos disponen de oxígeno y ventiladores. Si el hospital no dispone de alguno de ellos, podemos omitir esa información en el mensaje. El hospital no dispone de un determinado tipo de cama, oxígeno o respiradores si el total disponible es cero o si el valor del campo disponible es nulo.

También imprimimos el ID del hospital ya que el usuario tiene que proporcionar el ID del hospital en el comando obtener direcciones. También mostramos al usuario el porcentaje de camas ocupadas entre paréntesis.

Sigue quedando una línea en blanco si no se dispone de cama, ventilador u oxígeno. Para solucionarlo, creamos otra función auxiliar para eliminar estas líneas en blanco.

Para comprobar si hay líneas vacías, utilizamos una expresión regular corta que comprueba si la línea tiene más de un carácter de espacio:

// src/utils.js

const removeEmptyLines = (string) => {
  const lines = string.split("\n")
  const newLines = []

  for (const line of lines) {
    // Continue if the line is a blank line
    if (line.match(/^\s*$/)) continue
    newLines.push(line)
  }

  return newLines.join("\n")
}

A continuación, cree la función para formatear una lista de hospitales recorriendo todos los hospitales, obteniendo la cadena formateada para ese hospital y añadiendo una línea vacía entre cada hospital:

// src/utils.js

const getFormattedHospitals = (hospitals) => {
  let message = ""

  for (const hospital of hospitals) {
    const formattedHospital = getFormattedHospital(hospital)
    message += formattedHospital + "\n\n"
  }

  return message
}

module.exports = {
  ...
  getFormattedHospitals,
}

El controlador de comandos

Todas las consultas GraphQL se almacenarán en el archivo queries.js en la carpeta src carpeta.

Utilizamos la siguiente consulta GraphQL para buscar un hospital en una ubicación determinada y recuperar los datos necesarios. El argumento name del campo localidad se utiliza para indicar a la API los datos de la localidad que estamos consultando. El argumento first del campo hospitals especifica cuántos hospitales queremos que devuelva la API, y el argumento searchQuery proporciona la consulta de búsqueda.

// src/queries.js

const searchGraphQLQuery = gql`
  query($location: String, $query: String) {
    # get hospitals from a city named Bengaluru in the state of Karnataka
    locality(name: $location) {
      hospitals(first: 5, searchQuery: $query) {
        edges {
          node {
            id
            name
            phone
            website
            address
            latitude
            longitude

            icuAvailable
            hduAvailable
            oxygenAvailable
            generalAvailable
            ventilatorsAvailable

            icuOccupied
            hduOccupied
            oxygenOccupied
            generalOccupied
            ventilatorsOccupied

            icuTotal
            hduTotal
            oxygenTotal
            generalTotal
            ventilatorsTotal
          }
        }
      }
    }
  }
`

module.exports = {
  searchGraphQLQuery,
}
`

Tenga en cuenta que no necesitamos envolver nuestra consulta GraphQL con la etiqueta gql etiqueta. Pero, con las extensiones y herramientas adecuadas, podemos obtener un buen resaltado de sintaxis y comprobación de tipo.

A continuación, tenemos que asignar los nombres de ubicación al argumento name del campo locality que tiene la forma <city/district_name>-<state_name>. La asignación de la city/district a la localidad name puede convertirse en el siguiente objeto en JavaScript:

// src/utils.js

const locationKey = {
  bangalore: "bengaluru-karnataka",
  bengaluru: "bengaluru-karnataka",
  pune: "pune-maharashtra",
  kohlapur: "kohlapur-maharashtra",
  sangli: "sangli-maharashtra",
  satara: "satara-maharashtra",
  solapur: "solapur-maharashtra",
  anantapur: "anantapur-andhra pradesh",
  chittoor: "chittoor-andhra pradesh",
  "east godavari": "east godavari-andhra pradesh",
  guntur: "guntur-andhra pradesh",
  krishna: "krishna-andhra pradesh",
  kurnool: "kurnool-andhra pradesh",
  prakasam: "prakasam-andhra pradesh",
  nellore: "spsr nellore-andhra pradesh",
  srikakulam: "srikakulam-andhra pradesh",
  vishakapatanam: "vishakapatanam-andhra pradesh",
  vizianagaram: "vizianagaram-andhra pradesh",
  "west godavari": "west godavari-andhra pradesh",
  kadapa: "kadapa-andhra pradesh",
}

module.exports = {
  ...
  locationKey,
}

El texto que introduzca el usuario para buscar un hospital en una determinada localidad tendrá el formato search <search-query> in <location>. Vamos a crear la función handleSearch que obtiene la consulta de búsqueda y la ubicación del mensaje del usuario. Esta función es responsable de ejecutar la consulta de búsqueda y enviar la respuesta adecuada al usuario:

// src/app.js

const { fixedMessages, sendMessage, locationKey, getFormattedHospitals } = require("./utils")

const handleSearch = async (message, to) => {
  // extract the search query and location from the message
  const searchRegex = /^search (?<searchQuery>[a-zA-Z0-9_ ]+) in (?<location>[a-zA-Z0-9_ ]+)$/i
  const match = searchRegex.exec(message)

  if (match === null) {
    sendMessage(to, "Please enter the hospital name you want to search for and the location")
    return
  }

  const { groups: { searchQuery, location } } = match

  if (!Object.keys(locationKey).includes(location)) {
    sendMessage(to, "Invalid location entered. Type *cities* to look at all the cities available")
    return
  }

  try {
    const data = await client.request(searchGraphQLQuery, {
      location: locationKey[location],
      query: searchQuery,
    })

   const { edges } = data.locality.hospitals
    const hospitals = edges.map((item) => item.node)

    if (edges.length === 0) {
      sendMessage(to, "Sorry, there were no hospitals that matched your search 🙁")
      return
    }

    const formattedMessage = getFormattedHospitals(hospitals)
    sendMessage(to, formattedMessage)
  } catch (error) {
    sendMessage(to, "Sorry, there were no hospitals that matched your search 🙁")
  }
}

El comando Cómo llegar

El último comando que queda por implementar es el de obtener direcciones. El formato del comando es get directions to <hospital-id>.

En primer lugar, cree la consulta que obtendrá la información del hospital necesaria para enviar la ubicación del hospital al usuario:

// src/queries.js

const directionsGraphQLQuery = gql`
  query($id: ID!) {
    hospital(id: $id) {
      id
      longitude
      latitude
      name
      address
    }
  }
`

module.exports = {
  ...
  directionsGraphQLQuery,
}

Ahora, vamos a crear una función handleDirections que enviará las indicaciones al hospital solicitadas por el usuario. Si el usuario utiliza WhatsApp, enviamos un mensaje de tipo location que incluye el nombre y la dirección del hospital. Como no hay mensajes de tipo location en Messenger, enviamos un mensaje de tipo text que contiene un enlace de Google Maps a la ubicación del hospital:

// src/app.js

const handleDirections = async (message, to) => {
  const directionsRegex = /^get directions to (?<hospitalId>\d+)$/i
  const match = directionsRegex.exec(message)

  if (match === null) {
    sendMessage(to, "Please enter a valid Hospital ID")
    return
  }

  const hospitalId = getEncodedString(parseInt(match.groups.hospitalId))

  try {
    const { hospital } = await client.request(directionsGraphQLQuery, {
      id: hospitalId,
    })

    if (to.type === "whatsapp") {
      sendMessage(
        to,
        {
          type: "location",
          location: {
            longitude: hospital.longitude,
            latitude: hospital.latitude,
            name: hospital.name,
            address: hospital.address,
          },
        },
        "custom"
      )
    } else {
      const link = `https://maps.google.com/maps?q=${hospital.latitude},${hospital.longitude}`
      const message = `${link}\n*${hospital.name}*\n${hospital.address}\n`

      sendMessage(to, message)
    }
  } catch (error) {
    sendMessage(to, "Please enter a valid Hospital ID")
  }
}

Y eso es todo, ¡hemos terminado! Puedes probar todos los comandos tú mismo en el Sandbox y debería verse algo parecido a lo siguiente.

Final Example

El futuro

  • En este momento, nuestro webhook está abierto a todo Internet. Necesitamos una manera de asegurarnos de que sólo la API de Messages API de Vonage pueda acceder al punto final. Lee más sobre cómo limitar el acceso usando tokens JWT.

  • Si te gusta el aprendizaje automático, puedes utilizar el Procesamiento del Lenguaje Natural para hacer que el bot sea más natural de usar.

  • Utilizar el ID del hospital en el comando para obtener direcciones no es la mejor experiencia para el usuario. En su lugar, intente implementar un comando en el que el usuario introduzca el nombre del hospital al que desea llegar. Si hay varios hospitales con un nombre similar, devuelva una lista de hospitales junto con sus ID y pida al usuario que introduzca el ID del hospital al que quiere llegar.

El código final del tutorial se puede encontrar en GitHub.

Compartir:

https://a.storyblok.com/f/270183/400x514/4b6d6f78ee/shreyas-sreenivas.png
Shreyas Sreenivas

Shreyas Sreenivas is a High School Student currently residing in Bangalore, India. He has experience in building applications in JavaScript, TypeScript, Python and PHP. He's also a huge fan of React, GraphQL, Graph Databases and Serverless.