Product

Soluciones

Recursos

Compañía

Product

Soluciones

Recursos

Compañía

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.

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.

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.

Business in a box.

Descubre nuestras soluciones.

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

Quería probarlo, así que construí un bot de WhatsApp de lista de tareas, porque ¿quién no necesita una lista de tareas automatizada para ayudar a 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 sumergirme y empezar a construir. Si pruebas esto, necesitarás solicitar acceso anticipado. Pero una vez que te registras con 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. Descubrí 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 el mantenimiento. Como estaba empezando a codificarlo, decidí usar ngrok. Crea un túnel desde el Internet público hasta tu querido puerto localhost 5007. ¡En marcha!

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 cree un nuevo mensaje en el canal de WhatsApp que configuraste anteriormente.

Así es como se ve una carga útil 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é dices?


// 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, la parte interesante. Haz 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
}

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

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

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

handle analizará los mensajes de manera simplista, realizará 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 para la concurrencia que asigna IDs de conversación a listas de tareas.

Una lista de tareas es un segmento de cadenas seguro para la concurrencia. ¡Todo en memoria!

¡Otra cosa importante! Puedes archivar conversaciones. En algunas aplicaciones, como CRM, es importante hacer un seguimiento de ciertas interacciones, para controlar la efectividad de los empleados de atención 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á un nuevo tema automáticamente.

Además, hacer 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 esa id. Hacemos esto con el comando “bye”:

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

archiveConversation hará 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 multicanal. ¿Qué pasa si quisieras reutilizar el código del bot para una plataforma diferente, como WeChat? Este enfoque multicanal es parte de la estrategia de desviar consultas a canales de menor costo. ¿Cómo lo harías?

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

Esto funcionará porque el código del controlador siempre utiliza el conversationID de la carga útil del webhook para responder los mensajes en lugar de un channelID codificado. La API de Conversations de MessageBird determinará automáticamente el canal para la conversación y enviará tu mensaje.

¿Quieres construir tu propio bot? Echa un vistazo al código completo en Github: https://github.com/marcelcorso/wabot, solicita acceso anticipado a WhatsApp visitando la página de WhatsApp y haz clic en el botón Contactar Ventas para llenar el formulario. ¡Feliz creación de bots!

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

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

Product

Soluciones

Recursos

Compañía

Configuración de privacidad

Próximamente

Social

Newsletter

Mantente al día con Bird a través de actualizaciones semanales en tu buzón.

Signup

© 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.

Product

Soluciones

Recursos

Compañía

Configuración de privacidad

Social

Newsletter

Mantente al día con Bird a través de actualizaciones semanales en tu buzón.

Signup

© 2025 Bird