Cómo construir un bot de WhatsApp para listas de tareas utilizando la API de Conversaciones Programables de Bird.

Pájaro

5 feb 2020

WhatsApp

1 min read

Cómo construir un bot de WhatsApp para listas de tareas utilizando la API de Conversaciones Programables de Bird.

Puntos clave

    • Bird’s Programmable Conversations API unifica WhatsApp, Messenger y SMS en una única capa de comunicación, simplificando el desarrollo de bots multicanal.

    • Puedes prototipar rápidamente un bot de lista de tareas de WhatsApp utilizando webhooks y solicitudes POST simples.

    • Herramientas como ngrok te permiten exponer tu servidor local para pruebas de webhook sin una configuración de alojamiento compleja.

    • La API maneja conversaciones a través de múltiples canales, permitiendo una única base lógica para WhatsApp, WeChat y otras aplicaciones.

    • Usa el endpoint archiveConversation para cerrar conversaciones o "temas", ideal para soporte o seguimiento de flujo de trabajo.

    • La lógica del bot puede gestionar conversaciones concurrentes de forma segura en memoria con una estructura de datos simple.

    • El mismo manejador de webhooks funciona en todos los canales—Bird enruta automáticamente las respuestas basándose en el ID de conversación de origen.

Destacados de Q&A

  • ¿Qué tan difícil es construir un bot de WhatsApp usando el API de Bird?

    Es sorprendentemente fácil. Con un webhook y algunas llamadas a API, puedes construir un bot funcional que lea y responda a mensajes en minutos.

  • ¿Necesito una configuración especial para recibir mensajes?

    Sí — el bot debe ser accesible desde internet. Herramientas como ngrok ayudan a crear un túnel seguro desde tu máquina local.

  • ¿Puedo usar el mismo código base para diferentes aplicaciones de mensajería?

    Absolutamente. La API de Conversaciones abstrae los canales, por lo que tu bot puede funcionar en WhatsApp, WeChat, o Messenger usando la misma lógica.

  • ¿Cómo cierro o restablezco un hilo de chat?

    Envía una solicitud PATCH al endpoint de la conversación con el estado adecuado para archivarla. Cualquier mensaje nuevo abre automáticamente una nueva conversación.

  • ¿Dónde puedo encontrar código de ejemplo?

    A: La demostración completa en funcionamiento — Wabot on GitHub — muestra la implementación del manejo de mensajes, concurrencia y archivado.

Bird ha lanzado recientemente Conversaciones Programables. Permite a las empresas combinar plataformas de comunicación como WhatsApp, Messenger y SMS en sus sistemas, utilizando una única API.

Bird lanzó recientemente Programmable Conversations. Permite a las empresas integrar plataformas de comunicación como WhatsApp, Messenger y SMS en sus sistemas utilizando una sola API.

Quería probarlo, así que construí un bot de listas de tareas de WhatsApp, porque ¿quién no necesita una lista de tareas automatizada para organizar su día? Puede sonar complicado, pero en realidad fue fácil y me gustaría contártelo todo.

Ahora, trabajo en MessageBird, así que pude empezar a construir de inmediato. Si pruebas esto, necesitarás solicitar acceso anticipado. Pero una vez que tengas configurado un canal de WhatsApp, puedes iniciar sesión en el Dashboard en el sitio web de MessageBird y comenzar.

Lo primero que hice fue leer la documentación. Aprendí que, para recibir mensajes del bot, tendría que usar un webhook. Esto significaba que mi bot necesitaría ser accesible desde Internet. Al construir APIs como esta, es importante seguir las mejores prácticas de versionado de API para su mantenibilidad. Como apenas estaba empezando a codificarlo, decidí usar ngrok. Crea un túnel desde Internet público hasta el querido puerto localhost 5007. ¡Vamos a ello!

ngrok http 5007 -region eu -subdomain todobot

Luego, necesitaba hacer una llamada a la API de Programmable Conversations para crear el webhook. Es un POST a https://conversations.messagebird.com/v1/webhooks y se ve algo así:


