Reach

Grow

Manage

Automate

Reach

Grow

Manage

Automate

如何使用 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!

让我们为您联系Bird专家。
在30分钟内见证Bird的全部威力。

通过提交,您同意 Bird 可能会就我们的产品和服务与您联系。

您可以随时取消订阅。查看Bird的隐私声明以获取有关数据处理的详细信息。

Newsletter

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

让我们为您联系Bird专家。
在30分钟内见证Bird的全部威力。

通过提交,您同意 Bird 可能会就我们的产品和服务与您联系。

您可以随时取消订阅。查看Bird的隐私声明以获取有关数据处理的详细信息。

Newsletter

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

让我们为您联系Bird专家。
在30分钟内见证Bird的全部威力。

通过提交,您同意 Bird 可能会就我们的产品和服务与您联系。

您可以随时取消订阅。查看Bird的隐私声明以获取有关数据处理的详细信息。

R

Reach

G

Grow

M

Manage

A

Automate

Newsletter

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