Echo Dot (3rd Gen) - Smart speaker with Alexa - Charcoal

Use your voice to play a song, artist, or genre through Amazon Music, Apple Music, Spotify, Pandora, and others. With compatible Echo devices in different rooms, you can fill your whole home with music.

Buy Now

Wireless Rechargeable Battery Powered WiFi Camera.

Wireless Rechargeable Battery Powered WiFi Camera is home security camera system lets you listen in and talk back through the built in speaker and microphone that work directly through your iPhone or Android Mic.

Buy Now

Facebook Messenger bot: A tutorial in Go

0
108


Webhooks are the backbone of how this works. The chat platform receives a message from a user, packs up the message into a nice HTTP POST request and sends it off to a webserver that you control. On the webserver, you handle it however you would like, before sending information back to the platform, usually in the form of a return message.

Let’s start by creating a webserver in Go. The following code handles GET and POST requests to http://localhost:8000/, which then will verify whether its a valid Messenger request. Take note that we’ve skipped all error checks here, and the variable “secretKey”.

package mainimport (
"github.com/gorilla/mux"
"log"
"net/http"
"net/url"
)
func HandleMessenger(resp http.ResponseWriter, request *http.Request) {
secretKey := "secret_token"
if request.Method == "GET" {
u, _ := url.Parse(request.RequestURI)
values, _ := url.ParseQuery(u.RawQuery)
token := values.Get("hub.verify_token")
if token == secretKey {
resp.WriteHeader(200)
resp.Write([]byte(values.Get("hub.challenge")))
return
}
resp.WriteHeader(400)
resp.Write([]byte(`Bad token`))
return
}
}
// Initialize request
func main() {
router := mux.NewRouter()
router.HandleFunc("/", HandleMessenger).Methods("POST", "GET")
port := ":8000"
log.Printf("Server started on %s", port)
log.Fatal(http.ListenAndServe(port, router))
}
When you run it (go run function.go) and open it in your browser, it should look a little something like this.

When you run it ($ go run function.go) and open it in a browser (localhost:8000), it should look something like this. The reason for the “Bad token” will be explained in the following section, where we verify our endpoint.

Bad token request, as we’re verifying for Messenger

We will modify this code further down to match Messenger’s webhook needs.

As we’re still developing on localhost, there is no point in setting up webhook forwarding from messenger yet. You remember how I said to download Ngrok in the Prerequisites section? Otherwise, download it now.

Start the webserver and point it to http://localhost:8000

./ngrok http 8000

It will start a new session, and tell you the forwarding rule.

In my case, the URL is https://e1343ffe.ngrok.io. We will take this URL and use it in the next section.

How do we get facebook to point to this webserver, you ask?

  1. Create a Facebook app: https://developers.facebook.com/apps. This requires a Facebook Developer account and a Facebook page.
Click the “Set up” button next to Messenger under the Products menu.

2. Inside the app, find “Products” on the left menu and click the + icon. Find Messenger and click “Set up”. Find “Webhooks” on the page and click “Add callback URL”.

Messenger webhook setup

3. Set the Callback URL to the Ngrok address we set earlier. Input “secret_token” in the second field, as this was hardcoded in our basic server setup. If you changed it, set it to what you changed it for.

Connect it to a facebook page

4. Connect it to your page by clicking the “Add Pages” button. If you don’t have a page, create one. When it’s set up, click the “Generate Token” button next to your page, and save the token it gives you for later. To directly work with our code example, export it like so:

export FACEBOOK_ACCESS_TOKEN="<INPUT YOUR TOKEN HERE>"
Configure messaging

5. Configure page webhook by scrolling back down to “webhooks”. Select the “messages” for now.

Enabling Natural language processing

6. Enable Natural language processing from Facebook. We will use this for a really simple matter later.

A POST request from the messenger webhook

7. Test your integration by messaging YOUR page on https://messenger.com (write literally anything). Check your webserver, and you should have a return as seen above.

Now that we got the initial integration in order, its time to have fun and build what we actually want. This requires some overhead code so bear with me.

  • Text message (basic chatbot)
  • Postback (item with response question, e.g. a thing to buy)
  • Natural language processing

