Bygga en WhatsApp-to-do-lista-bot med MessageBirds programmerbara konversationer

MessageBird lanserade nyligen programmerbara konversationer. Det låter företag blanda kommunikationsplattformar som WhatsApp, Messenger och SMS i sina system - med ett enda API.

Jag ville ge den en virvel, så jag byggde en WhatsApp-bot-to-do-lista och jag skulle vilja berätta allt om det.

Nu arbetar jag på MessageBird, så jag kunde bara dyka in och börja bygga. Om du försöker detta måste du begära tidig åtkomst. Men när du är klar med en WhatsApp-kanal kan du logga in på instrumentpanelen på MessageBird-webbplatsen och komma igång.

Det första jag gjorde var att läsa dokumenten. Jag lärde mig att för att få meddelanden från botten skulle jag behöva använda en webhook. Det innebar att min bot skulle behöva vara tillgänglig från internet. Eftersom jag just började koda det, bestämde jag mig för att använda ngrok. Det skapar en tunnel från det offentliga internet till din kära localhost-port 5007. Engagera!

ngrok http 5007 -region eu - subdomain todobot

Därefter behövde jag ringa till rapporterings-API: t för att skapa webbhaken. Det är ett POST till https://conversations.messagebird.com/v1/webhooks och det ser ut så här:

