Product

解决方案

资源

Company

Product

解决方案

资源

Company

如何使用 Bird 的可编程对话 API 构建一个用于待办事项的 WhatsApp 机器人

2020年2月5日

WhatsApp

1 min read

如何使用 Bird 的可编程对话 API 构建一个用于待办事项的 WhatsApp 机器人

2020年2月5日

WhatsApp

1 min read

如何使用 Bird 的可编程对话 API 构建一个用于待办事项的 WhatsApp 机器人

Bird 最近推出了可编程对话。它使公司能够将 WhatsApp、Messenger 和 SMS 等通信平台融入他们的系统——使用一个单一的 API。

Business in a box.

探索我们的解决方案。

Bird 最近推出了可编程会话。这让公司可以将通讯平台(如 WhatsApp、Messenger 和 SMS)与他们的系统融合——使用单一 API。

我想试试,所以我构建了一个 WhatsApp bot 待办事项清单,因为谁不需要一个自动化的待办事项清单来帮助组织他们的一天呢?这可能听起来很复杂,但实际上很简单,我想告诉你们全部细节。

现在,我在 MessageBird 工作,所以我可以直接进入并开始构建。如果你尝试此操作,你需要 请求提前访问权限。但一旦你设置了 WhatsApp 渠道,你可以登录到 MessageBird 网站上的仪表板并开始。

我做的第一件事是阅读 文档。我了解到,为了从 bot 获取消息,我需要使用一个 webhook。这意味着我的 bot 需要从互联网访问。在构建这样的 API 时,遵循 API 版本控制最佳实践 以维护可维护性很重要。因为我刚刚开始编码,所以我决定使用 ngrok。它从公共互联网到你心爱的本地主机端口 5007 创建一个通道。参与吧!

ngrok http 5007 -region eu -subdomain todobot

接下来,我需要调用可编程会话 API 以创建 webhook。这是一个 POST 到 https://conversations.messagebird.com/v1/webhooks,看起来像这样:


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))
  }
}

很好。现在会话 API 将在 WhatsApp 渠道上创建新消息时进行 POST 请求到:

https://todobot.eu.ngrok.io/create-hook

这是一个 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"
}

我们想回答这些消息。让我们从回显它们开始,你说呢?


// 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")
}

现在,进入有趣的部分。进行一个 POST 请求到:

https://conversations.messagebird.com/v1/conversations/<conversationID>/messages 来回答请求。


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
}

在那里。这就是你需要做的所有事情来创建一个像五岁小孩一样行动的 bot。

现在,让我们推动构建整个待办事项清单。首先,稍微修改 createHookHandler 函数,使其调用新的 handleMessage 函数而不是响应。

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

handle 将简单地解析消息,进行一些工作,并选择响应。让我们看看“添加”命令:

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)
}

在这里,我们设置:list := manager.fetch(whp.Conversation.ID)。基本上,“manager” 是一个并发安全的映射,将对话 ID 映射到待办事项清单。

待办事项清单是一个并发安全的字符串切片。所有都在内存中!

另一件重要的事情!你可以存档对话。在某些应用中,如 CRM,跟踪某些交互很重要——例如,跟踪客户支持员工的有效性。会话 API 允许你存档对话以“关闭”主题。如果用户/客户发送另一个消息,会话 API 会自动打开一个新主题。

此外。对 https://conversations.messagebird.com/v1/conversations/{id} 的进行 PATCH 请求,并在主体上有正确的状态,允许你存档具有该 ID 的对话。我们通过“再见”命令来做到这一点:

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

archiveConversation 将执行 PATCH 请求,manager.close(whp.Conversation.ID) 将删除待办事项清单对话。

但是嘿,可编程会话是一个多渠道解决方案。如果你想为不同的平台(比如 WeChat)重用 bot 的代码怎么办?这种多渠道方法是 低成本渠道转移查询 策略的一部分。你会怎么做呢?

只需创建一个新的 webhook 来目标该渠道!一个 webhook 向我们用于 WhatsApp 的相同 https://todobot.eu.ngrok.io/create-hook 网址发送请求!

这将起作用,因为处理程序代码始终使用 webhook 负载中的 conversationID 来回答消息,而不是硬编码的 channelID。MessageBird 的会话 API 将自动确定将消息发送到的对话渠道。

你想构建自己的 bot 吗?查看完整代码在 Github 上:https://github.com/marcelcorso/wabot,通过此 链接 请求 WhatsApp 的提前访问权限并直接开始构建。快乐的 botting!

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

这个完整的AI原生平台可以随着您的业务进行扩展。

Product

解决方案

资源

Company

隐私设置

社交

Newsletter

通过每周更新到您的收件箱,随时了解 Bird 的最新动态。

Signup

© 2025 Bird

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

这个完整的AI原生平台可以随着您的业务进行扩展。

Product

解决方案

资源

Company

隐私设置

社交

Newsletter

通过每周更新到您的收件箱,随时了解 Bird 的最新动态。

Signup

© 2025 Bird