From f7a3ef787f6e7b38f10720a4ed457ee6b18d751a Mon Sep 17 00:00:00 2001 From: Ilyas Salikhov Date: Thu, 8 Sep 2016 10:55:48 +0300 Subject: [PATCH] * Added ability to set greeting setting, get started setting and persistent menu setting * Added IsEcho in Message which is indicator of own messages * Added QuickReply in Message for sending of quick reply buttons with message * Added mux parameter in Messenger constructor. It is useful when single go app processes the several bots * Added Messenger.SenderAction which allows to show writing and reading status * Added Messenger.GreetingSetting and Messenger.CallToActionsSetting which allow to set the bot settings * Fixed bug with nil returning instead of error object --- message.go | 4 +++ messenger.go | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++- receiving.go | 4 +-- response.go | 53 +++++++++++++++++++++++++++++------ settings.go | 27 ++++++++++++++++++ 5 files changed, 155 insertions(+), 11 deletions(-) create mode 100644 settings.go diff --git a/message.go b/message.go index d7a1a48..defe85e 100644 --- a/message.go +++ b/message.go @@ -10,6 +10,8 @@ type Message struct { Recipient Recipient `json:"-"` // Time is when the message was sent. Time time.Time `json:"-"` + // Message is mine + IsEcho bool `json:"is_echo,omitempty"` // Mid is the ID of the message. Mid string `json:"mid"` // Seq is order the message was sent in relation to other messages. @@ -19,6 +21,8 @@ type Message struct { // Attachments is the information about the attachments which were sent // with the message. Attachments []Attachment `json:"attachments"` + // Selected quick reply + QuickReply *QuickReply `json:"quick_reply,omitempty"` } // Delivery represents a the event fired when Facebook delivers a message to the diff --git a/messenger.go b/messenger.go index e5a7a20..8a83a33 100644 --- a/messenger.go +++ b/messenger.go @@ -1,6 +1,7 @@ package messenger import ( + "bytes" "encoding/json" "fmt" "net/http" @@ -11,6 +12,8 @@ const ( // 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/" + // SendSettingsURL is API endpoint for saving settings. + SendSettingsURL = "https://graph.facebook.com/v2.6/me/thread_settings" ) // Options are the settings used when creating a Messenger client. @@ -25,6 +28,8 @@ type Options struct { Token string // WebhookURL is where the Messenger client should listen for webhook events. Leaving the string blank implies a path of "/". WebhookURL string + // Mux is shared mux between several Messenger objects + Mux *http.ServeMux } // MessageHandler is a handler used for responding to a message containing text. @@ -52,8 +57,12 @@ type Messenger struct { // New creates a new Messenger. You pass in Options in order to affect settings. func New(mo Options) *Messenger { + if mo.Mux == nil { + mo.Mux = http.NewServeMux() + } + m := &Messenger{ - mux: http.NewServeMux(), + mux: mo.Mux, token: mo.Token, } @@ -116,6 +125,65 @@ func (m *Messenger) ProfileByID(id int64) (Profile, error) { return p, err } +// GreetingSetting sends settings for greeting +func (m *Messenger) GreetingSetting(text string) error { + d := GreetingSetting{ + SettingType: "greeting", + Greeting: GreetingInfo{ + Text: text, + }, + } + + data, err := json.Marshal(d) + if err != nil { + return err + } + + req, err := http.NewRequest("POST", SendSettingsURL, bytes.NewBuffer(data)) + if err != nil { + return err + } + + req.Header.Set("Content-Type", "application/json") + req.URL.RawQuery = "access_token=" + m.token + + client := &http.Client{} + + resp, err := client.Do(req) + defer resp.Body.Close() + + return err +} + +// CallToActionsSetting sends settings for Get Started or Persist Menu +func (m *Messenger) CallToActionsSetting(state string, actions []CallToActionsItem) error { + d := CallToActionsSetting{ + SettingType: "call_to_actions", + ThreadState: state, + CallToActions: actions, + } + + data, err := json.Marshal(d) + if err != nil { + return err + } + + req, err := http.NewRequest("POST", SendSettingsURL, bytes.NewBuffer(data)) + if err != nil { + return err + } + + req.Header.Set("Content-Type", "application/json") + req.URL.RawQuery = "access_token=" + m.token + + client := &http.Client{} + + resp, err := client.Do(req) + defer resp.Body.Close() + + return err +} + // handle is the internal HTTP handler for the webhooks. func (m *Messenger) handle(w http.ResponseWriter, r *http.Request) { if r.Method == "GET" { @@ -186,6 +254,14 @@ func (m *Messenger) dispatch(r Receive) { } } +// Response returns new Response object +func (m *Messenger) Response(to int64) *Response { + return &Response{ + to: Recipient{to}, + token: m.token, + } +} + // classify determines what type of message a webhook event is. func (m *Messenger) classify(info MessageInfo, e Entry) Action { if info.Message != nil { diff --git a/receiving.go b/receiving.go index aa9799d..b8565d7 100644 --- a/receiving.go +++ b/receiving.go @@ -60,9 +60,9 @@ type Attachment struct { // QuickReply is a file which used in a message. type QuickReply struct { // ContentType is the type of reply - ContentType string `json:"content_type"` + ContentType string `json:"content_type,omitempty"` // Title is the reply title - Title string `json:"title"` + Title string `json:"title,omitempty"` // Payload is the reply information Payload string `json:"payload"` } diff --git a/response.go b/response.go index c6e388b..3d9fdcf 100644 --- a/response.go +++ b/response.go @@ -39,7 +39,7 @@ func (r *Response) TextWithReplies(message string, replies []QuickReply) error { data, err := json.Marshal(m) if err != nil { - return nil + return err } req, err := http.NewRequest("POST", SendMessageURL, bytes.NewBuffer(data)) @@ -122,7 +122,7 @@ func (r *Response) ButtonTemplate(text string, buttons *[]StructuredMessageButto data, err := json.Marshal(m) if err != nil { - return nil + return err } req, err := http.NewRequest("POST", SendMessageURL, bytes.NewBuffer(data)) @@ -142,7 +142,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(text string, elements *[]StructuredMessageElement) error { +func (r *Response) GenericTemplate(elements *[]StructuredMessageElement) error { m := SendStructuredMessage{ Recipient: r.to, Message: StructuredMessageData{ @@ -159,7 +159,35 @@ func (r *Response) GenericTemplate(text string, elements *[]StructuredMessageEle data, err := json.Marshal(m) if err != nil { - return nil + return err + } + + 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) + defer resp.Body.Close() + + return err +} + +// SenderAction sends a info about sender action +func (r *Response) SenderAction(action string) error { + m := SendSenderAction{ + Recipient: r.to, + SenderAction: action, + } + + data, err := json.Marshal(m) + if err != nil { + return err } req, err := http.NewRequest("POST", SendMessageURL, bytes.NewBuffer(data)) @@ -212,23 +240,32 @@ type StructuredMessageAttachment struct { // StructuredMessagePayload is the actual payload of an attachment type StructuredMessagePayload struct { // TemplateType must be button, generic or receipt - TemplateType string `json:"template_type"` + TemplateType string `json:"template_type,omitempty"` Text string `json:"text,omitempty"` Elements *[]StructuredMessageElement `json:"elements,omitempty"` Buttons *[]StructuredMessageButton `json:"buttons,omitempty"` + Url string `json:"url,omitempty"` } // StructuredMessageElement is a response containing structural elements type StructuredMessageElement struct { Title string `json:"title"` ImageURL string `json:"image_url"` + ItemURL string `json:"item_url"` Subtitle string `json:"subtitle"` Buttons []StructuredMessageButton `json:"buttons"` } // StructuredMessageButton is a response containing buttons type StructuredMessageButton struct { - Type string `json:"type"` - URL string `json:"url"` - Title string `json:"title"` + Type string `json:"type"` + URL string `json:"url,omitempty"` + Title string `json:"title"` + Payload string `json:"payload,omitempty"` +} + +// SendSenderAction is the information about sender action +type SendSenderAction struct { + Recipient Recipient `json:"recipient"` + SenderAction string `json:"sender_action"` } diff --git a/settings.go b/settings.go new file mode 100644 index 0000000..a973168 --- /dev/null +++ b/settings.go @@ -0,0 +1,27 @@ +package messenger + +// GreetingSetting is the setting for greeting message +type GreetingSetting struct { + SettingType string `json:"setting_type"` + Greeting GreetingInfo `json:"greeting"` +} + +// GreetingInfo contains greeting message +type GreetingInfo struct { + Text string `json:"text"` +} + +// CallToActionsSetting is the settings for Get Started and Persist Menu +type CallToActionsSetting struct { + SettingType string `json:"setting_type"` + ThreadState string `json:"thread_state"` + CallToActions []CallToActionsItem `json:"call_to_actions"` +} + +// CallToActionsItem contains Get Started button or item of Persist Menu +type CallToActionsItem struct { + Type string `json:"type,omitempty"` + Title string `json:"title,omitempty"` + Payload string `json:"payload,omitempty"` + URL string `json:"url,omitempty"` +}