First we start with imports and structs. As you can see, we need quite a bit more to make this work now. The first struct, InputMessage, is the message sent from a user on Messenger. The others are to be used to SEND a response back to the user, either as a text message (responseMessage) or a template (responseAttachment).

package mainimport (
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/gorilla/mux"
"io/ioutil"
"log"
"net/http"
"net/url"
"os"
)
// The message we get from Messenger
type InputMessage struct {
Object string `json:"object"`
Entry []struct {
ID string `json:"id"`
Time int64 `json:"time"
Payload struct {
Title string `json:"title"`
payload string `json:"payload"`
} `json:"payload"``
Messaging []struct {
Sender struct {
ID string `json:"id"`
} `json:"sender"`
Recipient struct {
ID string `json:"id"`
} `json:"recipient"`
Timestamp int64 `json:"timestamp"`
Message struct {
Mid string `json:"mid"`
Text string `json:"text"`
Nlp struct {
Entities struct {
Sentiment []struct {
Confidence float64 `json:"confidence"`
Value string `json:"value"`
} `json:"sentiment"`
Greetings []struct {
Confidence float64 `json:"confidence"`
Value string `json:"value"`
} `json:"greetings"`
} `json:"entities"`
DetectedLocales []struct {
Locale string `json:"locale"`
Confidence float64 `json:"confidence"`
} `json:"detected_locales"`
} `json:"nlp"`
} `json:"message"`
} `json:"messaging"`
} `json:"entry"`
}
// The recipient of our message
type Recipient struct {
ID string `json:"id"`
}
// The message to send it its basic
type Message struct {
Text string `json:"text,omitempty"`
}
type Button struct {
Type string `json:"type,omitempty"`
Title string `json:"title,omitempty"`
Payload string `json:"payload,omitempty"`
URL string `json:"url,omitempty"`
}
type Element struct {
Title string `json:"title,omitempty"`
Subtitle string `json:"subtitle,omitempty"`
ImageURL string `json:"image_url,omitempty"`
DefaultAction DefaultAction `json:"default_action,omitempty"`
Buttons []Button `json:"buttons,omitempty"`
}
type DefaultAction struct {
Type string `json:"type,omitempty"`
URL string `json:"url,omitempty"`
WebViewHeightRation string `json:"webview_height_ratio,omitempty"`
}
// The attachment to send (custom)
type Attachment struct {
Attachment struct {
Type string `json:"type,omitempty"`
Payload struct {
TemplateType string `json:"template_type,omitempty"`
Elements []Element `json:"elements,omitempty"`
} `json:"payload,omitempty"`
} `json:"attachment,omitempty"`
}
// Full response
type ResponseAttachment struct {
Recipient Recipient `json:"recipient"`
Message Attachment `json:"message,omitempty"`
}
// Full response
type ResponseMessage struct {
Recipient Recipient `json:"recipient"`
Message Message `json:"message,omitempty"`
}

Update to HandleMessenger

Our new HandleMessenger takes into account POST requests from Messenger, parsing the body into the InputMessage struct, before sending the request along to the handleMessage function.

func HandleMessenger(resp http.ResponseWriter, request *http.Request) {
secretKey := "secret_token"
if request.Method == "GET" {
u, _ := url.Parse(request.RequestURI)
values, _ := url.ParseQuery(u.RawQuery)
token := values.Get("hub.verify_token")
if token == secretKey {
resp.WriteHeader(200)
resp.Write([]byte(values.Get("hub.challenge")))
return
}
resp.WriteHeader(400)
resp.Write([]byte(`Bad token`))
return
}
// Anything that reaches here is POST.
body, err := ioutil.ReadAll(request.Body)
if err != nil {
log.Printf("Failed parsing body: %s", err)
resp.WriteHeader(400)
resp.Write([]byte("An error occurred"))
return
}
// Parse message into the Message struct
var message InputMessage
err = json.Unmarshal(body, &message)
if err != nil {
log.Printf("Failed unmarshalling message: %s", err)
resp.WriteHeader(400)
resp.Write([]byte("An error occurred"))
return
}
// Find messages
//log.Printf("Message: %#v", message)
for _, entry := range message.Entry {
if len(entry.Messaging) == 0 {
log.Printf("No messages")
resp.WriteHeader(400)
resp.Write([]byte("An error occurred"))
return
}
event := entry.Messaging[0]
err = handleMessage(event.Sender.ID, event.Message.Text)
if err != nil {
log.Printf("Failed sending message: %s", err)
resp.WriteHeader(400)
resp.Write([]byte("An error occurred"))
return
}
}
}

