Skip to main content
Glama
event_message.go9.08 kB
package whatsapp import ( "context" "fmt" "regexp" "strings" "time" "go.mau.fi/whatsmeow/types" domainChatStorage "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/chatstorage" "github.com/aldinokemal/go-whatsapp-web-multidevice/config" pkgError "github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/error" "github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/utils" "github.com/sirupsen/logrus" "go.mau.fi/whatsmeow/types/events" ) var chatStorageRepo domainChatStorage.IChatStorageRepository // SetChatStorageRepository sets the chat storage repository for this package. func SetChatStorageRepository(repo domainChatStorage.IChatStorageRepository) { chatStorageRepo = repo } // forwardMessageToWebhook is a helper function to forward message event to webhook url func forwardMessageToWebhook(ctx context.Context, evt *events.Message) error { // Store the incoming message if err := StoreMessage(ctx, evt); err != nil { logrus.WithError(err).Error("Failed to store incoming message before forwarding to webhook") // Continue processing even if storage fails, as webhook forwarding is primary } logrus.Infof("Forwarding message event to %d configured webhook(s)", len(config.WhatsappWebhook)) payload, err := createMessagePayload(ctx, evt) if err != nil { return err } for _, url := range config.WhatsappWebhook { if err = submitWebhook(ctx, payload, url); err != nil { return err } } logrus.Info("Message event forwarded to webhook") return nil } func createMessagePayload(ctx context.Context, evt *events.Message) (map[string]any, error) { message := utils.BuildEventMessage(evt) waReaction := utils.BuildEventReaction(evt) forwarded := utils.BuildForwarded(evt) body := make(map[string]any) body["sender_id"] = evt.Info.Sender.User body["chat_id"] = evt.Info.Chat.User if from := evt.Info.SourceString(); from != "" { body["from"] = from from_user, from_group := from, "" if strings.Contains(from, " in ") { from_user = strings.Split(from, " in ")[0] from_group = strings.Split(from, " in ")[1] } if strings.HasSuffix(from_user, "@lid") { body["from_lid"] = from_user lid, err := types.ParseJID(from_user) if err != nil { logrus.Errorf("Error when parse jid: %v", err) } else { pn, err := cli.Store.LIDs.GetPNForLID(ctx, lid) if err != nil { logrus.Errorf("Error when get pn for lid %s: %v", lid.String(), err) } if !pn.IsEmpty() { if from_group != "" { body["from"] = fmt.Sprintf("%s in %s", pn.String(), from_group) } else { body["from"] = pn.String() } } } } } if message.ID != "" { tags := regexp.MustCompile(`\B@\w+`).FindAllString(message.Text, -1) tagsMap := make(map[string]bool) for _, tag := range tags { tagsMap[tag] = true } for tag := range tagsMap { lid, err := types.ParseJID(tag[1:] + "@lid") if err != nil { logrus.Errorf("Error when parse jid: %v", err) } else { pn, err := cli.Store.LIDs.GetPNForLID(ctx, lid) if err != nil { logrus.Errorf("Error when get pn for lid %s: %v", lid.String(), err) } if !pn.IsEmpty() { message.Text = strings.Replace(message.Text, tag, fmt.Sprintf("@%s", pn.User), -1) } } } 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 } // Handle protocol messages (revoke, etc.) if protocolMessage := evt.Message.GetProtocolMessage(); protocolMessage != nil { protocolType := protocolMessage.GetType().String() switch protocolType { case "REVOKE": body["action"] = "message_revoked" if key := protocolMessage.GetKey(); key != nil { body["revoked_message_id"] = key.GetID() body["revoked_from_me"] = key.GetFromMe() if key.GetRemoteJID() != "" { body["revoked_chat"] = key.GetRemoteJID() } } case "MESSAGE_EDIT": body["action"] = "message_edited" if editedMessage := protocolMessage.GetEditedMessage(); editedMessage != nil { if editedText := editedMessage.GetExtendedTextMessage(); editedText != nil { body["edited_text"] = editedText.GetText() } else if editedConv := editedMessage.GetConversation(); editedConv != "" { body["edited_text"] = editedConv } } } } if audioMedia := evt.Message.GetAudioMessage(); audioMedia != nil { path, err := utils.ExtractMedia(ctx, cli, 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 := utils.ExtractMedia(ctx, cli, 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 := utils.ExtractMedia(ctx, cli, 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 := utils.ExtractMedia(ctx, cli, 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 := utils.ExtractMedia(ctx, cli, 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 } // StoreMessage stores an incoming message into the chat storage repository. func StoreMessage(ctx context.Context, evt *events.Message) error { if evt.Message == nil { return fmt.Errorf("message event contains no message") } // Extract relevant message information messageID := evt.Info.ID chatJID := evt.Info.Chat.String() senderJID := evt.Info.Sender.String() content := evt.Message.GetConversation() timestamp := evt.Info.Timestamp isFromMe := evt.Info.IsFromMe mediaType := "text" filename := "" url := "" fileLength := uint64(0) // Handle different media types if image := evt.Message.GetImageMessage(); image != nil { mediaType = "image" filename = image.GetCaption() url = image.GetURL() fileLength = image.GetFileLength() } else if video := evt.Message.GetVideoMessage(); video != nil { mediaType = "video" filename = video.GetCaption() url = video.GetURL() fileLength = video.GetFileLength() } else if audio := evt.Message.GetAudioMessage(); audio != nil { mediaType = "audio" url = audio.GetURL() fileLength = audio.GetFileLength() } else if document := evt.Message.GetDocumentMessage(); document != nil { mediaType = "document" filename = document.GetFileName() url = document.GetURL() fileLength = document.GetFileLength() } // Create a new message object for storage msg := &domainChatStorage.Message{ ID: messageID, ChatJID: chatJID, Sender: senderJID, Content: content, Timestamp: timestamp, IsFromMe: isFromMe, MediaType: mediaType, Filename: filename, URL: url, FileLength: fileLength, CreatedAt: time.Now(), UpdatedAt: time.Now(), } // Store the message using the chat storage repository err := chatStorageRepo.StoreMessage(msg) if err != nil { logrus.WithError(err).Error("Failed to store incoming message") return err } logrus.WithFields(logrus.Fields{ "message_id": messageID, "chat_jid": chatJID, "sender": senderJID, }).Info("Incoming message stored successfully") return nil }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/samihalawa/whatsapp-go-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server