From b88edd8392cc335c54f0f5a45a42ef327ee82d41 Mon Sep 17 00:00:00 2001 From: DmitryZagorulko Date: Tue, 10 Jul 2018 15:03:41 +0300 Subject: [PATCH] add processing response from facebook --- messenger.go | 32 ++++++++++++----------- response.go | 71 ++++++++++++++++++++++++++-------------------------- 2 files changed, 54 insertions(+), 49 deletions(-) diff --git a/messenger.go b/messenger.go index c2628af..db86088 100644 --- a/messenger.go +++ b/messenger.go @@ -202,7 +202,9 @@ func (m *Messenger) ProfileByID(id int64, profileFields []string) (Profile, erro } // GreetingSetting sends settings for greeting -func (m *Messenger) GreetingSetting(text string) error { +func (m *Messenger) GreetingSetting(text string) (QueryResponse, error) { + var qr QueryResponse + d := GreetingSetting{ SettingType: "greeting", Greeting: GreetingInfo{ @@ -212,12 +214,12 @@ func (m *Messenger) GreetingSetting(text string) error { data, err := json.Marshal(d) if err != nil { - return err + return qr, err } req, err := http.NewRequest("POST", SendSettingsURL, bytes.NewBuffer(data)) if err != nil { - return err + return qr, err } req.Header.Set("Content-Type", "application/json") @@ -227,15 +229,17 @@ func (m *Messenger) GreetingSetting(text string) error { resp, err := client.Do(req) if err != nil { - return err + return qr, err } defer resp.Body.Close() - return checkFacebookError(resp.Body) + return getFacebookQueryResponse(resp.Body) } // CallToActionsSetting sends settings for Get Started or Persistent Menu -func (m *Messenger) CallToActionsSetting(state string, actions []CallToActionsItem) error { +func (m *Messenger) CallToActionsSetting(state string, actions []CallToActionsItem) (QueryResponse, error) { + var qr QueryResponse + d := CallToActionsSetting{ SettingType: "call_to_actions", ThreadState: state, @@ -244,12 +248,12 @@ func (m *Messenger) CallToActionsSetting(state string, actions []CallToActionsIt data, err := json.Marshal(d) if err != nil { - return err + return qr, err } req, err := http.NewRequest("POST", SendSettingsURL, bytes.NewBuffer(data)) if err != nil { - return err + return qr, err } req.Header.Set("Content-Type", "application/json") @@ -259,11 +263,11 @@ func (m *Messenger) CallToActionsSetting(state string, actions []CallToActionsIt resp, err := client.Do(req) if err != nil { - return err + return qr, err } defer resp.Body.Close() - return checkFacebookError(resp.Body) + return getFacebookQueryResponse(resp.Body) } // handle is the internal HTTP handler for the webhooks. @@ -426,12 +430,12 @@ func (m *Messenger) Response(to int64) *Response { } // Send will send a textual message to a user. This user must have previously initiated a conversation with the bot. -func (m *Messenger) Send(to Recipient, message string, messagingType MessagingType, tags ...string) error { +func (m *Messenger) Send(to Recipient, message string, messagingType MessagingType, tags ...string) (QueryResponse, error) { return m.SendWithReplies(to, message, nil, messagingType, tags...) } // SendGeneralMessage will send the GenericTemplate message -func (m *Messenger) SendGeneralMessage(to Recipient, elements *[]StructuredMessageElement, messagingType MessagingType, tags ...string) error { +func (m *Messenger) SendGeneralMessage(to Recipient, elements *[]StructuredMessageElement, messagingType MessagingType, tags ...string) (QueryResponse, error) { r := &Response{ token: m.token, to: to, @@ -440,7 +444,7 @@ func (m *Messenger) SendGeneralMessage(to Recipient, elements *[]StructuredMessa } // SendWithReplies sends a textual message to a user, but gives them the option of numerous quick response options. -func (m *Messenger) SendWithReplies(to Recipient, message string, replies []QuickReply, messagingType MessagingType, tags ...string) error { +func (m *Messenger) SendWithReplies(to Recipient, message string, replies []QuickReply, messagingType MessagingType, tags ...string) (QueryResponse, error) { response := &Response{ token: m.token, to: to, @@ -450,7 +454,7 @@ func (m *Messenger) SendWithReplies(to Recipient, message string, replies []Quic } // Attachment sends an image, sound, video or a regular file to a given recipient. -func (m *Messenger) Attachment(to Recipient, dataType AttachmentType, url string, messagingType MessagingType, tags ...string) error { +func (m *Messenger) Attachment(to Recipient, dataType AttachmentType, url string, messagingType MessagingType, tags ...string) (QueryResponse, error) { response := &Response{ token: m.token, to: to, diff --git a/response.go b/response.go index 97b0113..08dff38 100644 --- a/response.go +++ b/response.go @@ -62,8 +62,9 @@ const ( // QueryResponse is the response sent back by Facebook when setting up things // like greetings or call-to-actions type QueryResponse struct { - Error *QueryError `json:"error,omitempty"` - Result string `json:"result,omitempty"` + Error *QueryError `json:"error,omitempty"` + RecipientID string `json:"recipient_id"` + MessageID string `json:"message_id"` } // QueryError is representing an error sent back by Facebook @@ -80,19 +81,16 @@ func (e QueryError) Error() string { return e.Message } -func checkFacebookError(r io.Reader) error { - var err error - +func getFacebookQueryResponse(r io.Reader) (QueryResponse, error) { qr := QueryResponse{} - err = json.NewDecoder(r).Decode(&qr) + err := json.NewDecoder(r).Decode(&qr) if err != nil { - return xerrors.Errorf("json unmarshal error: %w", err) + return qr, xerrors.Errorf("json unmarshal error: %w", err) } if qr.Error != nil { - return xerrors.Errorf("facebook error: %w", qr.Error) + return qr, xerrors.Errorf("facebook error: %w", qr.Error) } - - return nil + return qr, nil } // Response is used for responding to events with messages. @@ -107,14 +105,14 @@ func (r *Response) SetToken(token string) { } // Text sends a textual message. -func (r *Response) Text(message string, messagingType MessagingType, tags ...string) error { +func (r *Response) Text(message string, messagingType MessagingType, tags ...string) (QueryResponse, error) { return r.TextWithReplies(message, nil, messagingType, tags...) } // TextWithReplies sends a textual message with some replies // messagingType should be one of the following: "RESPONSE","UPDATE","MESSAGE_TAG","NON_PROMOTIONAL_SUBSCRIPTION" // only supply tags when messagingType == "MESSAGE_TAG" (see https://developers.facebook.com/docs/messenger-platform/send-messages#messaging_types for more) -func (r *Response) TextWithReplies(message string, replies []QuickReply, messagingType MessagingType, tags ...string) error { +func (r *Response) TextWithReplies(message string, replies []QuickReply, messagingType MessagingType, tags ...string) (QueryResponse, error) { var tag string if len(tags) > 0 { tag = tags[0] @@ -134,7 +132,7 @@ func (r *Response) TextWithReplies(message string, replies []QuickReply, messagi } // AttachmentWithReplies sends a attachment message with some replies -func (r *Response) AttachmentWithReplies(attachment *StructuredMessageAttachment, replies []QuickReply, messagingType MessagingType, tags ...string) error { +func (r *Response) AttachmentWithReplies(attachment *StructuredMessageAttachment, replies []QuickReply, messagingType MessagingType, tags ...string) (QueryResponse, error) { var tag string if len(tags) > 0 { tag = tags[0] @@ -153,18 +151,20 @@ func (r *Response) AttachmentWithReplies(attachment *StructuredMessageAttachment } // Image sends an image. -func (r *Response) Image(im image.Image) error { +func (r *Response) Image(im image.Image) (QueryResponse, error) { + var qr QueryResponse + imageBytes := new(bytes.Buffer) err := jpeg.Encode(imageBytes, im, nil) if err != nil { - return err + return qr, err } return r.AttachmentData(ImageAttachment, "meme.jpg", imageBytes) } // Attachment sends an image, sound, video or a regular file to a chat. -func (r *Response) Attachment(dataType AttachmentType, url string, messagingType MessagingType, tags ...string) error { +func (r *Response) Attachment(dataType AttachmentType, url string, messagingType MessagingType, tags ...string) (QueryResponse, error) { var tag string if len(tags) > 0 { tag = tags[0] @@ -205,11 +205,12 @@ func createFormFile(filename string, w *multipart.Writer, contentType string) (i } // AttachmentData sends an image, sound, video or a regular file to a chat via an io.Reader. -func (r *Response) AttachmentData(dataType AttachmentType, filename string, filedata io.Reader) error { +func (r *Response) AttachmentData(dataType AttachmentType, filename string, filedata io.Reader) (QueryResponse, error) { + var qr QueryResponse filedataBytes, err := ioutil.ReadAll(filedata) if err != nil { - return err + return qr, err } contentType := http.DetectContentType(filedataBytes[:512]) fmt.Println("Content-type detected:", contentType) @@ -218,12 +219,12 @@ func (r *Response) AttachmentData(dataType AttachmentType, filename string, file multipartWriter := multipart.NewWriter(&body) data, err := createFormFile(filename, multipartWriter, contentType) if err != nil { - return err + return qr, err } _, err = bytes.NewBuffer(filedataBytes).WriteTo(data) if err != nil { - return err + return qr, err } multipartWriter.WriteField("recipient", fmt.Sprintf(`{"id":"%v"}`, r.to.ID)) @@ -231,7 +232,7 @@ func (r *Response) AttachmentData(dataType AttachmentType, filename string, file req, err := http.NewRequest("POST", SendMessageURL, &body) if err != nil { - return err + return qr, err } req.URL.RawQuery = "access_token=" + r.token @@ -241,15 +242,15 @@ func (r *Response) AttachmentData(dataType AttachmentType, filename string, file client := &http.Client{} resp, err := client.Do(req) if err != nil { - return err + return qr, err } defer resp.Body.Close() - return checkFacebookError(resp.Body) + return getFacebookQueryResponse(resp.Body) } // ButtonTemplate sends a message with the main contents being button elements -func (r *Response) ButtonTemplate(text string, buttons *[]StructuredMessageButton, messagingType MessagingType, tags ...string) error { +func (r *Response) ButtonTemplate(text string, buttons *[]StructuredMessageButton, messagingType MessagingType, tags ...string) (QueryResponse, error) { var tag string if len(tags) > 0 { tag = tags[0] @@ -276,7 +277,7 @@ func (r *Response) ButtonTemplate(text string, buttons *[]StructuredMessageButto } // GenericTemplate is a message which allows for structural elements to be sent -func (r *Response) GenericTemplate(elements *[]StructuredMessageElement, messagingType MessagingType, tags ...string) error { +func (r *Response) GenericTemplate(elements *[]StructuredMessageElement, messagingType MessagingType, tags ...string) (QueryResponse, error) { var tag string if len(tags) > 0 { tag = tags[0] @@ -301,7 +302,7 @@ func (r *Response) GenericTemplate(elements *[]StructuredMessageElement, messagi } // ListTemplate sends a list of elements -func (r *Response) ListTemplate(elements *[]StructuredMessageElement, messagingType MessagingType, tags ...string) error { +func (r *Response) ListTemplate(elements *[]StructuredMessageElement, messagingType MessagingType, tags ...string) (QueryResponse, error) { var tag string if len(tags) > 0 { tag = tags[0] @@ -327,7 +328,7 @@ func (r *Response) ListTemplate(elements *[]StructuredMessageElement, messagingT } // SenderAction sends a info about sender action -func (r *Response) SenderAction(action string) error { +func (r *Response) SenderAction(action string) (QueryResponse, error) { m := SendSenderAction{ Recipient: r.to, SenderAction: action, @@ -336,15 +337,16 @@ func (r *Response) SenderAction(action string) error { } // DispatchMessage posts the message to messenger, return the error if there's any -func (r *Response) DispatchMessage(m interface{}) error { +func (r *Response) DispatchMessage(m interface{}) (QueryResponse, error) { + var res QueryResponse data, err := json.Marshal(m) if err != nil { - return err + return res, err } req, err := http.NewRequest("POST", SendMessageURL, bytes.NewBuffer(data)) if err != nil { - return err + return res, err } req.Header.Set("Content-Type", "application/json") @@ -352,13 +354,12 @@ func (r *Response) DispatchMessage(m interface{}) error { resp, err := http.DefaultClient.Do(req) if err != nil { - return err + return res, err } + defer resp.Body.Close() - if resp.StatusCode == 200 { - return nil - } - return checkFacebookError(resp.Body) + + return getFacebookQueryResponse(resp.Body) } // PassThreadToInbox Uses Messenger Handover Protocol for live inbox