handleMessage function

With a parsed message and senderId, we now have everything required to make a proper answer. This function takes the message from the user, parses it and simply answers “Hello”. If you want to echo the request instead, change the response.Message.Text to equal the “message” string parameter. This is where you would use a NLP platform to help you out in your answer as well.

We take into account the access_token we previously generated as well. This example uses the OS environemtn variable “FACEBOOK_ACCESS_TOKEN” as previously defined.

// Handles messages
func handleMessage(senderId, message string) error {
if len(message) == 0 {
return errors.New("No message found.")
}
response := Response{
Recipient: Recipient{
ID: senderId,
},
Message: Message{
Text: "Hello",
},
}
data, err := json.Marshal(response)
if err != nil {
log.Printf("Marshal error: %s", err)
return err
}
uri := "https://graph.facebook.com/v2.6/me/messages"
uri = fmt.Sprintf("%s?access_token=%s", uri, os.Getenv("FACEBOOK_ACCESS_TOKEN"))
log.Printf("URI: %s", uri)
req, err := http.NewRequest(
"POST",
uri,
bytes.NewBuffer(data),
)
if err != nil {
log.Printf("Failed making request: %s", err)
return err
}
req.Header.Add("Content-Type", "application/json")client := http.Client{}
res, err := client.Do(req)
if err != nil {
log.Printf("Failed doing request: %s", err)
return err
}
log.Printf("MESSAGE SENT?n%#v", res)
return nil
}

Chat result

Automated result from our chatbot

With that out of the way, lets move onto something more interesting. There are multiple templates to be used (e.g. receipt or airline), but we’ll create a simple one like the image below. Click here to learn more about templates.

A giveaway template postback

There isn’t that much more to these actually. They require us to choose some features like Title, Subtitle, Buttons and a default action, but not much else.

Be sure to choose the right template however. The picture on the left is the “default” one. Here’s the function used to generate it:

// Handles messages
func handleAttachment(senderId, message string) error {
if len(message) == 0 {
return errors.New("No message found.")
}
response := ResponseAttachment{
Recipient: Recipient{
ID: senderId,
},
Message: Attachment{},
}
elements := []Element{
Element{
Title: "Check us out",
ImageURL: "https://niceable.co/images/heart.jpg",
Subtitle: "Fresh, organic and ethical giveaways",
DefaultAction: DefaultAction{
Type: "web_url",
URL: "https://niceable.co",
WebViewHeightRation: "tall",
},
Buttons: []Button{
Button{
Type: "web_url",
URL: "https://niceable.co",
Title: "Join giveaways",
},
},
},
}
response.Message.Attachment.Type = "template"
response.Message.Attachment.Payload.TemplateType = "generic"
response.Message.Attachment.Payload.Elements = elements
data, err := json.Marshal(response)
if err != nil {
log.Printf("Marshal error: %s", err)
return err
}
log.Printf("DATA: %s", string(data))return sendRequest(data)
}

Not that different from the handleMessage function is it? One difference can be seen in the fact that we have buttons. To make button conversations (move the chat forwards), you can also use whats called “postbacks”. The postback button code looks like this:

{
"type":"postback",
"title":"Start Chatting",
"payload":"DEVELOPER_DEFINED_PAYLOAD"
}

Postbacks are essential if you want to have a conversation that’s simpler than just answering the same thing over and over. Other button options can be found here



Read More

LEAVE A REPLY

Please enter your comment!
Please enter your name here