Jak zbudować bota WhatsApp do list zadań przy użyciu API Programowalnych Rozmów firmy Bird
Ptak
5 lut 2020
1 min read

Kluczowe Wnioski
Bird’s Programmable Conversations API łączy WhatsApp, Messenger i SMS w jedną warstwę komunikacyjną, upraszczając rozwój botów wielokanałowych.
Możesz szybko stworzyć prototyp bota do listy zadań na WhatsApp za pomocą webhooks i prostych żądań POST.
Narzędzia takie jak ngrok pozwalają na udostępnianie lokalnego serwera do testowania webhooków bez skomplikowanej konfiguracji hostingu.
API obsługuje konwersacje w wielu kanałach, umożliwiając jedną bazę logiki dla WhatsApp, WeChat i innych aplikacji.
Użyj punktu końcowego archiveConversation do zamykania konwersacji lub „tematów”, co jest idealne do wsparcia technicznego lub śledzenia przepływu pracy.
Logika bota może zarządzać współbieżnymi konwersacjami bezpiecznie w pamięci za pomocą prostej struktury danych.
Ten sam obsługiwacz webhooków działa we wszystkich kanałach—Bird automatycznie przekierowuje odpowiedzi na podstawie ID początkowej konwersacji.
Q&A Highlights
Jak trudne jest zbudowanie bota WhatsApp używając Bird’s API?
To zaskakująco łatwe. Za pomocą webhooka i kilku wywołań API możesz zbudować funkcjonalnego bota, który w ciągu kilku minut będzie czytał i odpowiadał na wiadomości.
Czy potrzebuję specjalnej konfiguracji, aby odbierać wiadomości?
Tak — bot musi być dostępny z internetu. Narzędzia takie jak ngrok pomagają stworzyć bezpieczny tunel z twojej lokalnej maszyny.
Czy mogę używać tej samej bazy kodu dla różnych aplikacji do przesyłania wiadomości?
Absolutnie. Conversations API abstrakcyjnie przedstawia kanały, dzięki czemu Twój bot może działać na WhatsApp, WeChat lub Messenger przy użyciu identycznej logiki.
Jak zamknąć lub zresetować wątek czatu?
Wyślij żądanie PATCH do punktu końcowego rozmowy z odpowiednim statusem, aby ją zarchiwizować. Każda nowa wiadomość automatycznie otwiera nową rozmowę.
Gdzie mogę znaleźć przykładowy kod?
A: Pełna działająca wersja demonstracyjna — Wabot on GitHub — pokazuje implementację obsługi wiadomości, współbieżności i archiwizacji.
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 wprowadził na rynek Programowalne Rozmowy. Pozwala firmom integrować platformy komunikacyjne takie jak WhatsApp, Messenger i SMS ze swoimi systemami — przy użyciu jednego API.
Chciałem tego spróbować, więc stworzyłem listę rzeczy do zrobienia w postaci bota WhatsApp, bo kto nie potrzebuje zautomatyzowanej listy zadań do organizowania swojego dnia? Może to brzmieć skomplikowanie, ale w rzeczywistości było to łatwe, i chciałbym wam o tym opowiedzieć.
Teraz pracuję w MessageBird, więc mogłem po prostu wskoczyć i zacząć budować. Jeśli spróbujesz tego, będziesz musiał poprosić o wczesny dostęp. Kiedy 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 odbierać wiadomości od bota, muszę używać webhooka. To oznaczało, że mój bot musi być dostępny z internetu. Podczas tworzenia takich interfejsów API, ważne jest, aby śledzić najlepsze praktyki wersjonowania API dla utrzymania. Ponieważ dopiero zaczynałem to kodować, postanowiłem użyć ngrok. Tworzy tunel z publicznego internetu do drogiego lokalnego portu 5007. Akcja!
ngrok http 5007 -region eu -subdomain todobot
Następnie musiałem wykonać wywołanie do API Programowalnych Rozmów, aby utworzyć webhooka. Jest to POST do https://conversations.messagebird.com/v1/webhooks i wygląda tak:
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)) } }
Słodko. Teraz API Rozmów wykona żądanie POST do:
https://todobot.eu.ngrok.io/create-hook za każdym razem, gdy nowa wiadomość zostanie utworzona na wcześniejszym skonfigurowanym kanale WhatsApp.
Tak wygląda ładunek 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 odpowiadać na te wiadomości. Zacznijmy od echowania ich, co ty na to?
// 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") }
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("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 }
To wszystko, czego potrzebujesz, aby stworzyć bota, który zachowuje się jak 5-letni człowiek.
Teraz, zróbmy krok naprzód w kierunku budowania pełnej listy rzeczy do zrobienia. Najpierw zmodyfikuj funkcję createHookHandler, 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 analizować wiadomości, wykonywać pewne czynności i wybierać odpowiedź. Przyjrzyjmy się poleceniu „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) }
Ustawiamy tutaj:list := manager.fetch(whp.Conversation.ID). Zasadniczo, „manager” to bezpieczna mapka konkurecyjna, która mapuje ID rozmowy na listy rzeczy do zrobienia.
Lista rzeczy do zrobienia to bezpieczny dla współbieżności ciąg stringów. Wszystko w pamięci!
Kolejna ważna rzecz! Możesz archiwizować rozmowy. W niektórych zastosowaniach, takich jak CRM, ważne jest śledzenie pewnych interakcji — aby śledzić skuteczność pracowników obsługi klienta, na przykład. API Rozmów pozwala archiwizować rozmowę, aby „zamknąć” temat. Jeśli użytkownik/klient wyśle inną wiadomość, API Rozmów automatycznie otworzy nowy temat.
Również. Wykonanie żądania PATCH do https://conversations.messagebird.com/v1/conversations/{id} z odpowiednim statusem w treści umożliwia archiwizację rozmowy z tym id. Robimy to za pomocą polecenia „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 listy rzeczy do zrobienia.
Ale hej, Programowalne Rozmowy to rozwiązanie wielokanałowe. Co, jeśli chciałbyś użyć kodu bota na inną platformę, np. WeChat? To podejście wielokanałowe jest częścią strategii przekierowywania zapytań na tańsze kanały. Jak byś się do tego zabrał?
Po prostu stwórz nowego webhooka, aby celować w ten kanał! Webhooka, który wysyła żądania do tego samego adresu https://todobot.eu.ngrok.io/create-hook, którego używaliśmy dla WhatsApp!
To zadziała, ponieważ kod handlera zawsze używa conversationID z payload webhooka, aby odpowiadać na wiadomości, zamiast używać zakodowanego na stałe channelID. API Rozmów MessageBird automatycznie określi kanał dla rozmowy, aby przesłać wiadomość.
Chcesz zbudować swojego własnego bota? Zobacz pełny kod na GitHubie: Wabot na Githubie, poproś o wczesny dostęp do WhatsApp, odwiedzając stronę WhatsApp i klikając przycisk Contact Sales, aby wypełnić formularz. Szczęśliwego botowania!
Bird niedawno wprowadził na rynek Programowalne Rozmowy. Pozwala firmom integrować platformy komunikacyjne takie jak WhatsApp, Messenger i SMS ze swoimi systemami — przy użyciu jednego API.
Chciałem tego spróbować, więc stworzyłem listę rzeczy do zrobienia w postaci bota WhatsApp, bo kto nie potrzebuje zautomatyzowanej listy zadań do organizowania swojego dnia? Może to brzmieć skomplikowanie, ale w rzeczywistości było to łatwe, i chciałbym wam o tym opowiedzieć.
Teraz pracuję w MessageBird, więc mogłem po prostu wskoczyć i zacząć budować. Jeśli spróbujesz tego, będziesz musiał poprosić o wczesny dostęp. Kiedy 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 odbierać wiadomości od bota, muszę używać webhooka. To oznaczało, że mój bot musi być dostępny z internetu. Podczas tworzenia takich interfejsów API, ważne jest, aby śledzić najlepsze praktyki wersjonowania API dla utrzymania. Ponieważ dopiero zaczynałem to kodować, postanowiłem użyć ngrok. Tworzy tunel z publicznego internetu do drogiego lokalnego portu 5007. Akcja!
ngrok http 5007 -region eu -subdomain todobot
Następnie musiałem wykonać wywołanie do API Programowalnych Rozmów, aby utworzyć webhooka. Jest to POST do https://conversations.messagebird.com/v1/webhooks i wygląda tak:
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)) } }
Słodko. Teraz API Rozmów wykona żądanie POST do:
https://todobot.eu.ngrok.io/create-hook za każdym razem, gdy nowa wiadomość zostanie utworzona na wcześniejszym skonfigurowanym kanale WhatsApp.
Tak wygląda ładunek 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 odpowiadać na te wiadomości. Zacznijmy od echowania ich, co ty na to?
// 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") }
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("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 }
To wszystko, czego potrzebujesz, aby stworzyć bota, który zachowuje się jak 5-letni człowiek.
Teraz, zróbmy krok naprzód w kierunku budowania pełnej listy rzeczy do zrobienia. Najpierw zmodyfikuj funkcję createHookHandler, 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 analizować wiadomości, wykonywać pewne czynności i wybierać odpowiedź. Przyjrzyjmy się poleceniu „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) }
Ustawiamy tutaj:list := manager.fetch(whp.Conversation.ID). Zasadniczo, „manager” to bezpieczna mapka konkurecyjna, która mapuje ID rozmowy na listy rzeczy do zrobienia.
Lista rzeczy do zrobienia to bezpieczny dla współbieżności ciąg stringów. Wszystko w pamięci!
Kolejna ważna rzecz! Możesz archiwizować rozmowy. W niektórych zastosowaniach, takich jak CRM, ważne jest śledzenie pewnych interakcji — aby śledzić skuteczność pracowników obsługi klienta, na przykład. API Rozmów pozwala archiwizować rozmowę, aby „zamknąć” temat. Jeśli użytkownik/klient wyśle inną wiadomość, API Rozmów automatycznie otworzy nowy temat.
Również. Wykonanie żądania PATCH do https://conversations.messagebird.com/v1/conversations/{id} z odpowiednim statusem w treści umożliwia archiwizację rozmowy z tym id. Robimy to za pomocą polecenia „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 listy rzeczy do zrobienia.
Ale hej, Programowalne Rozmowy to rozwiązanie wielokanałowe. Co, jeśli chciałbyś użyć kodu bota na inną platformę, np. WeChat? To podejście wielokanałowe jest częścią strategii przekierowywania zapytań na tańsze kanały. Jak byś się do tego zabrał?
Po prostu stwórz nowego webhooka, aby celować w ten kanał! Webhooka, który wysyła żądania do tego samego adresu https://todobot.eu.ngrok.io/create-hook, którego używaliśmy dla WhatsApp!
To zadziała, ponieważ kod handlera zawsze używa conversationID z payload webhooka, aby odpowiadać na wiadomości, zamiast używać zakodowanego na stałe channelID. API Rozmów MessageBird automatycznie określi kanał dla rozmowy, aby przesłać wiadomość.
Chcesz zbudować swojego własnego bota? Zobacz pełny kod na GitHubie: Wabot na Githubie, poproś o wczesny dostęp do WhatsApp, odwiedzając stronę WhatsApp i klikając przycisk Contact Sales, aby wypełnić formularz. Szczęśliwego botowania!
Bird niedawno wprowadził na rynek Programowalne Rozmowy. Pozwala firmom integrować platformy komunikacyjne takie jak WhatsApp, Messenger i SMS ze swoimi systemami — przy użyciu jednego API.
Chciałem tego spróbować, więc stworzyłem listę rzeczy do zrobienia w postaci bota WhatsApp, bo kto nie potrzebuje zautomatyzowanej listy zadań do organizowania swojego dnia? Może to brzmieć skomplikowanie, ale w rzeczywistości było to łatwe, i chciałbym wam o tym opowiedzieć.
Teraz pracuję w MessageBird, więc mogłem po prostu wskoczyć i zacząć budować. Jeśli spróbujesz tego, będziesz musiał poprosić o wczesny dostęp. Kiedy 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 odbierać wiadomości od bota, muszę używać webhooka. To oznaczało, że mój bot musi być dostępny z internetu. Podczas tworzenia takich interfejsów API, ważne jest, aby śledzić najlepsze praktyki wersjonowania API dla utrzymania. Ponieważ dopiero zaczynałem to kodować, postanowiłem użyć ngrok. Tworzy tunel z publicznego internetu do drogiego lokalnego portu 5007. Akcja!
ngrok http 5007 -region eu -subdomain todobot
Następnie musiałem wykonać wywołanie do API Programowalnych Rozmów, aby utworzyć webhooka. Jest to POST do https://conversations.messagebird.com/v1/webhooks i wygląda tak:
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)) } }
Słodko. Teraz API Rozmów wykona żądanie POST do:
https://todobot.eu.ngrok.io/create-hook za każdym razem, gdy nowa wiadomość zostanie utworzona na wcześniejszym skonfigurowanym kanale WhatsApp.
Tak wygląda ładunek 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 odpowiadać na te wiadomości. Zacznijmy od echowania ich, co ty na to?
// 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") }
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("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 }
To wszystko, czego potrzebujesz, aby stworzyć bota, który zachowuje się jak 5-letni człowiek.
Teraz, zróbmy krok naprzód w kierunku budowania pełnej listy rzeczy do zrobienia. Najpierw zmodyfikuj funkcję createHookHandler, 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 analizować wiadomości, wykonywać pewne czynności i wybierać odpowiedź. Przyjrzyjmy się poleceniu „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) }
Ustawiamy tutaj:list := manager.fetch(whp.Conversation.ID). Zasadniczo, „manager” to bezpieczna mapka konkurecyjna, która mapuje ID rozmowy na listy rzeczy do zrobienia.
Lista rzeczy do zrobienia to bezpieczny dla współbieżności ciąg stringów. Wszystko w pamięci!
Kolejna ważna rzecz! Możesz archiwizować rozmowy. W niektórych zastosowaniach, takich jak CRM, ważne jest śledzenie pewnych interakcji — aby śledzić skuteczność pracowników obsługi klienta, na przykład. API Rozmów pozwala archiwizować rozmowę, aby „zamknąć” temat. Jeśli użytkownik/klient wyśle inną wiadomość, API Rozmów automatycznie otworzy nowy temat.
Również. Wykonanie żądania PATCH do https://conversations.messagebird.com/v1/conversations/{id} z odpowiednim statusem w treści umożliwia archiwizację rozmowy z tym id. Robimy to za pomocą polecenia „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 listy rzeczy do zrobienia.
Ale hej, Programowalne Rozmowy to rozwiązanie wielokanałowe. Co, jeśli chciałbyś użyć kodu bota na inną platformę, np. WeChat? To podejście wielokanałowe jest częścią strategii przekierowywania zapytań na tańsze kanały. Jak byś się do tego zabrał?
Po prostu stwórz nowego webhooka, aby celować w ten kanał! Webhooka, który wysyła żądania do tego samego adresu https://todobot.eu.ngrok.io/create-hook, którego używaliśmy dla WhatsApp!
To zadziała, ponieważ kod handlera zawsze używa conversationID z payload webhooka, aby odpowiadać na wiadomości, zamiast używać zakodowanego na stałe channelID. API Rozmów MessageBird automatycznie określi kanał dla rozmowy, aby przesłać wiadomość.
Chcesz zbudować swojego własnego bota? Zobacz pełny kod na GitHubie: Wabot na Githubie, poproś o wczesny dostęp do WhatsApp, odwiedzając stronę WhatsApp i klikając przycisk Contact Sales, aby wypełnić formularz. Szczęśliwego botowania!



