├── README.md ├── errors.go ├── helpers.go ├── messengerbot.go ├── profile.go ├── send.go ├── webhook.go └── welcomeMessage.go /README.md: -------------------------------------------------------------------------------- 1 | Facebook Messenger Platform (Chatbot) Go API 2 | ===== 3 | 4 | A Golang implementation of the [Facebook Messenger Platform](https://developers.facebook.com/docs/messenger-platform). 5 | 6 | ## Installation 7 | ```bash 8 | go get github.com/ippy04/messengerbot 9 | ``` 10 | 11 | Or if using the excellent [glide] (https://github.com/Masterminds/glide) package manager: 12 | 13 | ```bash 14 | glide get github.com/ippy04/messengerbot 15 | ``` 16 | 17 | ## Usage 18 | 19 | ### Send a Regular Message 20 | ```go 21 | 22 | bot := messengerbot.NewMessengerBot(accessToken, verifyToken) 23 | bot.Debug = true 24 | 25 | user := messengerbot.NewUserFromId(userId) 26 | msg := messengerbot.NewMessage("Hello World") 27 | 28 | bot.Send(user, msg, messengerbot.NotificationTypeRegular) 29 | ``` 30 | ### Send an Image Message 31 | ```go 32 | msg := messengerbot.NewImageMessage("https://pixabay.com/static/uploads/photo/2016/04/01/09/29/cartoon-1299393_960_720.png") 33 | bot.Send(user, msg, messengerbot.NotificationTypeRegular) 34 | ``` 35 | 36 | 37 | ### Send an [Button Template](https://developers.facebook.com/docs/messenger-platform/send-api-reference#button_template) Message 38 | ```go 39 | msg := messengerbot.NewButtonTemplate("Pick one, any one") 40 | button1 := messengerbot.NewPostbackButton("Button 1", "POSTBACK_BUTTON_1") 41 | button2 := messengerbot.NewPostbackButton("Button 2", "POSTBACK_BUTTON_2") 42 | button3 := messengerbot.NewPostbackButton("Button 3", "POSTBACK_BUTTON_3") 43 | msg.AddButton(button1, button2, button3) 44 | 45 | bot.Send(user, msg, messengerbot.NotificationTypeRegular) 46 | ``` 47 | 48 | 49 | ### Send a [Generic Template](https://developers.facebook.com/docs/messenger-platform/send-api-reference#generic_template) Message 50 | ```go 51 | msg := messengerbot.NewGenericTemplate() 52 | element := messengerbot.Element{ 53 | Title: "This is a bolded title", 54 | ImageUrl: "https://pixabay.com/static/uploads/photo/2016/04/01/09/29/cartoon-1299393_960_720.png", 55 | Subtitle: "I am a dinosaur. Hear me Rawr.", 56 | } 57 | 58 | button1 := messengerbot.NewPostbackButton("Button 1", "POSTBACK_BUTTON_1") 59 | button2 := messengerbot.NewPostbackButton("Button 2", "POSTBACK_BUTTON_2") 60 | button3 := messengerbot.NewPostbackButton("Button 3", "POSTBACK_BUTTON_3") 61 | element.AddButton(button1, button2, button3) 62 | 63 | msg.AddElement(element) 64 | 65 | bot.Send(user, msg, messengerbot.NotificationTypeRegular) 66 | ``` 67 | 68 | 69 | ### Push Notification Types 70 | ```go 71 | bot.Send(user, msg, messengerbot.NotificationTypeRegular) // regular sound, vibrate and phone alert 72 | bot.Send(user, msg, messengerbot.NotificationTypeSilentPush) // phone notification only, no sound or vibrate alert 73 | bot.Send(user, msg, messengerbot.NotificationTypeNoPush) // no sound or phone notification 74 | ``` 75 | -------------------------------------------------------------------------------- /errors.go: -------------------------------------------------------------------------------- 1 | package messengerbot 2 | 3 | type rawError struct { 4 | Error Error `json:"error"` 5 | } 6 | 7 | type Error struct { 8 | Message string `json:"message"` 9 | Type string `json:"type"` 10 | Code int `json:"code"` 11 | ErrorData string `json:"error_data"` 12 | FbTraceId string `json:"fbtrace_id"` 13 | } 14 | -------------------------------------------------------------------------------- /helpers.go: -------------------------------------------------------------------------------- 1 | package messengerbot 2 | 3 | import ( 4 | "github.com/satori/go.uuid" 5 | ) 6 | 7 | func NewUserFromId(id string) User { 8 | return User{ 9 | Id: id, 10 | } 11 | } 12 | 13 | func NewUserFromPhone(phoneNumber string) User { 14 | return User{ 15 | PhoneNumber: phoneNumber, 16 | } 17 | } 18 | 19 | func NewMessage(text string) Message { 20 | return Message{ 21 | Text: text, 22 | } 23 | } 24 | 25 | func NewImageMessage(url string) Message { 26 | return Message{ 27 | Attachment: &Attachment{ 28 | Type: "image", 29 | Payload: ImagePayload{ 30 | Url: url, 31 | }, 32 | }, 33 | } 34 | } 35 | 36 | func NewGenericTemplate() GenericTemplate { 37 | return GenericTemplate{ 38 | TemplateBase: TemplateBase{ 39 | Type: "generic", 40 | }, 41 | Elements: []Element{}, 42 | } 43 | } 44 | 45 | func NewElement(title string) Element { 46 | return Element{ 47 | Title: title, 48 | } 49 | } 50 | 51 | func NewWebUrlButton(title, url string) Button { 52 | return Button{ 53 | Type: "web_url", 54 | Title: title, 55 | Url: url, 56 | } 57 | } 58 | 59 | func NewPostbackButton(title, postback string) Button { 60 | return Button{ 61 | Type: "postback", 62 | Title: title, 63 | Payload: postback, 64 | } 65 | } 66 | 67 | func NewButtonTemplate(text string) ButtonTemplate { 68 | return ButtonTemplate{ 69 | TemplateBase: TemplateBase{ 70 | Type: "button", 71 | }, 72 | Text: text, 73 | Buttons: []Button{}, 74 | } 75 | } 76 | 77 | func NewReceiptTemplate(recipientName string) ReceiptTemplate { 78 | return ReceiptTemplate{ 79 | TemplateBase: TemplateBase{ 80 | Type: "receipt", 81 | }, 82 | RecipientName: recipientName, 83 | Id: uuid.NewV4().String(), 84 | Currency: "USD", 85 | PaymentMethod: "", 86 | Items: []OrderItem{}, 87 | Summary: OrderSummary{}, 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /messengerbot.go: -------------------------------------------------------------------------------- 1 | package messengerbot 2 | 3 | import ( 4 | "net/http" 5 | ) 6 | 7 | // Messenger is the main service which handles all callbacks from facebook 8 | // Events are delivered to handlers if they are specified 9 | type MessengerBot struct { 10 | MessageReceived MessageReceivedHandler 11 | MessageDelivered MessageDeliveredHandler 12 | Postback PostbackHandler 13 | Authentication AuthenticationHandler 14 | 15 | VerifyToken string 16 | AppSecret string // Optional: For validating integrity of messages 17 | AccessToken string 18 | PageId string // Optional: For setting welcome message 19 | Debug bool 20 | Client *http.Client 21 | } 22 | 23 | func NewMessengerBot(token string, verifyToken string) *MessengerBot { 24 | return &MessengerBot{ 25 | AccessToken: token, 26 | VerifyToken: verifyToken, 27 | Debug: false, 28 | Client: &http.Client{}, 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /profile.go: -------------------------------------------------------------------------------- 1 | package messengerbot 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "io/ioutil" 8 | "net/http" 9 | ) 10 | 11 | type Profile struct { 12 | FirstName string `json:"first_name"` 13 | LastName string `json:"last_name"` 14 | ProfilePicture string `json:"profile_pic,omitempty"` 15 | Locale string `json:"locale,omitempty"` 16 | Timezone int `json:"timezone,omitempty"` 17 | Gender string `json:"gender,omitempty"` 18 | } 19 | 20 | func (bot *MessengerBot) GetProfile(userID string) (*Profile, error) { 21 | resp, err := bot.Client.Get(fmt.Sprintf(ProfileAPIEndpoint, userID, bot.AccessToken)) 22 | 23 | if resp != nil { 24 | defer resp.Body.Close() 25 | } 26 | if err != nil { 27 | return nil, err 28 | } 29 | 30 | fmt.Println(resp.Body) 31 | 32 | read, err := ioutil.ReadAll(resp.Body) 33 | 34 | if resp.StatusCode != http.StatusOK { 35 | er := new(rawError) 36 | json.Unmarshal(read, er) 37 | return nil, errors.New("Error occured fetching profile: " + er.Error.Message) 38 | } 39 | profile := new(Profile) 40 | return profile, json.Unmarshal(read, profile) 41 | } 42 | -------------------------------------------------------------------------------- /send.go: -------------------------------------------------------------------------------- 1 | package messengerbot 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "io/ioutil" 9 | "log" 10 | "net/http" 11 | ) 12 | 13 | type NotificationType string 14 | 15 | const ( 16 | NotificationTypeRegular NotificationType = "REGULAR" // regular sound, vibrate and phone alert 17 | NotificationTypeSilentPush NotificationType = "SILENT_PUSH" // phone notification only, no sound or vibrate alert 18 | NotificationTypeNoPush NotificationType = "NO_PUSH" // no sound or phone notification 19 | ) 20 | 21 | const ( 22 | GraphAPI = "https://graph.facebook.com/v2.6/" 23 | MessengerAPIEndpoint = GraphAPI + "me/messages?access_token=%s" 24 | ProfileAPIEndpoint = GraphAPI + "%s?fields=first_name,last_name,profile_pic,locale,timezone,gender&access_token=%s" 25 | 26 | GenericTemplateTitleLengthLimit = 45 27 | GenericTemplateSubtitleLengthLimit = 80 28 | GenericTemplateCallToActionTitleLimit = 20 29 | GenericTemplateCallToActionItemsLimit = 3 30 | GenericTemplateBubblesPerMessageLimit = 10 31 | 32 | ButtonTemplateButtonsLimit = 3 33 | ) 34 | 35 | var ( 36 | ErrTitleLengthExceeded = errors.New("Template element title exceeds the 45 character limit") 37 | ErrSubtitleLengthExceeded = errors.New("Template element subtitle exceeds the 80 character limit") 38 | ErrCallToActionTitleLengthExceeded = errors.New("Template call to action title exceeds the 20 character limit") 39 | ErrButtonsLimitExceeded = errors.New("Limit of 3 buttons exceeded") 40 | ErrBubblesLimitExceeded = errors.New("Limit of 10 bubbles per message exceeded") 41 | ) 42 | 43 | type User struct { 44 | Id string `json:"id,omitempty"` 45 | PhoneNumber string `json:"phone_number,omitempty"` 46 | } 47 | 48 | type Message struct { 49 | Text string `json:"text,omitempty"` 50 | Attachment *Attachment `json:"attachment,omitempty"` 51 | } 52 | 53 | type Attachment struct { 54 | Type string `json:"type"` 55 | Payload AttachmentPayload `json:"payload"` 56 | } 57 | 58 | type AttachmentPayload interface{} 59 | 60 | type ImagePayload struct { 61 | Url string `json:"url"` 62 | } 63 | 64 | type TemplateBase struct { 65 | Type string `json:"template_type"` 66 | } 67 | 68 | type GenericTemplate struct { 69 | TemplateBase 70 | Elements []Element `json:"elements"` 71 | } 72 | 73 | func (g GenericTemplate) Validate() error { 74 | if len(g.Elements) > GenericTemplateBubblesPerMessageLimit { 75 | return ErrBubblesLimitExceeded 76 | } 77 | for _, elem := range g.Elements { 78 | if len(elem.Title) > GenericTemplateTitleLengthLimit { 79 | return ErrTitleLengthExceeded 80 | } 81 | 82 | if len(elem.Subtitle) > GenericTemplateSubtitleLengthLimit { 83 | return ErrSubtitleLengthExceeded 84 | } 85 | 86 | if len(elem.Buttons) > GenericTemplateCallToActionItemsLimit { 87 | return ErrButtonsLimitExceeded 88 | } 89 | 90 | for _, button := range elem.Buttons { 91 | if len(button.Title) > GenericTemplateCallToActionTitleLimit { 92 | return ErrCallToActionTitleLengthExceeded 93 | } 94 | } 95 | } 96 | return nil 97 | } 98 | 99 | func (g *GenericTemplate) AddElement(e ...Element) { 100 | g.Elements = append(g.Elements, e...) 101 | } 102 | 103 | type Element struct { 104 | Title string `json:"title"` 105 | Url string `json:"item_url,omitempty"` 106 | ImageUrl string `json:"image_url,omitempty"` 107 | Subtitle string `json:"subtitle,omitempty"` 108 | Buttons []Button `json:"buttons,omitempty"` 109 | } 110 | 111 | func (e *Element) AddButton(b ...Button) { 112 | e.Buttons = append(e.Buttons, b...) 113 | } 114 | 115 | type Button struct { 116 | Type string `json:"type"` 117 | Title string `json:"title,omitempty"` 118 | Url string `json:"url,omitempty"` 119 | Payload string `json:"payload,omitempty"` 120 | } 121 | 122 | type ButtonTemplate struct { 123 | TemplateBase 124 | Text string `json:"text,omitempty"` 125 | Buttons []Button `json:"buttons,omitempty"` 126 | } 127 | 128 | func (b ButtonTemplate) Validate() error { 129 | if len(b.Buttons) > ButtonTemplateButtonsLimit { 130 | return ErrButtonsLimitExceeded 131 | } 132 | return nil 133 | } 134 | 135 | func (b *ButtonTemplate) AddButton(bt ...Button) { 136 | b.Buttons = append(b.Buttons, bt...) 137 | } 138 | 139 | type ReceiptTemplate struct { 140 | TemplateBase 141 | RecipientName string `json:"recipient_name"` 142 | Id string `json:"order_number"` 143 | Currency string `json:"currency"` 144 | PaymentMethod string `json:"payment_method"` 145 | Timestamp int64 `json:"timestamp,omitempty"` 146 | Url string `json:"order_url,omitempty"` 147 | Items []OrderItem `json:"elements"` 148 | Address *OrderAddress `json:"address,omitempty"` 149 | Summary OrderSummary `json:"summary"` 150 | Adjustments []OrderAdjustment `json:"adjustments,omitempty"` 151 | } 152 | 153 | type OrderItem struct { 154 | Title string `json:"title"` 155 | Subtitle string `json:"subtitle,omitempty"` 156 | Quantity int64 `json:"quantity,omitempty"` 157 | Price int64 `json:"price,omitempty"` 158 | Currency string `json:"currency,omitempty"` 159 | ImageURL string `json:"image_url,omiempty"` 160 | } 161 | 162 | type OrderAddress struct { 163 | Street1 string `json:"street_1"` 164 | Street2 string `json:"street_2,omitempty"` 165 | City string `json:"city"` 166 | PostalCode string `json:"postal_code"` 167 | State string `json:"state"` 168 | Country string `json:"country"` 169 | } 170 | 171 | type OrderSummary struct { 172 | TotalCost int `json:"total_cost,omitempty"` 173 | Subtotal int `json:"subtotal,omitempty"` 174 | ShippingCost int `json:"shipping_cost,omitempty"` 175 | TotalTax int `json:"total_tax,omitempty"` 176 | } 177 | 178 | type OrderAdjustment struct { 179 | Name string `json:"name"` 180 | Amount int64 `json:"amount"` 181 | } 182 | 183 | type SendRequest struct { 184 | Recipient User `json:"recipient"` 185 | Message Message `json:"message"` 186 | NotificationType string `json:"notification_type"` 187 | } 188 | 189 | type SendResponse struct { 190 | RecipientId string `json:"recipient_id"` 191 | MessageId string `json:"message_id"` 192 | Error ErrorResponse `json:"error"` 193 | } 194 | type ErrorResponse struct { 195 | Message string `json:"message"` 196 | Type string `json:"type"` 197 | Code int64 `json:"code"` 198 | ErrorData string `json:"error_data"` 199 | FBstraceId string `json:"fbstrace_id"` 200 | } 201 | 202 | func (bot *MessengerBot) MakeRequest(byt *bytes.Buffer) (*SendResponse, error) { 203 | url := fmt.Sprintf(MessengerAPIEndpoint, bot.AccessToken) 204 | 205 | request, _ := http.NewRequest("POST", url, byt) 206 | request.Header.Set("Content-Type", "application/json") 207 | response, err := bot.Client.Do(request) 208 | if response != nil { 209 | defer response.Body.Close() 210 | } 211 | if err != nil { 212 | return nil, err 213 | } 214 | 215 | body, err := ioutil.ReadAll(response.Body) 216 | 217 | if response.StatusCode != http.StatusOK { 218 | er := new(rawError) 219 | json.Unmarshal(body, &er) 220 | 221 | if bot.Debug { 222 | log.Printf("[DEBUG] Response: %s", string(body)) 223 | } 224 | 225 | return nil, errors.New("Error response received: " + er.Error.Message) 226 | } 227 | 228 | var sendResponse *SendResponse 229 | json.Unmarshal(body, &sendResponse) 230 | if err != nil { 231 | return nil, err 232 | } 233 | 234 | return sendResponse, nil 235 | } 236 | 237 | // convenience method for sending a text-based message 238 | func (bot *MessengerBot) SendTextMessage(user User, messageText string, notificationType NotificationType) (*SendResponse, error) { 239 | message := NewMessage(messageText) 240 | return bot.Send(user, message, notificationType) 241 | } 242 | 243 | func (bot *MessengerBot) Send(user User, content interface{}, notificationType NotificationType) (*SendResponse, error) { 244 | var r SendRequest 245 | 246 | switch content.(type) { 247 | case Message: 248 | r = SendRequest{ 249 | Recipient: user, 250 | Message: content.(Message), 251 | NotificationType: string(notificationType), 252 | } 253 | 254 | case GenericTemplate: 255 | r = SendRequest{ 256 | Recipient: user, 257 | NotificationType: string(notificationType), 258 | Message: Message{ 259 | Attachment: &Attachment{ 260 | Type: "template", 261 | Payload: content.(GenericTemplate), 262 | }, 263 | }, 264 | } 265 | 266 | case ButtonTemplate: 267 | r = SendRequest{ 268 | Recipient: user, 269 | NotificationType: string(notificationType), 270 | Message: Message{ 271 | Attachment: &Attachment{ 272 | Type: "template", 273 | Payload: content.(ButtonTemplate), 274 | }, 275 | }, 276 | } 277 | 278 | case ReceiptTemplate: 279 | r = SendRequest{ 280 | Recipient: user, 281 | NotificationType: string(notificationType), 282 | Message: Message{ 283 | Attachment: &Attachment{ 284 | Type: "template", 285 | Payload: content.(ReceiptTemplate), 286 | }, 287 | }, 288 | } 289 | 290 | default: 291 | return nil, errors.New("Unsupported message content type") 292 | } 293 | 294 | if r == (SendRequest{}) { 295 | return nil, errors.New("Unknown Error - Unable to create SendRequest") 296 | } 297 | 298 | payload, _ := json.Marshal(r) 299 | if bot.Debug { 300 | log.Printf("[DEBUG] Payload: %s", string(payload)) 301 | } 302 | return bot.MakeRequest(bytes.NewBuffer(payload)) 303 | } 304 | -------------------------------------------------------------------------------- /webhook.go: -------------------------------------------------------------------------------- 1 | package messengerbot 2 | 3 | import ( 4 | "crypto/hmac" 5 | "crypto/sha1" 6 | "encoding/json" 7 | "fmt" 8 | log "github.com/Sirupsen/logrus" 9 | "io/ioutil" 10 | "net/http" 11 | ) 12 | 13 | type rawEvent struct { 14 | Object string `json:"object"` 15 | Entries []*MessageEvent `json:"entry"` 16 | } 17 | 18 | type Event struct { 19 | Id json.Number `json:"id"` 20 | Time json.Number `json:"time"` 21 | } 22 | 23 | type MessageEvent struct { 24 | Event 25 | Messaging []struct { 26 | MessageOpts 27 | Message *ReceivedMessage `json:"message,omitempty"` 28 | Delivery *Delivery `json:"delivery,omitempty"` 29 | Postback *Postback `json:"postback,omitempty"` 30 | Optin *Optin `json:"optin,empty"` 31 | } `json:"messaging"` 32 | } 33 | 34 | type ReceivedMessage struct { 35 | Message 36 | Id string `json:"mid"` 37 | Seq int `json:"seq"` 38 | Attachments []Attachment `json:"attachments"` 39 | } 40 | 41 | type Delivery struct { 42 | MessageIds []string `json:"mids"` 43 | Watermark int64 `json:"watermark"` 44 | Seq int `json:"seq"` 45 | } 46 | 47 | type Postback struct { 48 | Payload string `json:"payload"` 49 | } 50 | 51 | type Optin struct { 52 | Ref string `json:"ref"` 53 | } 54 | 55 | type MessageOpts struct { 56 | Sender struct { 57 | ID string `json:"id"` 58 | } `json:"sender"` 59 | 60 | Recipient struct { 61 | ID string `json:"id"` 62 | } `json:"recipient"` 63 | 64 | Timestamp int64 `json:"timestamp"` 65 | } 66 | 67 | type MessageReceivedHandler func(*MessengerBot, Event, MessageOpts, ReceivedMessage) 68 | type MessageDeliveredHandler func(*MessengerBot, Event, MessageOpts, Delivery) 69 | type PostbackHandler func(*MessengerBot, Event, MessageOpts, Postback) 70 | type AuthenticationHandler func(*MessengerBot, Event, MessageOpts, *Optin) 71 | 72 | // Handler is the main HTTP handler for the Messenger service. 73 | // It must be attached to some web server in order to receive messages 74 | func (bot *MessengerBot) Handler(rw http.ResponseWriter, req *http.Request) { 75 | if req.Method == "GET" { 76 | query := req.URL.Query() 77 | if query.Get("hub.verify_token") != bot.VerifyToken { 78 | rw.WriteHeader(http.StatusUnauthorized) 79 | return 80 | } 81 | rw.WriteHeader(http.StatusOK) 82 | rw.Write([]byte(query.Get("hub.challenge"))) 83 | } else if req.Method == "POST" { 84 | bot.handlePOST(rw, req) 85 | } else { 86 | rw.WriteHeader(http.StatusMethodNotAllowed) 87 | } 88 | } 89 | 90 | func (bot *MessengerBot) handlePOST(rw http.ResponseWriter, req *http.Request) { 91 | read, err := ioutil.ReadAll(req.Body) 92 | 93 | if err != nil { 94 | log.Error(err) 95 | rw.WriteHeader(http.StatusBadRequest) 96 | return 97 | } 98 | //Message integrity check 99 | if bot.AppSecret != "" { 100 | if !checkIntegrity(bot.AppSecret, read, req.Header.Get("x-hub-signature")[5:]) { 101 | log.Error("Failed x-hub-signature integrity check") 102 | rw.WriteHeader(http.StatusBadRequest) 103 | return 104 | } 105 | } 106 | 107 | event := &rawEvent{} 108 | err = json.Unmarshal(read, event) 109 | if err != nil { 110 | log.Error("Couldn't parse fb json:" + err.Error()) 111 | rw.WriteHeader(http.StatusBadRequest) 112 | return 113 | } 114 | 115 | for _, entry := range event.Entries { 116 | for _, message := range entry.Messaging { 117 | if message.Delivery != nil { 118 | if bot.MessageDelivered != nil { 119 | go bot.MessageDelivered(bot, entry.Event, message.MessageOpts, *message.Delivery) 120 | } 121 | } else if message.Message != nil { 122 | if bot.MessageReceived != nil { 123 | go bot.MessageReceived(bot, entry.Event, message.MessageOpts, *message.Message) 124 | } 125 | } else if message.Postback != nil { 126 | if bot.Postback != nil { 127 | go bot.Postback(bot, entry.Event, message.MessageOpts, *message.Postback) 128 | } 129 | } else if bot.Authentication != nil { 130 | go bot.Authentication(bot, entry.Event, message.MessageOpts, message.Optin) 131 | } 132 | } 133 | } 134 | rw.WriteHeader(http.StatusOK) 135 | rw.Write([]byte(`{"status":"ok"}`)) 136 | } 137 | 138 | func checkIntegrity(appSecret string, bytes []byte, expectedSignature string) bool { 139 | mac := hmac.New(sha1.New, []byte(appSecret)) 140 | if fmt.Sprintf("%x", mac.Sum(bytes)) != expectedSignature { 141 | return false 142 | } 143 | return true 144 | } 145 | 146 | func (r *ReceivedMessage) IsHaveAttachment() bool { 147 | if len(r.Attachments) == 0 {return false} 148 | return true 149 | } 150 | -------------------------------------------------------------------------------- /welcomeMessage.go: -------------------------------------------------------------------------------- 1 | package messengerbot 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "net/http" 9 | "io/ioutil" 10 | ) 11 | 12 | type ctaBase struct { 13 | SettingType string `json:"setting_type"` 14 | ThreadState string `json:"thread_state"` 15 | } 16 | 17 | var welcomeMessage = ctaBase{ 18 | SettingType: "setting_type", 19 | ThreadState: "new_thread", 20 | } 21 | 22 | type Settings struct { 23 | SettingType string `json:"setting_type"` 24 | Greeting Greeting `json:"greeting"` 25 | } 26 | 27 | type Greeting struct { 28 | Text string `json:"text"` 29 | } 30 | 31 | type cta struct { 32 | ctaBase 33 | CallToActions []*Message `json:"call_to_actions"` 34 | } 35 | 36 | type result struct { 37 | Result string `json:"result"` 38 | } 39 | 40 | // SetWelcomeMessage sets the message that is sent first. If message is nil or empty the welcome message is not sent. 41 | func (bot *MessengerBot) SetWelcomeMessage(message *Message) error { 42 | cta := &cta{ 43 | ctaBase: welcomeMessage, 44 | CallToActions: []*Message{message}, 45 | } 46 | if bot.PageId == "" { 47 | return errors.New("PageId is empty") 48 | } 49 | byt, err := json.Marshal(cta) 50 | if err != nil { 51 | return err 52 | } 53 | 54 | request, _ := http.NewRequest("POST", fmt.Sprintf(GraphAPI+"%s/thread_settings", bot.PageId), bytes.NewReader(byt)) 55 | request.Header.Set("Content-Type", "application/json") 56 | 57 | response, err := bot.Client.Do(request) 58 | if response != nil { 59 | defer response.Body.Close() 60 | } 61 | if err != nil { 62 | return err 63 | } 64 | 65 | if response.StatusCode != http.StatusOK { 66 | return errors.New("Invalid status code") 67 | } 68 | decoder := json.NewDecoder(response.Body) 69 | result := &result{} 70 | err = decoder.Decode(result) 71 | if err != nil { 72 | return err 73 | } 74 | if result.Result != "Successfully added new_thread's CTAs" { 75 | return errors.New("Something went wrong with setting thread's welcome message, facebook error: " + result.Result) 76 | } 77 | return nil 78 | } 79 | 80 | // SetWelcomeMessage sets the message that is sent first. If message is nil or empty the welcome message is not sent. 81 | func (bot *MessengerBot) SetGreeting() error { 82 | setting := Settings{ 83 | SettingType: "greeting", 84 | Greeting: Greeting{ 85 | Text:"Welcome to the bots", 86 | }, 87 | } 88 | 89 | byt, err := json.Marshal(setting) 90 | if err != nil { 91 | return err 92 | } 93 | fmt.Print(string(byt)) 94 | 95 | url := GraphAPI+"me/thread_settings?access_token="+bot.AccessToken 96 | request, err := http.NewRequest("POST", url, bytes.NewReader(byt)) 97 | fmt.Print(url) 98 | request.Header.Set("Content-Type", "application/json") 99 | 100 | if err != nil { 101 | fmt.Println(err) 102 | } 103 | 104 | response, err := bot.Client.Do(request) 105 | if response != nil { 106 | defer response.Body.Close() 107 | } 108 | if err != nil { 109 | fmt.Println(err) 110 | } 111 | 112 | if response.StatusCode != http.StatusOK { 113 | return errors.New("Invalid status code") 114 | } 115 | 116 | body, err := ioutil.ReadAll(response.Body) 117 | if err != nil { 118 | fmt.Println(err) 119 | } 120 | fmt.Println("response Body:", string(body)) 121 | 122 | return nil 123 | } 124 | 125 | // SetWelcomeMessage sets the message that is sent first. If message is nil or empty the welcome message is not sent. 126 | func (bot *MessengerBot) SetGetstarted() error { 127 | str := ` 128 | { 129 | "setting_type":"call_to_actions", 130 | "thread_state":"new_thread", 131 | "call_to_actions":[ 132 | { 133 | "payload":"GetStarted" 134 | } 135 | ] 136 | } 137 | ` 138 | 139 | byt := []byte(str) 140 | fmt.Print(string(byt)) 141 | 142 | url := GraphAPI+"me/thread_settings?access_token="+bot.AccessToken 143 | request, err := http.NewRequest("POST", url, bytes.NewReader(byt)) 144 | fmt.Print(url) 145 | request.Header.Set("Content-Type", "application/json") 146 | 147 | if err != nil { 148 | fmt.Println(err) 149 | } 150 | 151 | response, err := bot.Client.Do(request) 152 | if response != nil { 153 | defer response.Body.Close() 154 | } 155 | if err != nil { 156 | fmt.Println(err) 157 | } 158 | 159 | if response.StatusCode != http.StatusOK { 160 | return errors.New("Invalid status code") 161 | } 162 | 163 | body, err := ioutil.ReadAll(response.Body) 164 | if err != nil { 165 | fmt.Println(err) 166 | } 167 | fmt.Println("response Body:", string(body)) 168 | 169 | return nil 170 | } 171 | --------------------------------------------------------------------------------