2016-04-13 05:03:26 +03:00
|
|
|
package messenger
|
|
|
|
|
|
|
|
import (
|
2016-09-08 10:55:48 +03:00
|
|
|
"bytes"
|
2017-06-28 05:10:34 +03:00
|
|
|
"crypto/hmac"
|
|
|
|
"crypto/sha1"
|
2016-04-13 09:14:23 +03:00
|
|
|
"encoding/json"
|
2016-04-13 05:03:26 +03:00
|
|
|
"fmt"
|
2016-10-07 16:28:27 +03:00
|
|
|
"io/ioutil"
|
2016-04-13 05:03:26 +03:00
|
|
|
"net/http"
|
2017-06-28 05:10:34 +03:00
|
|
|
"strings"
|
2016-04-13 09:14:23 +03:00
|
|
|
"time"
|
2019-07-31 00:38:58 +03:00
|
|
|
|
|
|
|
"golang.org/x/xerrors"
|
2016-04-13 05:03:26 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2016-04-15 03:16:52 +03:00
|
|
|
// ProfileURL is the API endpoint used for retrieving profiles.
|
2016-09-18 07:13:02 +03:00
|
|
|
// Used in the form: https://graph.facebook.com/v2.6/<USER_ID>?fields=<PROFILE_FIELDS>&access_token=<PAGE_ACCESS_TOKEN>
|
2016-04-15 03:16:52 +03:00
|
|
|
ProfileURL = "https://graph.facebook.com/v2.6/"
|
2021-02-09 11:14:42 +03:00
|
|
|
|
|
|
|
// ProfileFields is a list of JSON field names which will be populated by the profile query.
|
|
|
|
ProfileFields = "first_name,last_name,profile_pic"
|
|
|
|
|
2016-09-08 10:55:48 +03:00
|
|
|
// SendSettingsURL is API endpoint for saving settings.
|
|
|
|
SendSettingsURL = "https://graph.facebook.com/v2.6/me/thread_settings"
|
2018-11-15 15:56:11 +03:00
|
|
|
|
2019-07-25 14:37:27 +03:00
|
|
|
// MessengerProfileURL is the API endpoint where you set properties that define various aspects of the following Messenger Platform features.
|
2018-11-15 15:56:11 +03:00
|
|
|
// Used in the form https://graph.facebook.com/v2.6/me/messenger_profile?access_token=<PAGE_ACCESS_TOKEN>
|
|
|
|
// https://developers.facebook.com/docs/messenger-platform/reference/messenger-profile-api/
|
|
|
|
MessengerProfileURL = "https://graph.facebook.com/v2.6/me/messenger_profile"
|
2016-04-13 05:03:26 +03:00
|
|
|
)
|
|
|
|
|
2016-04-14 03:25:48 +03:00
|
|
|
// Options are the settings used when creating a Messenger client.
|
|
|
|
type Options struct {
|
2016-04-14 02:45:06 +03:00
|
|
|
// Verify sets whether or not to be in the "verify" mode. Used for
|
|
|
|
// verifying webhooks on the Facebook Developer Portal.
|
|
|
|
Verify bool
|
2017-06-28 05:10:34 +03:00
|
|
|
// AppSecret is the app secret from the Facebook Developer Portal. Used when
|
|
|
|
// in the "verify" mode.
|
|
|
|
AppSecret string
|
2016-04-14 02:45:06 +03:00
|
|
|
// VerifyToken is the token to be used when verifying the webhook. Is set
|
|
|
|
// when the webhook is created.
|
2016-04-13 05:03:26 +03:00
|
|
|
VerifyToken string
|
2016-04-14 02:45:06 +03:00
|
|
|
// Token is the access token of the Facebook page to send messages from.
|
|
|
|
Token string
|
2016-05-20 02:18:01 +03:00
|
|
|
// WebhookURL is where the Messenger client should listen for webhook events. Leaving the string blank implies a path of "/".
|
2016-05-03 16:42:25 +03:00
|
|
|
WebhookURL string
|
2016-09-08 10:55:48 +03:00
|
|
|
// Mux is shared mux between several Messenger objects
|
|
|
|
Mux *http.ServeMux
|
2021-06-04 14:42:12 +03:00
|
|
|
// SendAPIVersion is a Send API version
|
|
|
|
SendAPIVersion string
|
2016-04-13 05:03:26 +03:00
|
|
|
}
|
|
|
|
|
2016-04-14 02:45:06 +03:00
|
|
|
// MessageHandler is a handler used for responding to a message containing text.
|
2016-04-13 10:01:42 +03:00
|
|
|
type MessageHandler func(Message, *Response)
|
2016-04-14 02:45:06 +03:00
|
|
|
|
2016-08-18 02:41:25 +03:00
|
|
|
// DeliveryHandler is a handler used for responding to a delivery receipt.
|
2016-04-13 12:26:31 +03:00
|
|
|
type DeliveryHandler func(Delivery, *Response)
|
2016-04-13 09:14:23 +03:00
|
|
|
|
2016-08-18 02:41:25 +03:00
|
|
|
// ReadHandler is a handler used for responding to a read receipt.
|
|
|
|
type ReadHandler func(Read, *Response)
|
|
|
|
|
2016-05-03 16:42:25 +03:00
|
|
|
// PostBackHandler is a handler used postback callbacks.
|
|
|
|
type PostBackHandler func(PostBack, *Response)
|
|
|
|
|
2016-12-08 23:52:21 +03:00
|
|
|
// OptInHandler is a handler used to handle opt-ins.
|
|
|
|
type OptInHandler func(OptIn, *Response)
|
|
|
|
|
2017-02-17 05:29:12 +03:00
|
|
|
// ReferralHandler is a handler used postback callbacks.
|
|
|
|
type ReferralHandler func(ReferralMessage, *Response)
|
|
|
|
|
2018-03-10 12:12:35 +03:00
|
|
|
// AccountLinkingHandler is a handler used to react to an account
|
|
|
|
// being linked or unlinked.
|
|
|
|
type AccountLinkingHandler func(AccountLinking, *Response)
|
|
|
|
|
2016-04-14 02:45:06 +03:00
|
|
|
// Messenger is the client which manages communication with the Messenger Platform API.
|
2016-04-13 05:03:26 +03:00
|
|
|
type Messenger struct {
|
2018-03-10 12:12:35 +03:00
|
|
|
mux *http.ServeMux
|
|
|
|
messageHandlers []MessageHandler
|
|
|
|
deliveryHandlers []DeliveryHandler
|
|
|
|
readHandlers []ReadHandler
|
|
|
|
postBackHandlers []PostBackHandler
|
|
|
|
optInHandlers []OptInHandler
|
|
|
|
referralHandlers []ReferralHandler
|
|
|
|
accountLinkingHandlers []AccountLinkingHandler
|
|
|
|
token string
|
|
|
|
verifyHandler func(http.ResponseWriter, *http.Request)
|
|
|
|
verify bool
|
|
|
|
appSecret string
|
2021-06-04 14:42:12 +03:00
|
|
|
sendAPIVersion string
|
2016-04-13 05:03:26 +03:00
|
|
|
}
|
|
|
|
|
2016-04-14 03:25:48 +03:00
|
|
|
// New creates a new Messenger. You pass in Options in order to affect settings.
|
|
|
|
func New(mo Options) *Messenger {
|
2016-09-08 10:55:48 +03:00
|
|
|
if mo.Mux == nil {
|
|
|
|
mo.Mux = http.NewServeMux()
|
|
|
|
}
|
|
|
|
|
2016-04-13 05:03:26 +03:00
|
|
|
m := &Messenger{
|
2021-06-04 14:42:12 +03:00
|
|
|
mux: mo.Mux,
|
|
|
|
token: mo.Token,
|
|
|
|
verify: mo.Verify,
|
|
|
|
appSecret: mo.AppSecret,
|
|
|
|
sendAPIVersion: mo.SendAPIVersion,
|
2016-04-13 05:03:26 +03:00
|
|
|
}
|
|
|
|
|
2016-05-20 02:18:01 +03:00
|
|
|
if mo.WebhookURL == "" {
|
|
|
|
mo.WebhookURL = "/"
|
|
|
|
}
|
|
|
|
|
2021-06-04 14:42:12 +03:00
|
|
|
if m.sendAPIVersion == "" {
|
|
|
|
m.sendAPIVersion = DefaultSendAPIVersion
|
|
|
|
}
|
|
|
|
|
2016-04-18 13:03:57 +03:00
|
|
|
m.verifyHandler = newVerifyHandler(mo.VerifyToken)
|
2016-05-03 16:42:25 +03:00
|
|
|
m.mux.HandleFunc(mo.WebhookURL, m.handle)
|
2016-04-13 05:03:26 +03:00
|
|
|
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
2016-04-14 02:45:06 +03:00
|
|
|
// HandleMessage adds a new MessageHandler to the Messenger which will be triggered
|
2016-04-14 03:27:05 +03:00
|
|
|
// when a message is received by the client.
|
2016-04-13 12:26:31 +03:00
|
|
|
func (m *Messenger) HandleMessage(f MessageHandler) {
|
|
|
|
m.messageHandlers = append(m.messageHandlers, f)
|
|
|
|
}
|
|
|
|
|
2016-04-14 02:45:06 +03:00
|
|
|
// HandleDelivery adds a new DeliveryHandler to the Messenger which will be triggered
|
2016-08-18 02:41:25 +03:00
|
|
|
// when a previously sent message is delivered to the recipient.
|
2016-04-13 12:26:31 +03:00
|
|
|
func (m *Messenger) HandleDelivery(f DeliveryHandler) {
|
|
|
|
m.deliveryHandlers = append(m.deliveryHandlers, f)
|
2016-04-13 09:14:23 +03:00
|
|
|
}
|
|
|
|
|
2016-12-08 23:52:21 +03:00
|
|
|
// HandleOptIn adds a new OptInHandler to the Messenger which will be triggered
|
|
|
|
// once a user opts in to communicate with the bot.
|
|
|
|
func (m *Messenger) HandleOptIn(f OptInHandler) {
|
|
|
|
m.optInHandlers = append(m.optInHandlers, f)
|
|
|
|
}
|
|
|
|
|
2016-08-18 02:41:25 +03:00
|
|
|
// HandleRead adds a new DeliveryHandler to the Messenger which will be triggered
|
|
|
|
// when a previously sent message is read by the recipient.
|
|
|
|
func (m *Messenger) HandleRead(f ReadHandler) {
|
|
|
|
m.readHandlers = append(m.readHandlers, f)
|
|
|
|
}
|
|
|
|
|
2021-02-17 15:15:29 +03:00
|
|
|
// HandlePostBack adds a new PostBackHandler to the Messenger.
|
2016-05-03 16:42:25 +03:00
|
|
|
func (m *Messenger) HandlePostBack(f PostBackHandler) {
|
|
|
|
m.postBackHandlers = append(m.postBackHandlers, f)
|
|
|
|
}
|
|
|
|
|
2021-02-17 15:15:29 +03:00
|
|
|
// HandleReferral adds a new ReferralHandler to the Messenger.
|
2017-02-17 05:29:12 +03:00
|
|
|
func (m *Messenger) HandleReferral(f ReferralHandler) {
|
|
|
|
m.referralHandlers = append(m.referralHandlers, f)
|
|
|
|
}
|
|
|
|
|
2021-02-17 15:15:29 +03:00
|
|
|
// HandleAccountLinking adds a new AccountLinkingHandler to the Messenger.
|
2018-03-10 12:12:35 +03:00
|
|
|
func (m *Messenger) HandleAccountLinking(f AccountLinkingHandler) {
|
|
|
|
m.accountLinkingHandlers = append(m.accountLinkingHandlers, f)
|
|
|
|
}
|
|
|
|
|
2016-04-14 02:45:06 +03:00
|
|
|
// Handler returns the Messenger in HTTP client form.
|
2016-04-13 05:03:26 +03:00
|
|
|
func (m *Messenger) Handler() http.Handler {
|
|
|
|
return m.mux
|
|
|
|
}
|
|
|
|
|
2019-01-03 19:40:18 +03:00
|
|
|
// ProfileByID retrieves the Facebook user profile associated with that ID.
|
|
|
|
// According to the messenger docs: https://developers.facebook.com/docs/messenger-platform/identity/user-profile,
|
|
|
|
// Developers must ask for access except for some fields that are accessible without permissions.
|
|
|
|
//
|
2019-01-04 12:22:09 +03:00
|
|
|
// At the time of writing (2019-01-04), these fields are
|
2019-01-03 19:40:18 +03:00
|
|
|
// - Name
|
|
|
|
// - First Name
|
|
|
|
// - Last Name
|
2021-02-17 15:15:29 +03:00
|
|
|
// - Profile Picture.
|
2019-01-03 19:06:29 +03:00
|
|
|
func (m *Messenger) ProfileByID(id int64, profileFields []string) (Profile, error) {
|
2016-04-15 03:16:52 +03:00
|
|
|
p := Profile{}
|
|
|
|
url := fmt.Sprintf("%v%v", ProfileURL, id)
|
|
|
|
|
|
|
|
req, err := http.NewRequest("GET", url, nil)
|
|
|
|
if err != nil {
|
|
|
|
return p, err
|
|
|
|
}
|
|
|
|
|
2018-11-28 16:28:08 +03:00
|
|
|
fields := strings.Join(profileFields, ",")
|
|
|
|
req.URL.RawQuery = "fields=" + fields + "&access_token=" + m.token
|
2016-04-15 03:16:52 +03:00
|
|
|
|
|
|
|
client := &http.Client{}
|
|
|
|
resp, err := client.Do(req)
|
2016-10-07 16:28:27 +03:00
|
|
|
if err != nil {
|
|
|
|
return p, err
|
|
|
|
}
|
2016-04-15 03:16:52 +03:00
|
|
|
defer resp.Body.Close()
|
|
|
|
|
2016-10-07 16:28:27 +03:00
|
|
|
content, err := ioutil.ReadAll(resp.Body)
|
|
|
|
if err != nil {
|
|
|
|
return p, err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = json.Unmarshal(content, &p)
|
|
|
|
if err != nil {
|
2022-01-28 12:24:58 +03:00
|
|
|
return p, NewUnmarshalError(err).WithContent(content)
|
2016-10-07 16:28:27 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if p == *new(Profile) {
|
|
|
|
qr := QueryResponse{}
|
|
|
|
err = json.Unmarshal(content, &qr)
|
|
|
|
if qr.Error != nil {
|
2019-07-31 13:04:40 +03:00
|
|
|
err = xerrors.Errorf("facebook error: %w", qr.Error)
|
2016-10-07 16:28:27 +03:00
|
|
|
}
|
|
|
|
}
|
2016-04-15 03:16:52 +03:00
|
|
|
|
2019-07-31 13:04:40 +03:00
|
|
|
return p, err
|
2016-04-15 03:16:52 +03:00
|
|
|
}
|
|
|
|
|
2021-02-17 15:15:29 +03:00
|
|
|
// GreetingSetting sends settings for greeting.
|
2021-02-09 11:14:42 +03:00
|
|
|
func (m *Messenger) GreetingSetting(text string) (QueryResponse, error) {
|
|
|
|
var qr QueryResponse
|
|
|
|
|
2016-09-08 10:55:48 +03:00
|
|
|
d := GreetingSetting{
|
|
|
|
SettingType: "greeting",
|
|
|
|
Greeting: GreetingInfo{
|
|
|
|
Text: text,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
data, err := json.Marshal(d)
|
|
|
|
if err != nil {
|
2021-02-09 11:14:42 +03:00
|
|
|
return qr, err
|
2016-09-08 10:55:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
req, err := http.NewRequest("POST", SendSettingsURL, bytes.NewBuffer(data))
|
|
|
|
if err != nil {
|
2021-02-09 11:14:42 +03:00
|
|
|
return qr, err
|
2016-09-08 10:55:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
req.URL.RawQuery = "access_token=" + m.token
|
|
|
|
|
|
|
|
client := &http.Client{}
|
|
|
|
|
|
|
|
resp, err := client.Do(req)
|
2016-10-07 16:28:27 +03:00
|
|
|
if err != nil {
|
2021-02-09 11:14:42 +03:00
|
|
|
return qr, err
|
2016-10-07 16:28:27 +03:00
|
|
|
}
|
2016-09-08 10:55:48 +03:00
|
|
|
defer resp.Body.Close()
|
|
|
|
|
2021-02-09 11:14:42 +03:00
|
|
|
return getFacebookQueryResponse(resp.Body)
|
2016-09-08 10:55:48 +03:00
|
|
|
}
|
|
|
|
|
2021-02-17 15:15:29 +03:00
|
|
|
// CallToActionsSetting sends settings for Get Started or Persistent Menu.
|
2021-02-09 11:14:42 +03:00
|
|
|
func (m *Messenger) CallToActionsSetting(state string, actions []CallToActionsItem) (QueryResponse, error) {
|
|
|
|
var qr QueryResponse
|
|
|
|
|
2016-09-08 10:55:48 +03:00
|
|
|
d := CallToActionsSetting{
|
|
|
|
SettingType: "call_to_actions",
|
|
|
|
ThreadState: state,
|
|
|
|
CallToActions: actions,
|
|
|
|
}
|
|
|
|
|
|
|
|
data, err := json.Marshal(d)
|
|
|
|
if err != nil {
|
2021-02-09 11:14:42 +03:00
|
|
|
return qr, err
|
2016-09-08 10:55:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
req, err := http.NewRequest("POST", SendSettingsURL, bytes.NewBuffer(data))
|
|
|
|
if err != nil {
|
2021-02-09 11:14:42 +03:00
|
|
|
return qr, err
|
2016-09-08 10:55:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
req.URL.RawQuery = "access_token=" + m.token
|
|
|
|
|
|
|
|
client := &http.Client{}
|
|
|
|
|
|
|
|
resp, err := client.Do(req)
|
2016-10-07 16:28:27 +03:00
|
|
|
if err != nil {
|
2021-02-09 11:14:42 +03:00
|
|
|
return qr, err
|
2016-10-07 16:28:27 +03:00
|
|
|
}
|
2016-09-08 10:55:48 +03:00
|
|
|
defer resp.Body.Close()
|
|
|
|
|
2021-02-09 11:14:42 +03:00
|
|
|
return getFacebookQueryResponse(resp.Body)
|
2016-09-08 10:55:48 +03:00
|
|
|
}
|
|
|
|
|
2016-04-14 02:45:06 +03:00
|
|
|
// handle is the internal HTTP handler for the webhooks.
|
2016-04-13 09:14:23 +03:00
|
|
|
func (m *Messenger) handle(w http.ResponseWriter, r *http.Request) {
|
2016-04-18 13:03:57 +03:00
|
|
|
if r.Method == "GET" {
|
|
|
|
m.verifyHandler(w, r)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2016-04-13 09:14:23 +03:00
|
|
|
var rec Receive
|
|
|
|
|
2017-06-28 05:10:34 +03:00
|
|
|
// consume a *copy* of the request body
|
|
|
|
body, _ := ioutil.ReadAll(r.Body)
|
|
|
|
r.Body = ioutil.NopCloser(bytes.NewBuffer(body))
|
|
|
|
|
|
|
|
err := json.Unmarshal(body, &rec)
|
2016-04-13 09:14:23 +03:00
|
|
|
if err != nil {
|
2019-07-31 00:38:58 +03:00
|
|
|
err = xerrors.Errorf("could not decode response: %w", err)
|
|
|
|
fmt.Println(err)
|
2016-05-20 02:18:01 +03:00
|
|
|
fmt.Println("could not decode response:", err)
|
2019-07-31 00:11:26 +03:00
|
|
|
respond(w, http.StatusBadRequest)
|
2016-04-13 09:14:23 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if rec.Object != "page" {
|
2021-02-17 15:15:29 +03:00
|
|
|
fmt.Println("Object is not page, undefined behavior. Got", rec.Object)
|
2019-07-31 00:11:26 +03:00
|
|
|
respond(w, http.StatusUnprocessableEntity)
|
|
|
|
return
|
2016-04-13 09:14:23 +03:00
|
|
|
}
|
|
|
|
|
2017-06-28 05:10:34 +03:00
|
|
|
if m.verify {
|
|
|
|
if err := m.checkIntegrity(r); err != nil {
|
|
|
|
fmt.Println("could not verify request:", err)
|
2019-07-31 00:11:26 +03:00
|
|
|
respond(w, http.StatusUnauthorized)
|
2017-06-28 05:10:34 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-13 09:14:23 +03:00
|
|
|
m.dispatch(rec)
|
|
|
|
|
2019-07-31 00:11:26 +03:00
|
|
|
respond(w, http.StatusAccepted) // We do not return any meaningful response immediately so it should be 202
|
|
|
|
}
|
|
|
|
|
|
|
|
func respond(w http.ResponseWriter, code int) {
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
2019-07-31 00:43:42 +03:00
|
|
|
fmt.Fprintf(w, `{"code": %d, "status": "%s"}`, code, http.StatusText(code))
|
2016-04-13 09:14:23 +03:00
|
|
|
}
|
|
|
|
|
2021-02-17 15:15:29 +03:00
|
|
|
// checkIntegrity checks the integrity of the requests received.
|
2017-06-28 05:10:34 +03:00
|
|
|
func (m *Messenger) checkIntegrity(r *http.Request) error {
|
|
|
|
if m.appSecret == "" {
|
2019-07-31 00:38:58 +03:00
|
|
|
return xerrors.New("missing app secret")
|
2017-06-28 05:10:34 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
sigHeader := "X-Hub-Signature"
|
|
|
|
sig := strings.SplitN(r.Header.Get(sigHeader), "=", 2)
|
|
|
|
if len(sig) == 1 {
|
|
|
|
if sig[0] == "" {
|
2019-07-31 00:38:58 +03:00
|
|
|
return xerrors.Errorf("missing %s header", sigHeader)
|
2017-06-28 05:10:34 +03:00
|
|
|
}
|
2019-07-31 00:38:58 +03:00
|
|
|
return xerrors.Errorf("malformed %s header: %v", sigHeader, strings.Join(sig, "="))
|
2017-06-28 05:10:34 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
checkSHA1 := func(body []byte, hash string) error {
|
|
|
|
mac := hmac.New(sha1.New, []byte(m.appSecret))
|
|
|
|
if mac.Write(body); fmt.Sprintf("%x", mac.Sum(nil)) != hash {
|
2019-07-31 00:38:58 +03:00
|
|
|
return xerrors.Errorf("invalid signature: %s", hash)
|
2017-06-28 05:10:34 +03:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
body, _ := ioutil.ReadAll(r.Body)
|
|
|
|
r.Body = ioutil.NopCloser(bytes.NewBuffer(body))
|
|
|
|
|
|
|
|
sigEnc := strings.ToLower(sig[0])
|
|
|
|
sigHash := strings.ToLower(sig[1])
|
|
|
|
switch sigEnc {
|
|
|
|
case "sha1":
|
|
|
|
return checkSHA1(body, sigHash)
|
|
|
|
default:
|
2019-07-31 00:38:58 +03:00
|
|
|
return xerrors.Errorf("unknown %s header encoding, expected sha1: %s", sigHeader, sig[0])
|
2017-06-28 05:10:34 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-14 02:45:06 +03:00
|
|
|
// dispatch triggers all of the relevant handlers when a webhook event is received.
|
2016-04-13 09:14:23 +03:00
|
|
|
func (m *Messenger) dispatch(r Receive) {
|
|
|
|
for _, entry := range r.Entry {
|
|
|
|
for _, info := range entry.Messaging {
|
2019-07-25 14:37:27 +03:00
|
|
|
a := m.classify(info)
|
2016-04-13 09:14:23 +03:00
|
|
|
if a == UnknownAction {
|
|
|
|
fmt.Println("Unknown action:", info)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2016-04-13 12:26:31 +03:00
|
|
|
resp := &Response{
|
2021-08-24 10:13:05 +03:00
|
|
|
to: Recipient{ID: info.Sender.ID},
|
2021-06-04 14:42:12 +03:00
|
|
|
token: m.token,
|
|
|
|
sendAPIVersion: m.sendAPIVersion,
|
2016-04-13 12:26:31 +03:00
|
|
|
}
|
2016-04-13 12:12:23 +03:00
|
|
|
|
2016-04-13 12:26:31 +03:00
|
|
|
switch a {
|
2016-05-04 11:09:34 +03:00
|
|
|
case TextAction:
|
|
|
|
for _, f := range m.messageHandlers {
|
|
|
|
message := *info.Message
|
|
|
|
message.Sender = info.Sender
|
|
|
|
message.Recipient = info.Recipient
|
2016-10-04 04:44:18 +03:00
|
|
|
message.Time = time.Unix(info.Timestamp/int64(time.Microsecond), 0)
|
2016-05-04 11:09:34 +03:00
|
|
|
f(message, resp)
|
|
|
|
}
|
|
|
|
case DeliveryAction:
|
|
|
|
for _, f := range m.deliveryHandlers {
|
|
|
|
f(*info.Delivery, resp)
|
|
|
|
}
|
2016-08-18 02:41:25 +03:00
|
|
|
case ReadAction:
|
|
|
|
for _, f := range m.readHandlers {
|
|
|
|
f(*info.Read, resp)
|
|
|
|
}
|
2016-05-04 11:09:34 +03:00
|
|
|
case PostBackAction:
|
|
|
|
for _, f := range m.postBackHandlers {
|
|
|
|
message := *info.PostBack
|
|
|
|
message.Sender = info.Sender
|
|
|
|
message.Recipient = info.Recipient
|
2016-10-04 04:44:18 +03:00
|
|
|
message.Time = time.Unix(info.Timestamp/int64(time.Microsecond), 0)
|
2016-05-04 11:09:34 +03:00
|
|
|
f(message, resp)
|
|
|
|
}
|
2016-12-08 23:52:21 +03:00
|
|
|
case OptInAction:
|
|
|
|
for _, f := range m.optInHandlers {
|
|
|
|
message := *info.OptIn
|
|
|
|
message.Sender = info.Sender
|
|
|
|
message.Recipient = info.Recipient
|
|
|
|
message.Time = time.Unix(info.Timestamp/int64(time.Microsecond), 0)
|
|
|
|
f(message, resp)
|
|
|
|
}
|
2017-02-17 05:29:12 +03:00
|
|
|
case ReferralAction:
|
|
|
|
for _, f := range m.referralHandlers {
|
|
|
|
message := *info.ReferralMessage
|
|
|
|
message.Sender = info.Sender
|
|
|
|
message.Recipient = info.Recipient
|
|
|
|
message.Time = time.Unix(info.Timestamp/int64(time.Microsecond), 0)
|
|
|
|
f(message, resp)
|
|
|
|
}
|
2018-03-10 12:12:35 +03:00
|
|
|
case AccountLinkingAction:
|
|
|
|
for _, f := range m.accountLinkingHandlers {
|
|
|
|
message := *info.AccountLinking
|
|
|
|
message.Sender = info.Sender
|
|
|
|
message.Recipient = info.Recipient
|
|
|
|
message.Time = time.Unix(info.Timestamp/int64(time.Microsecond), 0)
|
|
|
|
f(message, resp)
|
|
|
|
}
|
2016-04-13 09:14:23 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-17 15:15:29 +03:00
|
|
|
// Response returns new Response object.
|
2016-09-08 10:55:48 +03:00
|
|
|
func (m *Messenger) Response(to int64) *Response {
|
|
|
|
return &Response{
|
2021-08-24 10:13:05 +03:00
|
|
|
to: Recipient{ID: to},
|
2021-06-04 14:42:12 +03:00
|
|
|
token: m.token,
|
|
|
|
sendAPIVersion: m.sendAPIVersion,
|
2016-09-08 10:55:48 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-23 00:49:05 +03:00
|
|
|
// Send will send a textual message to a user. This user must have previously initiated a conversation with the bot.
|
2021-02-09 11:14:42 +03:00
|
|
|
func (m *Messenger) Send(to Recipient, message string, messagingType MessagingType, metadata string, tags ...string) (QueryResponse, error) {
|
|
|
|
return m.SendWithReplies(to, message, nil, messagingType, metadata, tags...)
|
2016-09-23 00:49:05 +03:00
|
|
|
}
|
|
|
|
|
2021-02-17 15:15:29 +03:00
|
|
|
// SendGeneralMessage will send the GenericTemplate message.
|
2021-02-09 11:14:42 +03:00
|
|
|
func (m *Messenger) SendGeneralMessage(to Recipient, elements *[]StructuredMessageElement, messagingType MessagingType, metadata string, tags ...string) (QueryResponse, error) {
|
2017-03-15 20:29:26 +03:00
|
|
|
r := &Response{
|
2021-06-04 14:42:12 +03:00
|
|
|
token: m.token,
|
|
|
|
to: to,
|
|
|
|
sendAPIVersion: m.sendAPIVersion,
|
2017-03-15 20:29:26 +03:00
|
|
|
}
|
2021-02-09 11:14:42 +03:00
|
|
|
return r.GenericTemplate(elements, messagingType, metadata, tags...)
|
2017-03-15 20:29:26 +03:00
|
|
|
}
|
|
|
|
|
2016-09-23 00:49:05 +03:00
|
|
|
// SendWithReplies sends a textual message to a user, but gives them the option of numerous quick response options.
|
2021-02-09 11:14:42 +03:00
|
|
|
func (m *Messenger) SendWithReplies(to Recipient, message string, replies []QuickReply, messagingType MessagingType, metadata string, tags ...string) (QueryResponse, error) {
|
2016-09-23 00:49:05 +03:00
|
|
|
response := &Response{
|
2021-06-04 14:42:12 +03:00
|
|
|
token: m.token,
|
|
|
|
to: to,
|
|
|
|
sendAPIVersion: m.sendAPIVersion,
|
2016-09-23 00:49:05 +03:00
|
|
|
}
|
|
|
|
|
2021-02-09 11:14:42 +03:00
|
|
|
return response.TextWithReplies(message, replies, messagingType, metadata, tags...)
|
2016-09-23 00:49:05 +03:00
|
|
|
}
|
|
|
|
|
2016-10-09 06:28:21 +03:00
|
|
|
// Attachment sends an image, sound, video or a regular file to a given recipient.
|
2021-02-09 11:14:42 +03:00
|
|
|
func (m *Messenger) Attachment(to Recipient, dataType AttachmentType, url string, messagingType MessagingType, metadata string, tags ...string) (QueryResponse, error) {
|
2016-10-09 06:28:21 +03:00
|
|
|
response := &Response{
|
2021-06-04 14:42:12 +03:00
|
|
|
token: m.token,
|
|
|
|
to: to,
|
|
|
|
sendAPIVersion: m.sendAPIVersion,
|
2016-10-09 06:28:21 +03:00
|
|
|
}
|
|
|
|
|
2021-02-09 11:14:42 +03:00
|
|
|
return response.Attachment(dataType, url, messagingType, metadata, tags...)
|
2016-10-09 06:28:21 +03:00
|
|
|
}
|
|
|
|
|
2018-11-15 15:56:11 +03:00
|
|
|
// EnableChatExtension set the homepage url required for a chat extension.
|
|
|
|
func (m *Messenger) EnableChatExtension(homeURL HomeURL) error {
|
|
|
|
wrap := map[string]interface{}{
|
|
|
|
"home_url": homeURL,
|
|
|
|
}
|
|
|
|
data, err := json.Marshal(wrap)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
req, err := http.NewRequest("POST", MessengerProfileURL, 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)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
|
|
|
|
return checkFacebookError(resp.Body)
|
|
|
|
}
|
|
|
|
|
2022-07-29 15:40:30 +03:00
|
|
|
func (m *Messenger) SenderAction(to Recipient, action SenderAction) (QueryResponse, error) {
|
|
|
|
response := &Response{
|
|
|
|
token: m.token,
|
|
|
|
to: to,
|
|
|
|
sendAPIVersion: m.sendAPIVersion,
|
|
|
|
}
|
|
|
|
return response.SenderAction(action)
|
|
|
|
}
|
|
|
|
|
2016-04-14 02:45:06 +03:00
|
|
|
// classify determines what type of message a webhook event is.
|
2019-07-25 14:37:27 +03:00
|
|
|
func (m *Messenger) classify(info MessageInfo) Action {
|
2016-04-13 09:14:23 +03:00
|
|
|
if info.Message != nil {
|
|
|
|
return TextAction
|
2016-04-13 12:12:23 +03:00
|
|
|
} else if info.Delivery != nil {
|
|
|
|
return DeliveryAction
|
2016-08-18 02:41:25 +03:00
|
|
|
} else if info.Read != nil {
|
|
|
|
return ReadAction
|
2016-05-03 16:42:25 +03:00
|
|
|
} else if info.PostBack != nil {
|
|
|
|
return PostBackAction
|
2016-12-08 23:52:21 +03:00
|
|
|
} else if info.OptIn != nil {
|
|
|
|
return OptInAction
|
2017-02-17 05:29:12 +03:00
|
|
|
} else if info.ReferralMessage != nil {
|
|
|
|
return ReferralAction
|
2018-03-10 12:12:35 +03:00
|
|
|
} else if info.AccountLinking != nil {
|
|
|
|
return AccountLinkingAction
|
2016-04-13 09:14:23 +03:00
|
|
|
}
|
|
|
|
return UnknownAction
|
|
|
|
}
|
|
|
|
|
2021-02-17 15:15:29 +03:00
|
|
|
// newVerifyHandler returns a function which can be used to handle webhook verification.
|
2016-04-13 05:03:26 +03:00
|
|
|
func newVerifyHandler(token string) func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if r.FormValue("hub.verify_token") == token {
|
|
|
|
fmt.Fprintln(w, r.FormValue("hub.challenge"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
fmt.Fprintln(w, "Incorrect verify token.")
|
|
|
|
}
|
|
|
|
}
|