
Bird niedawno uruchomił Programowalne Rozmowy. Umożliwia to firmom łączenie platform komunikacyjnych, takich jak WhatsApp, Messenger i SMS, w ich systemach - przy użyciu jednego interfejsu API.
Bird niedawno uruchomił Programmable Conversations. Pozwala firmom integrować platformy komunikacyjne, takie jak WhatsApp, Messenger i SMS, do ich systemów — używając jednego API.
Chciałem to wypróbować, więc zbudowałem listę zadań do zrobienia przez WhatsApp bota, bo kto nie potrzebuje zautomatyzowanej listy zadań do pomocy w organizacji dnia? Może to brzmieć skomplikowanie, ale było to naprawdę łatwe i chciałbym Wam o tym opowiedzieć.
Teraz pracuję w MessageBird, więc mogłem po prostu zacząć budować. Jeśli to wypróbujesz, będziesz musiał złożyć prośbę o wcześniejszy dostęp. Ale gdy już skonfigurujesz kanał WhatsApp, możesz zalogować się na Dashboard na stronie MessageBird i zacząć działać.
Pierwszą rzeczą, którą zrobiłem, było przeczytanie dokumentacji. Dowiedziałem się, że aby otrzymywać wiadomości od bota, będę musiał używać webhooka. Oznaczało to, że mój bot musiałby być dostępny z Internetu. Ponieważ dopiero zaczynałem to kodować, zdecydowałem się użyć ngrok. Tworzy tunel z Internetu publicznego do Twojego lokalnego portu 5007. Działaj!
ngrok http 5007 -region eu -subdomain todobot
Następnie musiałem wykonać połączenie do Programmable Conversations API, aby utworzyć webhooka. To jest POST do https://conversations.messagebird.com/v1/webhooks i wygląda to mniej więcej tak:
func main() {// zdefiniuj payload json webhooka
wh := struct {
Events []string `json:"events"`
ChannelID string `json:"channelId"`
URL string `json:"url"`
} {// chcemy być powiadamiani na URL-u
URL: "https://todobot.eu.ngrok.io/create-hook",
// kiedykolwiek wiadomość zostaje utworzona
Events: []string{"message.created"},
// na kanale WhatsApp z ID
ChannelID: "23a780701b8849f7b974d8620a89a279",
}// koduj payload do json
var b bytes.Buffer
err := json.NewEncoder(&b).Encode(&wh)
if err != nil {
panic(err)
}// utwórz zapytanie http i ustaw nagłówek autoryzacji
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")// wyślij zapytanie http
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()// czy wszystko ok?
body, _ := ioutil.ReadAll(resp.Body)
if resp.StatusCode >= http.StatusBadRequest {
panic(fmt.Errorf("Zły kod odpowiedzi z API przy próbie utworzenia webhooka: %s. Body: %s", resp.Status, string(body)))
} else {
log.Println("Wszystko dobrze. treść odpowiedzi: ", string(body))
}
}
Słodko. Teraz Conversations API będzie wykonywać żądanie POST do:
https://todobot.eu.ngrok.io/create-hook za każdym razem, gdy nowa wiadomość zostanie utworzona na kanale WhatsApp, który skonfigurowałeś wcześniej.
Tak wygląda payload webhooka:
{
"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"
}
Chcemy odpowiedzieć na te wiadomości. Zacznijmy od ich echa, co ty na to?
// zdefiniuj struktury, w których będziemy parsować ładunek webhooka intype 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 jest handlerem http, który będzie obsługiwał żądania webhooków
func createHookHandler(w http.ResponseWriter, r *http.Request) {
// parsuj przychodzący ładunek json
whp := &whPayload{}
err := json.NewDecoder(r.Body).Decode(whp)
if err != nil {
log.Println("Err: otrzymano dziwne ciało na webhooku")
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "Internal Server Error")
return
}if whp.Message.Direction != "received" {
// otrzymasz *wszystkie* wiadomości na webhooku. Nawet te, które bot wysyła do kanału. Nie chcemy na nie odpowiadać.
fmt.Fprintf(w, "ok")
return
}// echo: odpowiadaj tym, co dostajemy
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")
}
Teraz czas na interesującą część. Wykonaj żądanie POST do:
“https://conversations.messagebird.com/v1/conversations/<conversationID>/messages” aby odpowiedzieć na żądanie.
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("Błąd kodowania bufora: %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("Zły kod odpowiedzi z API przy próbie utworzenia wiadomości: %s. Body: %s", resp.Status, string(body))
}log.Println("Wszystko dobrze. Treść odpowiedzi: ", string(body))
return nil
}
I oto Wszystko. To wszystko, czego potrzebujesz, aby stworzyć bota zachowującego się jak 5-letni człowiek.
Teraz zróbmy krok w kierunku stworzenia całej listy zadań. Najpierw zmodyfikuj funkcję createHookHandler tak, aby wywoływała nową funkcję handleMessage zamiast respond.
func createHookHandler(w http.ResponseWriter, r *http.Request) {
...
err = handleMessage(whp)
...
}
handle będzie w prosty sposób parsować wiadomości, wykonywać pewną pracę i wybierać odpowiedź. Spójrzmy na polecenie “add”:
func handleMessage(whp *whPayload) error {
// każda konwersacja ma listę zadań
list := manager.fetch(whp.Conversation.ID)
// parsuj polecenie z treści wiadomości: to pierwsze słowo
text := whp.Message.Content.Text
text = regexp.MustCompile(" +").ReplaceAllString(text, " ")
parts := strings.Split(text, " ")
command := strings.ToLower(parts[0])
// domyślna wiadomość
responseBody := "Nie rozumiem. Wpisz 'help', aby uzyskać pomoc."
switch command {
...
case "add":
if len(parts) < 2 {
return respond(whp.Conversation.ID, "err... polecenie 'add' wymaga drugiego parametru: przedmiotu, który chcesz zapisać. Coś takiego jak 'add buy milk'.")
}
// pobierz element z treści wiadomości
item := strings.Join(parts[1:], " ")list.add(item)
responseBody = "dodano."
...
return respond(whp.Conversation.ID, responseBody)
}
Tu konfigurujemy: list := manager.fetch(whp.Conversation.ID). W zasadzie “manager” to bezpieczna współbieżnie mapa, która mapuje ID konwersacji na listy zadań.
Lista zadań to bezpieczna współbieżnie tablica stringów. Wszystko w pamięci!
Jeszcze jedna ważna rzecz! Możesz archiwizować rozmowy. W niektórych aplikacjach, takich jak CRM-y, ważne jest śledzenie pewnych interakcji — aby śledzić skuteczność pracowników obsługi klienta, na przykład. Conversations API umożliwia archiwizację rozmowy, aby “zamknąć” temat. Jeśli użytkownik/klient wyśle kolejną wiadomość, Conversations API automatycznie otworzy nowy temat.
Poza tym. Wykonanie żądania PATCH do https://conversations.messagebird.com/v1/conversations/{id} z właściwym statusem w ciele pozwala na archiwizację rozmowy o tym id. Robimy to z poleceniem “bye”:
case "bye":
archiveConversation(whp.Conversation.ID)
manager.close(whp.Conversation.ID)
responseBody = "bye!"
archiveConversation wykona żądanie PATCH, a manager.close(whp.Conversation.ID) usunie rozmowę z listą zadań.
Ale hej, Programmable Conversations to rozwiązanie omni-channel. Co jeśli chcesz ponownie wykorzystać kod bota na innej platformie, jak WeChat? Jak byś to zrobił?
Wystarczy stworzyć nowego webhooka, aby kierować ten kanał! Webhook, który wysyła żądania na ten sam URL https://todobot.eu.ngrok.io/create-hook, którego używaliśmy dla WhatsApp!
To zadziała, ponieważ kod obsługi zawsze używa conversationID z payloadu webhooka do odpowiedzi na wiadomości, zamiast zakodowanego channelID. Conversations API MessageBird’a automatycznie określi kanał dla konwersacji, nad którą chce się przesyłać wiadomość.
Chcesz zbudować swojego własnego bota? Zobacz pełny kod na Github: https://github.com/marcelcorso/wabot, złoż prośbę o wcześniejszy dostęp do WhatsApp poprzez ten link i zacznij budować. Życzymy udanego tworzenia botów!