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 lanzó recientemente Conversaciones Programables. Permite a las empresas integrar plataformas de comunicación como WhatsApp, Messenger y SMS en sus sistemas, utilizando una única API.

Quise probarlo, así que construí una lista de tareas pendiente con un bot de WhatsApp, 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 contarte todo al respecto.

Ahora, trabajo en MessageBird, así que pude simplemente sumergirme y comenzar a construir. Si intentas esto, necesitarás solicitar acceso anticipado. Pero una vez que estés configurado con un canal de WhatsApp, puedes iniciar sesión en el Dashboard en el sitio web de MessageBird y comenzar.

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

Quise probarlo, así que construí una lista de tareas pendiente con un bot de WhatsApp, 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 contarte todo al respecto.

Ahora, trabajo en MessageBird, así que pude simplemente sumergirme y comenzar a construir. Si intentas esto, necesitarás solicitar acceso anticipado. Pero una vez que estés configurado con un canal de WhatsApp, puedes iniciar sesión en el Dashboard en el sitio web de MessageBird y comenzar.

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

Quise probarlo, así que construí una lista de tareas pendiente con un bot de WhatsApp, 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 contarte todo al respecto.

Ahora, trabajo en MessageBird, así que pude simplemente sumergirme y comenzar a construir. Si intentas esto, necesitarás solicitar acceso anticipado. Pero una vez que estés configurado con un canal de WhatsApp, puedes iniciar sesión en el Dashboard en el sitio web de MessageBird y comenzar.

Configurando tu entorno de bot de WhatsApp

Lo primero que hice fue leer la docs. 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 el mantenimiento. Como apenas estaba empezando a programarlo, decidí usar ngrok. Crea un túnel desde el internet público hasta tu querido puerto localhost 5007. ¡Engage!

ngrok http 5007 -region eu -subdomain todobot

Lo primero que hice fue leer la docs. 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 el mantenimiento. Como apenas estaba empezando a programarlo, decidí usar ngrok. Crea un túnel desde el internet público hasta tu querido puerto localhost 5007. ¡Engage!

ngrok http 5007 -region eu -subdomain todobot

Lo primero que hice fue leer la docs. 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 el mantenimiento. Como apenas estaba empezando a programarlo, decidí usar ngrok. Crea un túnel desde el internet público hasta tu querido puerto localhost 5007. ¡Engage!

ngrok http 5007 -region eu -subdomain todobot

Creando tu webhook y conectando Bird

Ahora, necesitaba hacer una llamada a la Programmable Conversations API 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 Conversations API 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 un webhook payload:

{
  "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. Comencemos 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, necesitaba hacer una llamada a la Programmable Conversations API 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 Conversations API 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 un webhook payload:

{
  "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. Comencemos 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, necesitaba hacer una llamada a la Programmable Conversations API 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 Conversations API 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 un webhook payload:

{
  "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. Comencemos 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")
}

Enviando respuestas y gestionando mensajes

Ahora, para 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
}

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


Estas son las razones por las que usar Conversations API de Bird hace que el desarrollo de bots de WhatsApp sea rápido y escalable:

Característica

Lo que soluciona

ID de conversación unificada

Mantén un solo hilo a través de aplicaciones como WhatsApp, WeChat, Messenger

API única para todos los canales

Reutiliza la lógica del bot sin reescribirla para cada plataforma

Automatización impulsada por Webhook

Gestión de respuestas rápidas sin sondeo

Archivar + reabrir temas

Organiza el historial de soporte y flujos de trabajo

Estructura segura para la concurrencia

Maneja múltiples chats a la vez de manera confiable


Ahora, avancemos 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 a respond.

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


handle analizará simplísticamente 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 para la concurrencia que mapea IDs de conversación a listas de tareas.

¡Una lista de tareas es una cadena slice segura para la concurrencia! Todo en memoria!

Ahora, para 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
}

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


Estas son las razones por las que usar Conversations API de Bird hace que el desarrollo de bots de WhatsApp sea rápido y escalable:

Característica

Lo que soluciona

ID de conversación unificada

Mantén un solo hilo a través de aplicaciones como WhatsApp, WeChat, Messenger

API única para todos los canales

Reutiliza la lógica del bot sin reescribirla para cada plataforma

Automatización impulsada por Webhook

Gestión de respuestas rápidas sin sondeo

Archivar + reabrir temas

Organiza el historial de soporte y flujos de trabajo

Estructura segura para la concurrencia

Maneja múltiples chats a la vez de manera confiable


Ahora, avancemos 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 a respond.

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


handle analizará simplísticamente 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 para la concurrencia que mapea IDs de conversación a listas de tareas.

¡Una lista de tareas es una cadena slice segura para la concurrencia! Todo en memoria!

Ahora, para 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
}

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


Estas son las razones por las que usar Conversations API de Bird hace que el desarrollo de bots de WhatsApp sea rápido y escalable:

Característica

Lo que soluciona

ID de conversación unificada

Mantén un solo hilo a través de aplicaciones como WhatsApp, WeChat, Messenger

API única para todos los canales

Reutiliza la lógica del bot sin reescribirla para cada plataforma

Automatización impulsada por Webhook

Gestión de respuestas rápidas sin sondeo

Archivar + reabrir temas

Organiza el historial de soporte y flujos de trabajo

Estructura segura para la concurrencia

Maneja múltiples chats a la vez de manera confiable


Ahora, avancemos 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 a respond.

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


handle analizará simplísticamente 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 para la concurrencia que mapea IDs de conversación a listas de tareas.

¡Una lista de tareas es una cadena slice segura para la concurrencia! Todo en memoria!

Archivar conversaciones y escalar tu bot

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

Gestionando el ciclo de vida de la conversación

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 ese 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 pendientes.

Pero oye, Programmable Conversations es una solución omnicanal. ¿Qué pasa si quieres 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 dirigir 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 Conversations API 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: Wabot on Github, solicita acceso anticipado a WhatsApp visitando la página de WhatsApp y haz clic en el botón Contactar con Ventas para completar el formulario. ¡Feliz creación de bots!

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

Gestionando el ciclo de vida de la conversación

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 ese 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 pendientes.

Pero oye, Programmable Conversations es una solución omnicanal. ¿Qué pasa si quieres 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 dirigir 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 Conversations API 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: Wabot on Github, solicita acceso anticipado a WhatsApp visitando la página de WhatsApp y haz clic en el botón Contactar con Ventas para completar el formulario. ¡Feliz creación de bots!

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

Gestionando el ciclo de vida de la conversación

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 ese 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 pendientes.

Pero oye, Programmable Conversations es una solución omnicanal. ¿Qué pasa si quieres 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 dirigir 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 Conversations API 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: Wabot on Github, solicita acceso anticipado a WhatsApp visitando la página de WhatsApp y haz clic en el botón Contactar con Ventas para completar 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 AI-native que escala con tu negocio.

© 2025 Bird

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

La plataforma completa AI-native que escala con tu negocio.

© 2025 Bird