-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
404 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package messenger | ||
|
||
import "fmt" | ||
|
||
// FacebookRequest received from Facebook server on webhook, contains messages, delivery reports and/or postbacks | ||
type FacebookRequest struct { | ||
Entry []struct { | ||
ID int64 `json:"id,string"` | ||
Messaging []struct { | ||
Recipient struct { | ||
ID int64 `json:"id,string"` | ||
} `json:"recipient"` | ||
Sender struct { | ||
ID int64 `json:"id,string"` | ||
} `json:"sender"` | ||
Timestamp int `json:"timestamp"` | ||
Message *struct { | ||
Mid string `json:"mid"` | ||
Seq int `json:"seq"` | ||
Text string `json:"text"` | ||
} `json:"message,omitempty"` | ||
Delivery *struct { | ||
Mids []string `json:"mids"` | ||
Seq int `json:"seq"` | ||
Watermark int `json:"watermark"` | ||
} `json:"delivery"` | ||
Postback *struct { | ||
Payload string `json:"payload"` | ||
} `json:"postback"` | ||
} `json:"messaging"` | ||
Time int `json:"time"` | ||
} `json:"entry"` | ||
Object string `json:"object"` | ||
} | ||
|
||
// rawFBResponse received from Facebook server after sending the message | ||
// if Error is null we copy this into FacebookResponse object | ||
type rawFBResponse struct { | ||
MessageID string `json:"message_id"` | ||
RecipientID int64 `json:"recipient_id,string"` | ||
Error *FacebookError `json:"error"` | ||
} | ||
|
||
// FacebookResponse received from Facebook server after sending the message | ||
type FacebookResponse struct { | ||
MessageID string `json:"message_id"` | ||
RecipientID int64 `json:"recipient_id,string"` | ||
} | ||
|
||
// FacebookError received form Facebook server if sending messages failed | ||
type FacebookError struct { | ||
Code int `json:"code"` | ||
FbtraceID string `json:"fbtrace_id"` | ||
Message string `json:"message"` | ||
Type string `json:"type"` | ||
} | ||
|
||
// Error returns Go error object constructed from FacebookError data | ||
func (err *FacebookError) Error() error { | ||
return fmt.Errorf("FB Error: Type %s: %s; FB trace ID: %s", err.Type, err.Message, err.FbtraceID) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
package messenger | ||
|
||
// messenger := &messenger.Messenger { | ||
// VerifyToken: "VERIFY_TOKEN/optional", | ||
// AppSecret: "APP_SECRET/optional", | ||
// AccessToken: "PAGE_ACCESS_TOKEN", | ||
// PageID: "PAGE_ID/optional", | ||
// } | ||
|
||
// ButtonType for buttons, it can be ButtonTypeWebURL or ButtonTypePostback | ||
type ButtonType string | ||
|
||
type AttachmentType string | ||
|
||
type TemplateType string | ||
|
||
type NotificationType string | ||
|
||
type Message interface { | ||
foo() | ||
} | ||
|
||
func (m TextMessage) foo() {} | ||
func (m GenericMessage) foo() {} | ||
|
||
const ( | ||
// ButtonTypeWebURL is type for web links | ||
ButtonTypeWebURL = ButtonType("web_url") | ||
//ButtonTypePostback is type for postback buttons that sends data back to webhook | ||
ButtonTypePostback = ButtonType("postback") | ||
|
||
AttachmentTypeTemplate = AttachmentType("template") | ||
|
||
TemplateTypeGeneric = TemplateType("generic") | ||
|
||
NotificationTypeRegular = NotificationType("REGULAR") | ||
NotificationTypeSilentPusg = NotificationType("SILENT_PUSH") | ||
NotificationTypeNoPush = NotificationType("NO_PUSH") | ||
) | ||
|
||
type TextMessage struct { | ||
Message textMessageContent `json:"message"` | ||
Recipient recipient `json:"recipient"` | ||
NotificationType NotificationType `json:"notification_type,omitempty"` | ||
} | ||
|
||
type GenericMessage struct { | ||
Message genericMessageContent `json:"message"` | ||
Recipient recipient `json:"recipient"` | ||
NotificationType NotificationType `json:"notification_type,omitempty"` | ||
} | ||
|
||
type recipient struct { | ||
ID int64 `json:"id,string"` | ||
} | ||
|
||
type textMessageContent struct { | ||
Text string `json:"text,omitempty"` | ||
} | ||
|
||
type genericMessageContent struct { | ||
Attachment *Attachment `json:"attachment,omitempty"` | ||
} | ||
|
||
// type MessageData struct { | ||
// Text string `json:"text,omitempty"` | ||
// Attachment *Attachment `json:"attachment,omitempty"` | ||
// } | ||
|
||
type Attachment struct { | ||
Type string `json:"type,omitempty"` | ||
Payload payload `json:"payload,omitempty"` | ||
} | ||
|
||
type payload struct { | ||
TemplateType string `json:"template_type,omitempty"` | ||
Elements []Element `json:"elements,omitempty"` | ||
} | ||
|
||
// Element in Generic Message template attachment | ||
type Element struct { | ||
Title string `json:"title"` | ||
Subtitle string `json:"subtitle,omitempty"` | ||
ItemURL string `json:"item_url,omitempty"` | ||
ImageURL string `json:"image_url,omitempty"` | ||
Buttons []Button `json:"buttons,omitempty"` | ||
} | ||
|
||
// Button on Generic Message template element | ||
type Button struct { | ||
Type ButtonType `json:"type"` | ||
URL string `json:"url,omitempty"` | ||
Title string `json:"title"` | ||
Payload string `json:"payload,omitempty"` | ||
} | ||
|
||
// NewTextMessage creates new text message for receiverID | ||
// This function is here for convenient reason, you will | ||
// probably use shorthand version SentTextMessage which sends message immediatly | ||
func NewTextMessage(receiverID int64, text string) TextMessage { | ||
return TextMessage{ | ||
Recipient: recipient{ID: receiverID}, | ||
Message: textMessageContent{Text: text}, | ||
} | ||
} | ||
|
||
// NewGenericMessage creates new Generic Template message for receiverID | ||
// Generic template messages are used for structured messages with images, links, buttons and postbacks | ||
func NewGenericMessage(receiverID int64) GenericMessage { | ||
return GenericMessage{ | ||
Recipient: recipient{ID: receiverID}, | ||
Message: genericMessageContent{ | ||
Attachment: &Attachment{ | ||
Type: "template", | ||
Payload: payload{TemplateType: "generic"}, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
// AddNewElement adds element to Generic template message with defined title, subtitle, link url and image url | ||
// Only title is mandatory, other params can be empty string | ||
// Generic messages can have up to 10 elements which are scolled horizontaly in users messenger | ||
func (m *GenericMessage) AddNewElement(title, subtitle, itemURL, imageURL string) { | ||
m.AddElement(NewElement(title, subtitle, itemURL, imageURL)) | ||
} | ||
|
||
// AddElement adds element e to Generic Message | ||
// Generic messages can have up to 10 elements which are scolled horizontaly in users messenger | ||
// If element contain buttons, you can create element with NewElement, than add some buttons with | ||
// AddWebURLButton and AddPostbackButton and add it to message using this method | ||
func (m *GenericMessage) AddElement(e Element) { | ||
m.Message.Attachment.Payload.Elements = append(m.Message.Attachment.Payload.Elements, e) | ||
} | ||
|
||
// NewElement creates new element with defined title, subtitle, link url and image url | ||
// Only title is mandatory, other params can be empty string | ||
// Instead of calling this function you can also initialize Element struct, depends what you prefere | ||
func NewElement(title, subtitle, itemURL, imageURL string) Element { | ||
e := Element{ | ||
Title: title, | ||
Subtitle: subtitle, | ||
ItemURL: itemURL, | ||
ImageURL: imageURL, | ||
} | ||
return e | ||
} | ||
|
||
// AddWebURLButton adds web link URL button to the element | ||
func (e *Element) AddWebURLButton(title, URL string) { | ||
b := Button{ | ||
Type: ButtonTypeWebURL, | ||
Title: title, | ||
URL: URL, | ||
} | ||
e.Buttons = append(e.Buttons, b) | ||
} | ||
|
||
// AddPostbackButton adds button that sends payload string back to webhook when pressed | ||
func (e *Element) AddPostbackButton(title, payload string) { | ||
b := Button{ | ||
Type: ButtonTypePostback, | ||
Title: title, | ||
Payload: payload, | ||
} | ||
e.Buttons = append(e.Buttons, b) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
package messenger | ||
|
||
import ( | ||
"bytes" | ||
"encoding/json" | ||
"log" | ||
"net/http" | ||
) | ||
|
||
const apiURL = "https://graph.facebook.com/v2.6/" | ||
|
||
// TestURL to mock FB server, used for testing | ||
var TestURL = "" | ||
|
||
// Messenger struct | ||
type Messenger struct { | ||
AccessToken string | ||
PageID string | ||
apiURL string | ||
pageURL string | ||
} | ||
|
||
// New creates new messenger instance | ||
func New(accessToken, pageID string) Messenger { | ||
return Messenger{ | ||
AccessToken: accessToken, | ||
PageID: pageID, | ||
} | ||
} | ||
|
||
// SendMessage sends chat message | ||
func (msng *Messenger) SendMessage(m Message) (FacebookResponse, error) { | ||
if msng.apiURL == "" { | ||
if TestURL != "" { | ||
msng.apiURL = TestURL + "me/messages?access_token=" + msng.AccessToken // testing, mock FB URL | ||
} else { | ||
msng.apiURL = apiURL + "me/messages?access_token=" + msng.AccessToken | ||
} | ||
} | ||
|
||
s, _ := json.Marshal(m) | ||
log.Println("MESSAGE:", string(s)) | ||
req, err := http.NewRequest("POST", msng.apiURL, bytes.NewBuffer(s)) | ||
req.Header.Set("Content-Type", "application/json") | ||
|
||
client := &http.Client{} | ||
resp, err := client.Do(req) | ||
if err != nil { | ||
return FacebookResponse{}, err | ||
} | ||
|
||
return decodeResponse(resp) | ||
} | ||
|
||
// SendTextMessage sends text messate to receiverID | ||
// it is shorthand instead of crating new text message and then sending it | ||
func (msng Messenger) SendTextMessage(receiverID int64, text string) (FacebookResponse, error) { | ||
m := NewTextMessage(receiverID, text) | ||
return msng.SendMessage(&m) | ||
} | ||
|
||
// DecodeRequest decodes http request from FB messagner to FacebookRequest struct | ||
// DecodeRequest will close the Body reader | ||
func DecodeRequest(r *http.Request) FacebookRequest { | ||
defer r.Body.Close() | ||
var fbRq FacebookRequest | ||
err := json.NewDecoder(r.Body).Decode(&fbRq) | ||
if err != nil { | ||
log.Println(err) | ||
} | ||
|
||
return fbRq | ||
} | ||
|
||
// decodeResponse decodes Facebook response after sending message, usually contains MessageID or Error | ||
func decodeResponse(r *http.Response) (FacebookResponse, error) { | ||
defer r.Body.Close() | ||
var fbResp rawFBResponse | ||
err := json.NewDecoder(r.Body).Decode(&fbResp) | ||
if err != nil { | ||
return FacebookResponse{}, nil | ||
} | ||
|
||
if fbResp.Error != nil { | ||
return FacebookResponse{}, fbResp.Error.Error() | ||
} | ||
|
||
return FacebookResponse{ | ||
MessageID: fbResp.MessageID, | ||
RecipientID: fbResp.RecipientID, | ||
}, nil | ||
} |
Oops, something went wrong.