func main() {// define the webhook json payload
  wh := struct {
         Events    []string `json:"events"`
         ChannelID string   `json:"channelId"`
         URL       string   `json:"url"`
  } {
    // we would like to be notified on the URL
    URL:       "https://todobot.eu.ngrok.io/create-hook",
    // whenever a message gets created
    Events:    []string{"message.created"},
    // on the WhatsApp channel with ID
    ChannelID: "23a780701b8849f7b974d8620a89a279",
  }
  
  // encode the payload to json
  var b bytes.Buffer
  err := json.NewEncoder(&b).Encode(&wh)
  if err != nil {
    panic(err)
  }
  
  // create the http request and set authorization header
  req, err := http.NewRequest("POST", "https://conversations.messagebird.com/v1/webhooks", &b)
  req.Header.Set("Authorization", "AccessKey todo-your-access-key")
  req.Header.Set("Content-Type", "application/json") // fire the http request
  client := &http.Client{}
  resp, err := client.Do(req)
  if err != nil {
         panic(err)
  }
  defer resp.Body.Close()// is everything ok?
  body, _ := ioutil.ReadAll(resp.Body)
  if resp.StatusCode >= http.StatusBadRequest {
    panic(fmt.Errorf("Bad response code from api when trying to create webhook: %s. Body: %s", resp.Status, string(body)))
  } else {
    log.Println("All good. response body: ", string(body))
  }
}

Genial. Ahora la API de Conversations va a hacer una solicitud POST a:

https://todobot.eu.ngrok.io/create-hook cada vez que se crea un nuevo mensaje en el canal de WhatsApp que configuraste antes.

Así es como se ve un payload de webhook:


{
  "conversation":{
    "id":"55c66895c22a40e39a8e6bd321ec192e",
    "contactId":"db4dd5087fb343738e968a323f640576",
    "status":"active",
    "createdDatetime":"2018-08-17T10:14:14Z",
    "updatedDatetime":"2018-08-17T14:30:31.915292912Z",
    "lastReceivedDatetime":"2018-08-17T14:30:31.898389294Z"
  },
  "message":{
    "id":"ddb150149e2c4036a48f581544e22cfe",
    "conversationId":"55c66895c22a40e39a8e6bd321ec192e",
    "channelId":"23a780701b8849f7b974d8620a89a279",
    "status":"received",
    "type":"text",
    "direction":"received",
    "content":{
      "text":"add buy milk"
    },
    "createdDatetime":"2018-08-17T14:30:31.898389294Z",
    "updatedDatetime":"2018-08-17T14:30:31.915292912Z"
  },
  "type":"message.created"
}

Queremos responder a esos mensajes. Empecemos por repetirlos, ¿qué te parece?


// define the structs where we'll parse the webhook payload into
type whPayload struct {
  Conversation conversation `json:"conversation"`
  Message      message      `json:"message"`
  Type         string       `json:"type"`
}

type message struct {
  ID        string  `json:"id"`
  Direction string  `json:"direction"`
  Type      string  `json:"type"`
  Content   content `json:"content"`
}

type content struct {
  Text string `json:"text"`
}

type conversation struct {
  ID string `json:"id"`
}

func main() {
  http.HandleFunc("/create-hook", createHookHandler)
  log.Fatal(http.ListenAndServe(*httpListenAddress, nil))
}
// createHookHandler is an http handler that will handle webhook requests
func createHookHandler(w http.ResponseWriter, r *http.Request) {
  // parse the incoming json payload
  whp := &whPayload{}
  err := json.NewDecoder(r.Body).Decode(whp)
  if err != nil {
    log.Println("Err: got weird body on the webhook")
    w.WriteHeader(http.StatusInternalServerError)
    fmt.Fprintf(w, "Internal Server Error")
    return
  } 
  if whp.Message.Direction != "received" {
    // you will get *all* messages on the webhook. Even the ones this bot sends to the channel. We don't want to answer those.
    fmt.Fprintf(w, "ok")
    return
  } // echo: respond what we get
  err = respond(whp.Conversation.ID, whp.Message.Content.Text)
  
  if err != nil {
    log.Println("Err: ", err)
    w.WriteHeader(http.StatusInternalServerError)
    fmt.Fprintf(w, "Internal Server Error")return
  }
  w.WriteHeader(http.StatusOK)
  fmt.Fprintf(w, "ok")
}

Ahora, para la parte interesante. Hacer una solicitud POST a:

https://conversations.messagebird.com/v1/conversations/<conversationID>/messages para responder a la solicitud.


