Bereik

Grow

Manage

Automate

Bereik

Grow

Manage

Automate

Hoe bouw je een WhatsApp-bot voor takenlijsten met behulp van Bird’s Programmable Conversations API

Bird

5 feb 2020

WhatsApp

1 min read

Hoe bouw je een WhatsApp-bot voor takenlijsten met behulp van Bird’s Programmable Conversations API

Bird

5 feb 2020

WhatsApp

1 min read

Hoe bouw je een WhatsApp-bot voor takenlijsten met behulp van Bird’s Programmable Conversations API

Bird heeft onlangs Programmable Conversations gelanceerd. Hiermee kunnen bedrijven communicatieplatforms zoals WhatsApp, Messenger en SMS in hun systemen integreren — met behulp van een enkele API.

Business in a box.

Ontdek onze oplossingen.

Bird lanceerde onlangs Programmeerbare Conversaties. Hiermee kunnen bedrijven communicatieplatformen zoals WhatsApp, Messenger en SMS integreren in hun systemen — met behulp van een enkele API.

Ik wilde het eens proberen, dus ik bouwde een WhatsApp-bot takenlijst, want wie heeft er geen geautomatiseerde takenlijst nodig om hun dag te organiseren? Het klinkt misschien ingewikkeld, maar het was eigenlijk eenvoudig, en ik wil je er alles over vertellen.

Nu werk ik bij MessageBird, dus ik kon gewoon aan de slag gaan. Als jij dit probeert, moet je vroegtijdige toegang aanvragen. Maar zodra je een WhatsApp-kanaal hebt opgezet, kun je inloggen op het Dashboard op de website van MessageBird en beginnen.

Het eerste dat ik deed, was de documentatie lezen. Ik ontdekte dat ik een webhook moest gebruiken om berichten van de bot te ontvangen. Dit betekende dat mijn bot toegankelijk moest zijn vanaf het internet. Bij het bouwen van API's zoals deze is het belangrijk om API-versiebeheer best practices te volgen voor onderhoudbaarheid. Omdat ik net begon met programmeren, besloot ik ngrok te gebruiken. Het creëert een tunnel van het publieke internet naar je localhost poort 5007. Engage!

ngrok http 5007 -region eu -subdomain todobot

Vervolgens moest ik een oproep doen naar de Programmable Conversations API om de webhook te creëren. Het is een POST naar https://conversations.messagebird.com/v1/webhooks en het ziet er ongeveer zo uit:


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

Zoet. Nu gaat de Conversations API een POST-verzoek doen naar:

https://todobot.eu.ngrok.io/create-hook telkens wanneer er een nieuw bericht wordt aangemaakt op het WhatsApp-kanaal dat je eerder hebt ingesteld.

Dit is hoe een webhook payload eruitziet:


{
  "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"
}

We willen die berichten beantwoorden. Laten we beginnen door ze te echoën, wat zeg je?


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

Nu, voor het interessante deel. Doe een POST-verzoek naar:

https://conversations.messagebird.com/v1/conversations/<conversationID>/messages om het verzoek te beantwoorden.


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
}

Zo. Dit is alles wat je nodig hebt om een bot te creëren die zich gedraagt als een 5-jarige mens.

Nu, laten we een duwtje in de richting van het bouwen van de hele takenlijst geven. Eerst, wijzig de createHookHandler functie een beetje zodat het de nieuwe handleMessage functie aanroept in plaats van reageren.

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

handle zal op een eenvoudige manier de berichten parseren, wat werk doen, en de respons kiezen. Laten we kijken naar het “add” commando:

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

Hier stellen we in: list := manager.fetch(whp.Conversation.ID). Kortom, “manager” is een concurrency-veilige map die gesprek-ID's toewijst aan takenlijsten.

Een takenlijst is een concurrency-veilige stringreeks. Alles in het geheugen!

Nog iets belangrijks! Je kunt gesprekken archiveren. In sommige toepassingen, zoals CRM's, is het belangrijk om bepaalde interacties bij te houden — bijvoorbeeld de effectiviteit van klantenservice-medewerkers volgen. De Conversations API laat je een gesprek archiveren om het onderwerp te “sluiten”. Als de gebruiker/klant een ander bericht stuurt, opent de Conversations API automatisch een nieuw onderwerp.

Ook. Door een PATCH-verzoek te doen naar https://conversations.messagebird.com/v1/conversations/{id} met de juiste status in de body kun je het gesprek met die id archiveren. We doen dit met het “bye” commando:

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

archiveConversation zal de PATCH-verzoek uitvoeren en manager.close(whp.Conversation.ID) zal de takenlijst van het gesprek verwijderen.

Maar hey, Programmeerbare Conversaties is een omni-channel oplossing. Wat als je de code van de bot wilde hergebruiken voor een ander platform, zoals WeChat? Deze multikanaalbenadering is onderdeel van het afbuigen van vragen naar goedkopere kanalen strategie. Hoe zou je dat aanpakken?

Maak gewoon een nieuwe webhook om dat kanaal te targeten! Een webhook die verzoeken stuurt naar dezelfde https://todobot.eu.ngrok.io/create-hook url die we voor WhatsApp hebben gebruikt!

Dit zal werken omdat de handlercode altijd het conversationID van de webhook payload gebruikt om de berichten te beantwoorden in plaats van een hard-coded channelID. MessageBird’s Conversations API zal automatisch het kanaal van het gesprek bepalen om je bericht over te brengen.

Wil je je eigen bot bouwen? Bekijk de volledige code op Github: https://github.com/marcelcorso/wabot, vraag vroegtijdige toegang tot WhatsApp aan via deze link en begin direct te bouwen. Veel succes met botten!

Laten we je in contact brengen met een Bird-expert.
Bekijk de volledige kracht van de Bird in 30 minuten.

Door te verzenden, ga je ermee akkoord dat Bird contact met je mag opnemen over onze producten en diensten.

U kunt zich op elk moment afmelden. Zie Bird's Privacyverklaring voor details over gegevensverwerking.

Nieuwsbrief

Blijf op de hoogte met Bird via wekelijkse updates in je inbox.

Laten we je in contact brengen met een Bird-expert.
Bekijk de volledige kracht van de Bird in 30 minuten.

Door te verzenden, ga je ermee akkoord dat Bird contact met je mag opnemen over onze producten en diensten.

U kunt zich op elk moment afmelden. Zie Bird's Privacyverklaring voor details over gegevensverwerking.

Nieuwsbrief

Blijf op de hoogte met Bird via wekelijkse updates in je inbox.

Laten we je in contact brengen met een Bird-expert.
Bekijk de volledige kracht van de Bird in 30 minuten.

Door te verzenden, ga je ermee akkoord dat Bird contact met je mag opnemen over onze producten en diensten.

U kunt zich op elk moment afmelden. Zie Bird's Privacyverklaring voor details over gegevensverwerking.

R

Bereik

G

Grow

M

Manage

A

Automate

Nieuwsbrief

Blijf op de hoogte met Bird via wekelijkse updates in je inbox.