You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

179 lines
5.4 KiB

package whatsapp
import (
"bytes"
"context"
"encoding/json"
"fmt"
"go.mau.fi/whatsmeow/types"
"net/http"
"strings"
"time"
"github.com/aldinokemal/go-whatsapp-web-multidevice/config"
pkgError "github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/error"
"github.com/sirupsen/logrus"
"go.mau.fi/whatsmeow/types/events"
)
// forwardToWebhook is a helper function to forward event to webhook url
func forwardToWebhook(ctx context.Context, evt *events.Message) error {
logrus.Info("Forwarding event to webhook:", config.WhatsappWebhook)
payload, err := createPayload(ctx, evt)
if err != nil {
return err
}
for _, url := range config.WhatsappWebhook {
if err = submitWebhook(payload, url); err != nil {
return err
}
}
logrus.Info("Event forwarded to webhook")
return nil
}
func createPayload(ctx context.Context, evt *events.Message) (map[string]interface{}, error) {
message := buildEventMessage(evt)
waReaction := buildEventReaction(evt)
forwarded := buildForwarded(evt)
body := make(map[string]interface{})
if from := evt.Info.SourceString(); from != "" {
body["from"] = from
if strings.HasSuffix(body["from"].(string), "@lid") {
body["from_lid"] = body["from"]
jid, _ := types.ParseJID(body["from"].(string))
pn, _ := cli.Store.LIDs.GetPNForLID(context.Background(), jid)
if !pn.IsEmpty() {
body["from"] = pn.String()
}
}
}
if message.ID != "" {
body["message"] = message
}
if pushname := evt.Info.PushName; pushname != "" {
body["pushname"] = pushname
}
if waReaction.Message != "" {
body["reaction"] = waReaction
}
if evt.IsViewOnce {
body["view_once"] = evt.IsViewOnce
}
if forwarded {
body["forwarded"] = forwarded
}
if timestamp := evt.Info.Timestamp.Format(time.RFC3339); timestamp != "" {
body["timestamp"] = timestamp
}
if audioMedia := evt.Message.GetAudioMessage(); audioMedia != nil {
path, err := ExtractMedia(ctx, config.PathMedia, audioMedia)
if err != nil {
logrus.Errorf("Failed to download audio from %s: %v", evt.Info.SourceString(), err)
return nil, pkgError.WebhookError(fmt.Sprintf("Failed to download audio: %v", err))
}
body["audio"] = path
}
if contactMessage := evt.Message.GetContactMessage(); contactMessage != nil {
body["contact"] = contactMessage
}
if documentMedia := evt.Message.GetDocumentMessage(); documentMedia != nil {
path, err := ExtractMedia(ctx, config.PathMedia, documentMedia)
if err != nil {
logrus.Errorf("Failed to download document from %s: %v", evt.Info.SourceString(), err)
return nil, pkgError.WebhookError(fmt.Sprintf("Failed to download document: %v", err))
}
body["document"] = path
}
if imageMedia := evt.Message.GetImageMessage(); imageMedia != nil {
path, err := ExtractMedia(ctx, config.PathMedia, imageMedia)
if err != nil {
logrus.Errorf("Failed to download image from %s: %v", evt.Info.SourceString(), err)
return nil, pkgError.WebhookError(fmt.Sprintf("Failed to download image: %v", err))
}
body["image"] = path
}
if listMessage := evt.Message.GetListMessage(); listMessage != nil {
body["list"] = listMessage
}
if liveLocationMessage := evt.Message.GetLiveLocationMessage(); liveLocationMessage != nil {
body["live_location"] = liveLocationMessage
}
if locationMessage := evt.Message.GetLocationMessage(); locationMessage != nil {
body["location"] = locationMessage
}
if orderMessage := evt.Message.GetOrderMessage(); orderMessage != nil {
body["order"] = orderMessage
}
if stickerMedia := evt.Message.GetStickerMessage(); stickerMedia != nil {
path, err := ExtractMedia(ctx, config.PathMedia, stickerMedia)
if err != nil {
logrus.Errorf("Failed to download sticker from %s: %v", evt.Info.SourceString(), err)
return nil, pkgError.WebhookError(fmt.Sprintf("Failed to download sticker: %v", err))
}
body["sticker"] = path
}
if videoMedia := evt.Message.GetVideoMessage(); videoMedia != nil {
path, err := ExtractMedia(ctx, config.PathMedia, videoMedia)
if err != nil {
logrus.Errorf("Failed to download video from %s: %v", evt.Info.SourceString(), err)
return nil, pkgError.WebhookError(fmt.Sprintf("Failed to download video: %v", err))
}
body["video"] = path
}
return body, nil
}
func submitWebhook(payload map[string]interface{}, url string) error {
client := &http.Client{Timeout: 10 * time.Second}
postBody, err := json.Marshal(payload)
if err != nil {
return pkgError.WebhookError(fmt.Sprintf("Failed to marshal body: %v", err))
}
req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(postBody))
if err != nil {
return pkgError.WebhookError(fmt.Sprintf("error when create http object %v", err))
}
secretKey := []byte(config.WhatsappWebhookSecret)
signature, err := getMessageDigestOrSignature(postBody, secretKey)
if err != nil {
return pkgError.WebhookError(fmt.Sprintf("error when create signature %v", err))
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Hub-Signature-256", fmt.Sprintf("sha256=%s", signature))
var attempt int
var maxAttempts = 5
var sleepDuration = 1 * time.Second
for attempt = 0; attempt < maxAttempts; attempt++ {
if _, err = client.Do(req); err == nil {
logrus.Infof("Successfully submitted webhook on attempt %d", attempt+1)
return nil
}
logrus.Warnf("Attempt %d to submit webhook failed: %v", attempt+1, err)
time.Sleep(sleepDuration)
sleepDuration *= 2
}
return pkgError.WebhookError(fmt.Sprintf("error when submit webhook after %d attempts: %v", attempt, err))
}