func respond(conversationID, responseBody string) error {
  u := fmt.Sprintf("https://conversations.messagebird.com/v1/conversations/%s/messages", conversationID)msg := message{
  Content: content{
    Text: responseBody,
  },
  Type: "text",
  }
  var b bytes.Buffer
  err := json.NewEncoder(&b).Encode(&msg)
  if err != nil {
    return fmt.Errorf("Error encoding buffer: %v", err)
  }
  req, err := http.NewRequest("POST", u.String(), &b)
  req.Header.Set("Authorization", "AccessKey todo-your-access-key")
  req.Header.Set("Content-Type", "application/json")client := &http.Client{}
  resp, err := client.Do(req)
  if err != nil {
    return err
  }
  defer resp.Body.Close()body, _ := ioutil.ReadAll(resp.Body)
  if resp.StatusCode != http.StatusCreated {
    return fmt.Errorf("Bad response code from api when trying to create message: %s. Body: %s", resp.Status, string(body))
  }
  log.Println("All good. Response body: ", string(body))
  return nil
}

Ahí lo tienes. Esto es todo lo que necesitas para crear un bot que actúe como un humano de 5 años.

Ahora, avancemos hacia la construcción de toda la lista de tareas. Primero, modifica la función createHookHandler un poco para que llame a la nueva función handleMessage en lugar de respond.

func createHookHandler(w http.ResponseWriter, r *http.Request) {
  ...
  err = handleMessage(whp)
  ...
}

handle analizará simplistamente los mensajes, hará algún trabajo y elegirá la respuesta. Veamos el comando “add”:

func handleMessage(whp *whPayload) error {
  // every conversation has a todo list
  list := manager.fetch(whp.Conversation.ID)
  // parse the command from the message body: it's the first word
  text := whp.Message.Content.Text
  text = regexp.MustCompile(" +").ReplaceAllString(text, " ")
  parts := strings.Split(text, " ")
  command := strings.ToLower(parts[0])
  // default message
  responseBody := "I don't understand. Type 'help' to get help."
  switch command {
  ...
  case "add":
    if len(parts) < 2 {
           return respond(whp.Conversation.ID, "err... the 'add' command needs a second param: the todo item you want to save. Something like 'add buy milk'.")
    }
    // get the item from the message body
    item := strings.Join(parts[1:], " ")list.add(item)
    responseBody = "added."
  ...
  return respond(whp.Conversation.ID, responseBody)
}

Aquí, configuramos: list := manager.fetch(whp.Conversation.ID). Básicamente, “manager” es un mapa seguro de concurrencia que asigna IDs de conversación a listas de tareas.

Una lista de tareas es un corte de cadenas seguro de concurrencia. ¡Todo en memoria!

¡Otra cosa importante! Puedes archivar conversaciones. En algunas aplicaciones, como los CRM, es importante hacer un seguimiento de ciertas interacciones — para seguir la efectividad de los empleados de soporte al cliente, por ejemplo. La API de Conversations te permite archivar una conversación para “cerrar” el tema. Si el usuario/cliente envía otro mensaje, la API de Conversations abrirá automáticamente un nuevo tema.

Además. Realizar una solicitud PATCH a https://conversations.messagebird.com/v1/conversations/{id} con el estado correcto en el cuerpo te permite archivar la conversación con ese id. Hacemos esto con el comando “bye”:

case "bye":
  archiveConversation(whp.Conversation.ID)
  manager.close(whp.Conversation.ID)
  responseBody = "bye!"

archiveConversation realizará la solicitud PATCH y manager.close(whp.Conversation.ID) eliminará la conversación de la lista de tareas.

Pero oye, Programmable Conversations es una solución de múltiples canales. ¿Qué pasa si quisieras reutilizar el código del bot para una plataforma diferente, como WeChat? Este enfoque de múltiples canales es parte de la estrategia de desviar consultas a canales de costo más bajo. ¿Cómo lo harías?

¡Simplemente crea un nuevo webhook para apuntar a ese canal! ¡Un webhook que envíe solicitudes a la misma URL https://todobot.eu.ngrok.io/create-hook que usamos para WhatsApp!

Esto funcionará porque el código del manejador siempre usa el conversationID del payload del webhook para responder a los mensajes en lugar de un channelID codificado. La API de Conversations de MessageBird determinará automáticamente el canal de la conversación para enviar tu mensaje.

¿Quieres construir tu propio bot? Echa un vistazo al código completo en Github: Wabot en Github, solicita acceso anticipado a WhatsApp visitando la página de WhatsApp y haciendo clic en el botón Contact Sales para llenar el formulario. ¡Feliz creación de bots!

Otras noticias

Leer más de esta categoría

A person is standing at a desk while typing on a laptop.

La plataforma completa nativa de AI que escala con tu negocio.

© 2025 Bird

A person is standing at a desk while typing on a laptop.

La plataforma completa nativa de AI que escala con tu negocio.

© 2025 Bird