func main () {
        // definiera webhook json nyttolast wh: = struct {Händelser [] sträng `json:" händelser "` ChannelID-sträng `json: 'channelId'` URL-sträng `json: 'url'`}}
                // vi vill bli meddelade på URL-adressen: "https://todobot.eu.ngrok.io/create-hook", // när ett meddelande skapas Händelser: [] string {"message.created"} , // på WhatsApp-kanalen med ID ChannelID: "23a780701b8849f7b974d8620a89a279",}
        // kodar nyttolasten till json var b bytes.Buffer err: = json.NewEncoder (& b). Kod (& wh) om fel! = nil {panic (err)}
        // skapa http-begäran och ange behörighetsrubrikreq, err: = http.NewRequest ("POST", "https://conversations.messagebird.com/v1/webhooks", & b) req.Header.Set ("Autorisation" , "AccessKey todo-your-access-key") req.Header.Set ("Content-Type", "application / json")
        // avfyr http-begärarklienten: = & http.Client {} resp, err: = client.Do (req) om fel! = nil {panic (err)} skjuta upp resp.Body.Close ()
// är allting okej? body, _: = ioutil.ReadAll (resp.Body) om resp.StatusCode> = http.StatusBadRequest {panik (fmt.Errorf ("Dålig svarskod från api när du försöker skapa webbhook:% s. Kropp:% s", resp.Status, string (body)))} else {log.Println ("All good. response body:", string (body))}}

Ljuv. Nu kommer Conversations API att göra en POST-begäran för att:

https://todobot.eu.ngrok.io/create-hook varje gång ett nytt meddelande skapas på WhatsApp-kanalen du ställde in tidigare.

Så här ser en nyttolast på webhook ut:

{"konversation": {"id": "55c66895c22a40e39a8e6bd321ec192e", "contactId": "db4dd5087fb343738e968a323f640576", "status": "aktiv", "createDatetime": "2018-08-17T10: 14: 14Z", "uppdaterad "2018-08-17T14: 30: 31.915292912Z", "lastReceivedDatetime": "2018-08-17T14: 30: 31.898389294Z"}, "meddelande": {"id": "ddb150149e2c4036a48f581544e22cfe", "conversId": "55c66895a1c6a "," channelId ":" 23a780701b8849f7b974d8620a89a279 "," status ":" fick "," typ ":" text "," direction ":" fick "," innehåll ": {" text ":" lägg till köpa mjölk "}, "createDatetime": "2018-08-17T14: 30: 31.898389294Z", "updatedDatetime": "2018-08-17T14: 30: 31.915292912Z"}, "type": "message.created"}

Vi vill svara på dessa meddelanden. Låt oss börja med att återkalla dem, vad säger du?

// definiera strukturerna där vi kommer att analysera nyttolasten för webhook
typ whPayload struct {Konversationssamtal `json:" konversation "` Meddelande meddelande `json:“ meddelande ”` Skriv sträng `json:“ typ ”`}
skriv meddelandestruktur {ID-sträng `json:" id "` Riktningsträng `json: 'riktning'` Skriv sträng `json: 'typ'` Innehållsinnehåll `json: 'innehåll'`}
skriv innehållsstruktur {Textsträng `json:" text "`}
skriv konversationsstruktur {ID-sträng `json:" id "`}
func main () {http.HandleFunc ("/ create-hook", createHookHandler) log.Fatal (http.ListenAndServe (* httpListenAddress, nil))}}
// createHookHandler är en http-hanterare som hanterar webbhookförfrågningar func createHookHandler (w http.ResponseWriter, r * http.Request) {// analysera den inkommande json nyttolasten whp: = & whPayload {} err: = json.NewDecoder (r.Body ) .Decode (whp) om fel! = Nil {log.Println ("Err: fick konstig kropp på webhooken") w.WriteHeader (http.StatusInternalServerError) fmt.Fprintf (w, "Internal Server Error") return}
if whp.Message.Direction! = "emot" {// får du * alla * meddelanden på webhooken. Även de som denna bot skickar till kanalen. Vi vill inte svara på dem. fmt.Fprintf (w, "ok") return}
        // echo: svara på vad vi får fel = svara (whp.Conversation.ID, whp.Message.Content.Text) om fel! = nil {log.Println ("Err:", fel) w.WriteHeader (http.StatusInternalServerError ) fmt.Fprintf (w, "Internt serverfel") 
                lämna tillbaka }
        w.WriteHeader (http.StatusOK) fmt.Fprintf (w, "ok")}

Nu för den intressanta delen. Gör en POST-begäran för att:

”https://conversations.messagebird.com/v1/conversations/ / meddelanden ”för att besvara begäran.

func svar (konversationsID, svarBodsträng) fel {u: = fmt.Sprintf ("https://conversations.messagebird.com/v1/conversations/%s/messages", konversationsID)
        msg: = meddelande {Innehåll: innehåll {Text: responsBody,}, Skriv: "text",}
        var b bytes.Buffer err: = json.NewEncoder (& b). Kod (& msg) om err! = nil {return fmt.Errorf ("Felkodningsbuffert:% v", err)}
req, err: = http.NewRequest ("POST", u.String () och b) req.Header.Set ("Autorisation", "AccessKey todo-your-access-key") req.Header.Set ("Content" -Typ "," applikation / json ")
        client: = & http.Client {} resp, err: = client.Do (req) om fel! = noll {return err} skjut upp resp.Body.Close ()
        body, _: = ioutil.ReadAll (resp.Body) om resp.StatusCode! = http.StatusCreated {return fmt.Errorf ("Dålig svarskod från api när du försöker skapa meddelande:% s. Kropp:% s", resp .Status, sträng (kropp))}
        log.Println ("Allt bra. Svarorgan:", string (body)) return nil}

Där. Detta är allt du behöver för att skapa en bot som fungerar som en 5-årig människa.

Låt oss nu göra ett tryck mot att bygga upp hela listan. Först ändra createHookHandler-funktionen lite så att den anropar den nya handleMessage-funktionen istället för att svara.

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

handtag förenklar meddelanden på ett enkelt sätt, gör lite arbete och väljer svaret. Låt oss titta på kommandot "lägg till":

func handleMessage (whp * whPayload) -fel {// varje konversation har en todo-lista: = manager.fetch (whp.Conversation.ID) // analysera kommandot från meddelandekroppen: det är den första ordtexten: = whp.Message .Content.Text text = regexp.MustCompile ("+"). ErsättAllString (text, "") delar: = strings.Split (text, "") kommando: = strängar.ToLower (delar [0]) // standardmeddelande responseBody: = "Jag förstår inte. Skriv 'hjälp' för att få hjälp." växla kommando {... case "add": om len (delar) <2 {return respond (whp.Conversation.ID, "err ..." add "-kommandot behöver en andra parameter: todo-objektet du vill spara . Något som "lägg till köpa mjölk". ")} // hämta objektet från meddelandets kroppspost: = strängar. Gå med (delar [1:]," ")
list.add (item) responseBody = "lagt till." ... returnera svar (whp.Conversation.ID, responseBody)}

Här ställer vi in: lista: = manager.fetch (whp.Conversation.ID). I grund och botten är ”manager” en karta med samtidighet som kartlägger konversations-ID: er till att göra-listor.

En uppgiftslista är en strängskiva för samtidigt säkerhet. Allt i minnet!

En annan viktig sak! Du kan arkivera konversationer. I vissa applikationer, som CRM, är det viktigt att hålla reda på vissa interaktioner - till exempel för att spåra effektiviteten hos anställda i kundtjänst. Med Conversations API kan du arkivera en konversation för att ”stänga” ämnet. Om användaren / kunden skickar ett nytt meddelande öppnar Conversations API automatiskt ett nytt ämne.

Också. Genom att göra PATCH-begäran till https://conversations.messagebird.com/v1/conversations/{id} med rätt status på kroppen kan du arkivera konversationen med den iden. Vi gör detta med "bye" -kommandot:

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

archiveConversation gör PATCH-begäran och manager.close (whp.Conversation.ID) kommer att ta bort samtalet om att göra-lista.

Men hey, programmerbara konversationer är en omnikanal-lösning. Tänk om du ville återanvända koden för bot för en annan plattform, som WeChat? Hur skulle du göra det?

Skapa bara en ny webhook för att rikta in sig mot den kanalen! En webhook som skickar förfrågningar till samma https://todobot.eu.ngrok.io/create-hook url som vi använde för WhatsApp!

Detta fungerar eftersom hanterarkoden alltid använder konversations-ID från nyttolasten för webhook för att svara på meddelandena istället för ett hårdkodat kanalID. MessageBirds API för konversationer avgör automatiskt kanalen för konversationen att skicka ditt meddelande till.

Vill du bygga din egen bot? Ta en titt på hela koden på Github: https://github.com/marcelcorso/wabot, begär tidig tillgång till WhatsApp via denna länk och börja bygga direkt. Glad tappning!