diff --git a/actions.go b/actions.go index 5d689d7..3b29a8a 100644 --- a/actions.go +++ b/actions.go @@ -11,4 +11,6 @@ const ( // DeliveryAction means that the event was a previous recipient reading their respective // messages. DeliveryAction + // PostBackAction represents post call back + PostBackAction ) diff --git a/message.go b/message.go index c6c77fd..1756c7a 100644 --- a/message.go +++ b/message.go @@ -32,6 +32,18 @@ type Delivery struct { Seq int `json:"seq"` } +// PostBack represents postback callback +type PostBack struct { + // Sender is who the message was sent from. + Sender Sender `json:"-"` + // Recipient is who the message was sent to. + Recipient Recipient `json:"-"` + // Time is when the message was sent. + Time time.Time `json:"-"` + // PostBack ID + Payload string `json:"payload"` +} + // Watermark is the RawWatermark timestamp rendered as a time.Time. func (d Delivery) Watermark() time.Time { return time.Unix(d.RawWatermark, 0) diff --git a/messenger.go b/messenger.go index 895952e..bf8c98c 100644 --- a/messenger.go +++ b/messenger.go @@ -8,9 +8,6 @@ import ( ) const ( - // WebhookURL is where the Messenger client should listen for webhook events. - WebhookURL = "/webhook" - // ProfileURL is the API endpoint used for retrieving profiles. // Used in the form: https://graph.facebook.com/v2.6/?fields=first_name,last_name,profile_pic&access_token= ProfileURL = "https://graph.facebook.com/v2.6/" @@ -26,6 +23,8 @@ type Options struct { VerifyToken string // Token is the access token of the Facebook page to send messages from. Token string + // WebhookURL is where the Messenger client should listen for webhook events. + WebhookURL string } // MessageHandler is a handler used for responding to a message containing text. @@ -34,11 +33,15 @@ type MessageHandler func(Message, *Response) // DeliveryHandler is a handler used for responding to a read receipt. type DeliveryHandler func(Delivery, *Response) +// PostBackHandler is a handler used postback callbacks. +type PostBackHandler func(PostBack, *Response) + // Messenger is the client which manages communication with the Messenger Platform API. type Messenger struct { mux *http.ServeMux messageHandlers []MessageHandler deliveryHandlers []DeliveryHandler + postBackHandlers []PostBackHandler token string verifyHandler func(http.ResponseWriter, *http.Request) } @@ -51,7 +54,7 @@ func New(mo Options) *Messenger { } m.verifyHandler = newVerifyHandler(mo.VerifyToken) - m.mux.HandleFunc(WebhookURL, m.handle) + m.mux.HandleFunc(mo.WebhookURL, m.handle) return m } @@ -68,6 +71,11 @@ func (m *Messenger) HandleDelivery(f DeliveryHandler) { m.deliveryHandlers = append(m.deliveryHandlers, f) } +// HandlePostBack adds a new PostBackHandler to the Messenger +func (m *Messenger) HandlePostBack(f PostBackHandler) { + m.postBackHandlers = append(m.postBackHandlers, f) +} + // Handler returns the Messenger in HTTP client form. func (m *Messenger) Handler() http.Handler { return m.mux @@ -106,7 +114,6 @@ func (m *Messenger) handle(w http.ResponseWriter, r *http.Request) { err := json.NewDecoder(r.Body).Decode(&rec) if err != nil { fmt.Println(err) - fmt.Fprintln(w, `{status: 'not ok'}`) return } @@ -136,19 +143,26 @@ func (m *Messenger) dispatch(r Receive) { } switch a { - case TextAction: - for _, f := range m.messageHandlers { - message := *info.Message - message.Sender = info.Sender - message.Recipient = info.Recipient - message.Time = time.Unix(info.Timestamp, 0) - - f(message, resp) - } - case DeliveryAction: - for _, f := range m.deliveryHandlers { - f(*info.Delivery, resp) - } + case TextAction: + for _, f := range m.messageHandlers { + message := *info.Message + message.Sender = info.Sender + message.Recipient = info.Recipient + message.Time = time.Unix(info.Timestamp, 0) + f(message, resp) + } + case DeliveryAction: + for _, f := range m.deliveryHandlers { + f(*info.Delivery, resp) + } + case PostBackAction: + for _, f := range m.postBackHandlers { + message := *info.PostBack + message.Sender = info.Sender + message.Recipient = info.Recipient + message.Time = time.Unix(info.Timestamp, 0) + f(message, resp) + } } } } @@ -160,8 +174,9 @@ func (m *Messenger) classify(info MessageInfo, e Entry) Action { return TextAction } else if info.Delivery != nil { return DeliveryAction + } else if info.PostBack != nil { + return PostBackAction } - return UnknownAction } @@ -172,7 +187,6 @@ func newVerifyHandler(token string) func(w http.ResponseWriter, r *http.Request) fmt.Fprintln(w, r.FormValue("hub.challenge")) return } - fmt.Fprintln(w, "Incorrect verify token.") } } diff --git a/receiving.go b/receiving.go index 9d16119..52040af 100644 --- a/receiving.go +++ b/receiving.go @@ -33,6 +33,8 @@ type MessageInfo struct { // Delivery is the contents of a message if it is a DeliveryAction. // Nil if it is not a DeliveryAction. Delivery *Delivery `json:"delivery"` + + PostBack *PostBack `json:"postback"` } // Sender is who the message was sent from. diff --git a/response.go b/response.go index 8c6340a..482f432 100644 --- a/response.go +++ b/response.go @@ -97,6 +97,85 @@ func (r *Response) Image(im image.Image) error { return nil } +func (r *Response) ButtonTemplate(text string, buttons *[]StructuredMessageButton) error { + m := SendStructuredMessage { + Recipient: r.to, + Message: StructuredMessageData { + Attachment: StructuredMessageAttachment { + Type: "template", + Payload: StructuredMessagePayload { + TemplateType: "button", + Text: text, + Buttons: buttons, + Elements: nil, + }, + }, + }, + } + + fmt.Println(m) + + data, err := json.Marshal(m) + if err != nil { + return nil + } + + req, err := http.NewRequest("POST", SendMessageURL, bytes.NewBuffer(data)) + if err != nil { + return err + } + + req.Header.Set("Content-Type", "application/json") + req.URL.RawQuery = "access_token=" + r.token + + client := &http.Client{} + + resp, err := client.Do(req) + fmt.Println(resp) + defer resp.Body.Close() + + return err +} + +func (r *Response) GenericTemplate(text string, elements *[]StructuredMessageElement) error { + m := SendStructuredMessage { + Recipient: r.to, + Message: StructuredMessageData { + Attachment: StructuredMessageAttachment { + Type: "template", + Payload: StructuredMessagePayload { + TemplateType: "generic", + Buttons: nil, + Elements: elements, + }, + }, + }, + } + + fmt.Println(m) + + data, err := json.Marshal(m) + if err != nil { + return nil + } + + req, err := http.NewRequest("POST", SendMessageURL, bytes.NewBuffer(data)) + if err != nil { + return err + } + + req.Header.Set("Content-Type", "application/json") + req.URL.RawQuery = "access_token=" + r.token + + client := &http.Client{} + + resp, err := client.Do(req) + fmt.Println(resp) + defer resp.Body.Close() + + return err +} + // SendMessage is the information sent in an API request to Facebook. type SendMessage struct { Recipient Recipient `json:"recipient"` @@ -107,3 +186,46 @@ type SendMessage struct { type MessageData struct { Text string `json:"text,omitempty"` } + +// SendStructuredMessage is a structured message template +type SendStructuredMessage struct { + Recipient Recipient `json:"recipient"` + Message StructuredMessageData `json:"message"` +} + +type StructuredMessageData struct { + Attachment StructuredMessageAttachment `json:"attachment"` +} + +type StructuredMessageAttachment struct { + // Template allways + Type string `json:"type"` + // Payload is the information for the file which was sent in the attachment. + Payload StructuredMessagePayload `json:"payload"` +} + +type StructuredMessagePayload struct { + // button, generic, receipt + TemplateType string `json:"template_type"` + Text string `json:"text,omitempty"` + Elements *[]StructuredMessageElement `json:"elements,omitempty"` + Buttons *[]StructuredMessageButton `json:"buttons,omitempty"` +} + +// StructuredMessageElement - Generic Template +type StructuredMessageElement struct { + Title string `json:"title"` + ImageURL string `json:"image_url"` + Subtitle string `json:"subtitle"` + Buttons []StructuredMessageButton `json:"buttons"` +} + +// StructuredMessageButton - Button Template +type StructuredMessageButton struct { + Type string `json:"type"` + URL string `json:"url"` + Title string `json:"title"` +} + + +