forked from kkdai/FBBotTemplate
/
messenger.go
136 lines (121 loc) · 4.12 KB
/
messenger.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// ADDED BY DROP - https://github.com/matryer/drop (v0.6)
// source: github.com/maciekmm/messenger-platform-go-sdk (ca9227b956ad50bc8b6225a464f6c0146887f7c5)
// update: drop -f github.com/maciekmm/messenger-platform-go-sdk
// license: The MIT License (MIT) (see repo for details)
package main
import (
"crypto/hmac"
"crypto/sha1"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
)
var (
//GraphAPI specifies host used for API requests
GraphAPI = "https://graph.facebook.com"
)
// MessageReceivedHandler is called when a new message is received
type MessageReceivedHandler func(Event, MessageOpts, ReceivedMessage)
// MessageDeliveredHandler is called when a message sent has been successfully delivered
type MessageDeliveredHandler func(Event, MessageOpts, Delivery)
// PostbackHandler is called when the postback button has been pressed by recipient
type PostbackHandler func(Event, MessageOpts, Postback)
// AuthenticationHandler is called when a new user joins/authenticates
type AuthenticationHandler func(Event, MessageOpts, *Optin)
// Messenger is the main service which handles all callbacks from facebook
// Events are delivered to handlers if they are specified
type Messenger struct {
VerifyToken string
AppSecret string
AccessToken string
PageID string
MessageReceived MessageReceivedHandler
MessageDelivered MessageDeliveredHandler
Postback PostbackHandler
Authentication AuthenticationHandler
}
// Handler is the main HTTP handler for the Messenger service.
// It MUST be attached to some web server in order to receive messages
func (m *Messenger) Handler(rw http.ResponseWriter, req *http.Request) {
if req.Method == "GET" {
query := req.URL.Query()
verifyToken := query.Get("hub.verify_token")
log.Println("Handle", req.Method, " token=", verifyToken, " verify_token=", m.VerifyToken)
log.Println("Equal:", verifyToken == m.VerifyToken)
if verifyToken != m.VerifyToken {
rw.WriteHeader(http.StatusUnauthorized)
log.Println("StatusUnauthorized")
return
}
rw.WriteHeader(http.StatusOK)
log.Println("RET:", query.Get("hub.challenge"))
rw.Write([]byte(query.Get("hub.challenge")))
} else if req.Method == "POST" {
m.handlePOST(rw, req)
} else {
rw.WriteHeader(http.StatusMethodNotAllowed)
}
}
func (m *Messenger) handlePOST(rw http.ResponseWriter, req *http.Request) {
read, err := ioutil.ReadAll(req.Body)
if err != nil {
rw.WriteHeader(http.StatusBadRequest)
return
}
//Message integrity check
if m.AppSecret != "" {
if req.Header.Get("x-hub-signature") == "" || !checkIntegrity(m.AppSecret, read, req.Header.Get("x-hub-signature")[5:]) {
rw.WriteHeader(http.StatusBadRequest)
return
}
}
event := &upstreamEvent{}
err = json.Unmarshal(read, event)
if err != nil {
rw.WriteHeader(http.StatusBadRequest)
return
}
for _, entry := range event.Entries {
for _, message := range entry.Messaging {
if message.Delivery != nil {
if m.MessageDelivered != nil {
go m.MessageDelivered(entry.Event, message.MessageOpts, *message.Delivery)
}
} else if message.Message != nil {
if m.MessageReceived != nil {
go m.MessageReceived(entry.Event, message.MessageOpts, *message.Message)
}
} else if message.Postback != nil {
if m.Postback != nil {
go m.Postback(entry.Event, message.MessageOpts, *message.Postback)
}
} else if m.Authentication != nil {
go m.Authentication(entry.Event, message.MessageOpts, message.Optin)
}
}
}
rw.WriteHeader(http.StatusOK)
rw.Write([]byte(`{"status":"ok"}`))
}
func checkIntegrity(appSecret string, bytes []byte, expectedSignature string) bool {
mac := hmac.New(sha1.New, []byte(appSecret))
mac.Write(bytes)
if fmt.Sprintf("%x", mac.Sum(nil)) != expectedSignature {
return false
}
return true
}
func (m *Messenger) doRequest(method string, url string, body io.Reader) (*http.Response, error) {
req, err := http.NewRequest(method, url, body)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
query := req.URL.Query()
query.Set("access_token", m.AccessToken)
req.URL.RawQuery = query.Encode()
return http.DefaultClient.Do(req)
}