diff --git a/README.md b/README.md
index fcfb0624..d71214ac 100644
--- a/README.md
+++ b/README.md
@@ -323,6 +323,13 @@ Check the [API Reference](https://github.com/asternic/wuzapi/blob/main/API.md)
Nicolas
+
+
+
+
+ cleitonme
+
+ |
@@ -337,13 +344,6 @@ Check the [API Reference](https://github.com/asternic/wuzapi/blob/main/API.md)
Luiz Felipe Neves
|
-
-
-
-
- cleitonme
-
- |
@@ -367,6 +367,13 @@ Check the [API Reference](https://github.com/asternic/wuzapi/blob/main/API.md)
ramon-victor
|
+
+
+
+
+ Anton Kozyk
+
+ |
@@ -395,6 +402,8 @@ Check the [API Reference](https://github.com/asternic/wuzapi/blob/main/API.md)
Vitor Silva Lima
|
+
+
@@ -402,8 +411,6 @@ Check the [API Reference](https://github.com/asternic/wuzapi/blob/main/API.md)
Ruan Kaylo
|
-
-
@@ -439,6 +446,8 @@ Check the [API Reference](https://github.com/asternic/wuzapi/blob/main/API.md)
João Victor Souza
|
+
+
@@ -446,15 +455,6 @@ Check the [API Reference](https://github.com/asternic/wuzapi/blob/main/API.md)
Gustavo Salomé
|
-
-
-
-
-
-
- Anton Kozyk
-
- |
@@ -500,7 +500,7 @@ Check the [API Reference](https://github.com/asternic/wuzapi/blob/main/API.md)
|
-
+
Joseph Fernandes
diff --git a/handlers.go b/handlers.go
index 285a7a2f..a4436e87 100644
--- a/handlers.go
+++ b/handlers.go
@@ -949,6 +949,11 @@ func (s *server) SendDocument() http.HandlerFunc {
historyLimit, _ := strconv.Atoi(historyStr)
s.saveOutgoingMessageToHistory(txtid, recipient.String(), msgid, "document", t.Caption, "", historyLimit)
+ // Publish sent message event to RabbitMQ
+ token := r.Context().Value("userinfo").(Values).Get("Token")
+ userID := r.Context().Value("userinfo").(Values).Get("Id")
+ s.publishSentMessageEvent(token, userID, txtid, recipient, msgid, msg, resp.Timestamp)
+
log.Info().Str("timestamp", fmt.Sprintf("%v", resp.Timestamp)).Str("id", msgid).Msg("Message sent")
response := map[string]interface{}{"Details": "Sent", "Timestamp": resp.Timestamp.Unix(), "Id": msgid}
responseJson, err := json.Marshal(response)
@@ -1115,6 +1120,11 @@ func (s *server) SendAudio() http.HandlerFunc {
historyLimit, _ := strconv.Atoi(historyStr)
s.saveOutgoingMessageToHistory(txtid, recipient.String(), msgid, "audio", "", "", historyLimit)
+ // Publish sent message event to RabbitMQ
+ token := r.Context().Value("userinfo").(Values).Get("Token")
+ userID := r.Context().Value("userinfo").(Values).Get("Id")
+ s.publishSentMessageEvent(token, userID, txtid, recipient, msgid, msg, resp.Timestamp)
+
log.Info().Str("timestamp", fmt.Sprintf("%v", resp.Timestamp)).Str("id", msgid).Msg("Message sent")
response := map[string]interface{}{"Details": "Sent", "Timestamp": resp.Timestamp.Unix(), "Id": msgid}
responseJson, err := json.Marshal(response)
@@ -1313,6 +1323,11 @@ func (s *server) SendImage() http.HandlerFunc {
historyLimit, _ := strconv.Atoi(historyStr)
s.saveOutgoingMessageToHistory(txtid, recipient.String(), msgid, "image", t.Caption, "", historyLimit)
+ // Publish sent message event to RabbitMQ
+ token := r.Context().Value("userinfo").(Values).Get("Token")
+ userID := r.Context().Value("userinfo").(Values).Get("Id")
+ s.publishSentMessageEvent(token, userID, txtid, recipient, msgid, msg, resp.Timestamp)
+
log.Info().Str("timestamp", fmt.Sprintf("%v", resp.Timestamp)).Str("id", msgid).Msg("Message sent")
response := map[string]interface{}{"Details": "Sent", "Timestamp": resp.Timestamp.Unix(), "Id": msgid}
responseJson, err := json.Marshal(response)
@@ -1462,6 +1477,11 @@ func (s *server) SendSticker() http.HandlerFunc {
historyLimit, _ := strconv.Atoi(historyStr)
s.saveOutgoingMessageToHistory(txtid, recipient.String(), msgid, "sticker", "", "", historyLimit)
+ // Publish sent message event to RabbitMQ
+ token := r.Context().Value("userinfo").(Values).Get("Token")
+ userID := r.Context().Value("userinfo").(Values).Get("Id")
+ s.publishSentMessageEvent(token, userID, txtid, recipient, msgid, msg, resp.Timestamp)
+
log.Info().Str("timestamp", fmt.Sprintf("%v", resp.Timestamp)).Str("id", msgid).Msg("Message sent")
response := map[string]interface{}{"Details": "Sent", "Timestamp": resp.Timestamp.Unix(), "Id": msgid}
responseJson, err := json.Marshal(response)
@@ -1631,6 +1651,11 @@ func (s *server) SendVideo() http.HandlerFunc {
historyLimit, _ := strconv.Atoi(historyStr)
s.saveOutgoingMessageToHistory(txtid, recipient.String(), msgid, "video", t.Caption, "", historyLimit)
+ // Publish sent message event to RabbitMQ
+ token := r.Context().Value("userinfo").(Values).Get("Token")
+ userID := r.Context().Value("userinfo").(Values).Get("Id")
+ s.publishSentMessageEvent(token, userID, txtid, recipient, msgid, msg, resp.Timestamp)
+
log.Info().Str("timestamp", fmt.Sprintf("%v", resp.Timestamp)).Str("id", msgid).Msg("Message sent")
response := map[string]interface{}{"Details": "Sent", "Timestamp": resp.Timestamp.Unix(), "Id": msgid}
responseJson, err := json.Marshal(response)
@@ -1748,6 +1773,11 @@ func (s *server) SendContact() http.HandlerFunc {
historyLimit, _ := strconv.Atoi(historyStr)
s.saveOutgoingMessageToHistory(txtid, recipient.String(), msgid, "contact", t.Name, "", historyLimit)
+ // Publish sent message event to RabbitMQ
+ token := r.Context().Value("userinfo").(Values).Get("Token")
+ userID := r.Context().Value("userinfo").(Values).Get("Id")
+ s.publishSentMessageEvent(token, userID, txtid, recipient, msgid, msg, resp.Timestamp)
+
log.Info().Str("timestamp", fmt.Sprintf("%v", resp.Timestamp)).Str("id", msgid).Msg("Message sent")
response := map[string]interface{}{"Details": "Sent", "Timestamp": resp.Timestamp.Unix(), "Id": msgid}
responseJson, err := json.Marshal(response)
@@ -1867,6 +1897,11 @@ func (s *server) SendLocation() http.HandlerFunc {
historyLimit, _ := strconv.Atoi(historyStr)
s.saveOutgoingMessageToHistory(txtid, recipient.String(), msgid, "location", t.Name, "", historyLimit)
+ // Publish sent message event to RabbitMQ
+ token := r.Context().Value("userinfo").(Values).Get("Token")
+ userID := r.Context().Value("userinfo").(Values).Get("Id")
+ s.publishSentMessageEvent(token, userID, txtid, recipient, msgid, msg, resp.Timestamp)
+
log.Info().Str("timestamp", fmt.Sprintf("%v", resp.Timestamp)).Str("id", msgid).Msg("Message sent")
response := map[string]interface{}{"Details": "Sent", "Timestamp": resp.Timestamp.Unix(), "Id": msgid}
responseJson, err := json.Marshal(response)
@@ -1961,16 +1996,22 @@ func (s *server) SendButtons() http.HandlerFunc {
Buttons: buttons,
}
- resp, err = clientManager.GetWhatsmeowClient(txtid).SendMessage(context.Background(), recipient, &waE2E.Message{ViewOnceMessage: &waE2E.FutureProofMessage{
+ msg := &waE2E.Message{ViewOnceMessage: &waE2E.FutureProofMessage{
Message: &waE2E.Message{
ButtonsMessage: msg2,
},
- }}, whatsmeow.SendRequestExtra{ID: msgid})
+ }}
+ resp, err = clientManager.GetWhatsmeowClient(txtid).SendMessage(context.Background(), recipient, msg, whatsmeow.SendRequestExtra{ID: msgid})
if err != nil {
s.Respond(w, r, http.StatusInternalServerError, errors.New(fmt.Sprintf("error sending message: %v", err)))
return
}
+ // Publish sent message event to RabbitMQ
+ token := r.Context().Value("userinfo").(Values).Get("Token")
+ userID := r.Context().Value("userinfo").(Values).Get("Id")
+ s.publishSentMessageEvent(token, userID, txtid, recipient, msgid, msg, resp.Timestamp)
+
log.Info().Str("timestamp", fmt.Sprintf("%v", resp.Timestamp)).Str("id", msgid).Msg("Message sent")
response := map[string]interface{}{"Details": "Sent", "Timestamp": resp.Timestamp.Unix(), "Id": msgid}
responseJson, err := json.Marshal(response)
@@ -2119,6 +2160,11 @@ func (s *server) SendList() http.HandlerFunc {
return
}
+ // Publish sent message event to RabbitMQ
+ token := r.Context().Value("userinfo").(Values).Get("Token")
+ userID := r.Context().Value("userinfo").(Values).Get("Id")
+ s.publishSentMessageEvent(token, userID, txtid, recipient, msgid, msg, resp.Timestamp)
+
log.Info().Str("timestamp", fmt.Sprintf("%v", resp.Timestamp)).Str("id", msgid).Msg("Message list sent")
response := map[string]interface{}{
"Details": "Sent",
@@ -2297,6 +2343,12 @@ func (s *server) SendMessage() http.HandlerFunc {
historyStr := r.Context().Value("userinfo").(Values).Get("History")
historyLimit, _ := strconv.Atoi(historyStr)
s.saveOutgoingMessageToHistory(txtid, recipient.String(), msgid, "text", t.Body, "", historyLimit)
+
+ // Publish sent message event to RabbitMQ
+ token := r.Context().Value("userinfo").(Values).Get("Token")
+ userID := r.Context().Value("userinfo").(Values).Get("Id")
+ s.publishSentMessageEvent(token, userID, txtid, recipient, msgid, msg, resp.Timestamp)
+
log.Info().Str("timestamp", fmt.Sprintf("%v", resp.Timestamp)).Str("id", msgid).Msg("Message sent")
response := map[string]interface{}{"Details": "Sent", "Timestamp": resp.Timestamp.Unix(), "Id": msgid}
responseJson, err := json.Marshal(response)
@@ -2370,6 +2422,11 @@ func (s *server) SendPoll() http.HandlerFunc {
return
}
+ // Publish sent message event to RabbitMQ
+ token := r.Context().Value("userinfo").(Values).Get("Token")
+ userID := r.Context().Value("userinfo").(Values).Get("Id")
+ s.publishSentMessageEvent(token, userID, txtid, recipient, msgid, pollMessage, resp.Timestamp, "poll")
+
log.Info().Str("timestamp", fmt.Sprintf("%v", resp.Timestamp)).Str("id", msgid).Msg("Poll sent")
response := map[string]interface{}{"Details": "Poll sent successfully", "Id": msgid}
@@ -2800,6 +2857,11 @@ func (s *server) SendTemplate() http.HandlerFunc {
return
}
+ // Publish sent message event to RabbitMQ
+ token := r.Context().Value("userinfo").(Values).Get("Token")
+ userID := r.Context().Value("userinfo").(Values).Get("Id")
+ s.publishSentMessageEvent(token, userID, txtid, recipient, msgid, msg, resp.Timestamp)
+
log.Info().Str("timestamp", fmt.Sprintf("%d", resp.Timestamp.Unix())).Str("id", msgid).Msg("Message sent")
response := map[string]interface{}{"Details": "Sent", "Timestamp": resp.Timestamp.Unix(), "Id": msgid}
responseJson, err := json.Marshal(response)
@@ -6558,3 +6620,165 @@ func (s *server) DownloadSticker() http.HandlerFunc {
return
}
}
+
+// Helper function to determine message type from waE2E.Message
+func (s *server) getMessageType(msg *waE2E.Message) string {
+ if msg.GetConversation() != "" {
+ return "text"
+ }
+ if msg.GetExtendedTextMessage() != nil {
+ return "text"
+ }
+ if msg.GetImageMessage() != nil {
+ return "image"
+ }
+ if msg.GetVideoMessage() != nil {
+ return "video"
+ }
+ if msg.GetAudioMessage() != nil {
+ return "audio"
+ }
+ if msg.GetDocumentMessage() != nil {
+ return "document"
+ }
+ if msg.GetStickerMessage() != nil {
+ return "sticker"
+ }
+ if msg.GetContactMessage() != nil {
+ return "contact"
+ }
+ if msg.GetLocationMessage() != nil {
+ return "location"
+ }
+ // Note: Poll messages are handled by BuildPollCreation and the type is determined from RawMessage
+ if msg.GetButtonsMessage() != nil || msg.GetButtonsResponseMessage() != nil {
+ return "buttons"
+ }
+ if msg.GetListMessage() != nil || msg.GetListResponseMessage() != nil {
+ return "list"
+ }
+ if msg.GetTemplateMessage() != nil {
+ return "template"
+ }
+ return "text"
+}
+
+// publishSentMessageEvent creates and publishes a Message event for sent messages to RabbitMQ
+// messageTypeOverride is optional - if provided, it will be used instead of auto-detecting the type
+func (s *server) publishSentMessageEvent(token, userID, txtid string, recipient types.JID, msgid string, msg *waE2E.Message, timestamp time.Time, messageTypeOverride ...string) {
+ // Get the client to access store info
+ client := clientManager.GetWhatsmeowClient(txtid)
+ if client == nil {
+ return
+ }
+
+ // Get sender JID (account owner) - use ToNonAD() to remove device ID, matching manual messages
+ var senderJID types.JID
+ if client.Store != nil && client.Store.ID != nil {
+ senderJID = client.Store.ID.ToNonAD()
+ }
+
+ // Determine if it's a group
+ isGroup := recipient.Server == types.GroupServer || recipient.Server == types.BroadcastServer
+
+ // Determine message type
+ messageType := s.getMessageType(msg)
+ if len(messageTypeOverride) > 0 && messageTypeOverride[0] != "" {
+ messageType = messageTypeOverride[0]
+ }
+
+ // Get LIDs for SenderAlt and RecipientAlt (matching manual message format)
+ var senderLID types.JID
+ var recipientLID types.JID
+ if client.Store != nil && client.Store.LIDs != nil {
+ ctx := context.Background()
+
+ // Get sender LID
+ if !senderJID.IsEmpty() {
+ if lid, err := client.Store.LIDs.GetLIDForPN(ctx, senderJID); err == nil && !lid.IsEmpty() {
+ senderLID = lid
+ }
+ }
+
+ // Get recipient LID (only for non-group chats)
+ if !isGroup && !recipient.IsEmpty() {
+ if lid, err := client.Store.LIDs.GetLIDForPN(ctx, recipient); err == nil && !lid.IsEmpty() {
+ recipientLID = lid
+ }
+ }
+ }
+
+ // Create MessageInfo structure
+ messageInfo := types.MessageInfo{
+ MessageSource: types.MessageSource{
+ Chat: recipient,
+ Sender: senderJID,
+ IsFromMe: true,
+ IsGroup: isGroup,
+ },
+ ID: msgid,
+ Timestamp: timestamp,
+ Type: messageType,
+ }
+
+ // Set SenderAlt and RecipientAlt (LIDs)
+ if !senderLID.IsEmpty() {
+ messageInfo.SenderAlt = senderLID
+ }
+ if !recipientLID.IsEmpty() {
+ messageInfo.RecipientAlt = recipientLID
+ }
+
+ // Set DeviceSentMeta (matching manual message format)
+ messageInfo.DeviceSentMeta = &types.DeviceSentMeta{
+ DestinationJID: recipient.String(),
+ Phash: "",
+ }
+
+ // Get push name from store
+ if client.Store != nil && client.Store.PushName != "" {
+ messageInfo.PushName = client.Store.PushName
+ }
+
+ // Wrap message in DeviceSentMessage structure for RawMessage (matching manual message format)
+ rawMessage := &waE2E.Message{
+ DeviceSentMessage: &waE2E.DeviceSentMessage{
+ DestinationJID: proto.String(recipient.String()),
+ Message: msg,
+ },
+ }
+
+ // Create the event structure matching whatsmeow's events.Message
+ messageEvent := map[string]interface{}{
+ "Info": messageInfo,
+ "Message": msg,
+ "IsEphemeral": false,
+ "IsViewOnce": false,
+ "IsViewOnceV2": false,
+ "IsViewOnceV2Extension": false,
+ "IsDocumentWithCaption": false,
+ "IsLottieSticker": false,
+ "IsBotInvoke": false,
+ "IsEdit": false,
+ "SourceWebMsg": nil,
+ "UnavailableRequestID": "",
+ "RetryCount": 0,
+ "NewsletterMeta": nil,
+ "RawMessage": rawMessage,
+ }
+
+ // Create the postmap structure that matches what sendEventWithWebHook expects
+ postmap := make(map[string]interface{})
+ postmap["type"] = "Message"
+ postmap["event"] = messageEvent
+
+ // Marshal to JSON
+ jsonData, err := json.Marshal(postmap)
+ if err != nil {
+ log.Error().Err(err).Msg("Failed to marshal sent message event to JSON")
+ return
+ }
+
+ // Publish directly to RabbitMQ (bypassing subscription check for sent messages)
+ go sendToGlobalRabbit(jsonData, token, userID)
+}
diff --git a/main.go b/main.go
index 0d6d7238..a1a6ebe6 100755
--- a/main.go
+++ b/main.go
@@ -51,6 +51,7 @@ var (
logType = flag.String("logtype", "console", "Type of log output (console or json)")
skipMedia = flag.Bool("skipmedia", false, "Do not attempt to download media in messages")
osName = flag.String("osname", "Mac OS 10", "Connection OSName in Whatsapp")
+ platformType = flag.String("platformtype", "DESKTOP", "Device platform type (DESKTOP, IPAD, ANDROID_TABLET, IOS_PHONE, ANDROID_PHONE, etc.)")
colorOutput = flag.Bool("color", false, "Enable colored output for console logs")
sslcert = flag.String("sslcertificate", "", "SSL Certificate File")
sslprivkey = flag.String("sslprivatekey", "", "SSL Certificate Private Key File")
@@ -225,6 +226,11 @@ func main() {
*osName = v
}
+ // Override platformType from environment variable if set
+ if v := os.Getenv("SESSION_PLATFORM_TYPE"); v != "" {
+ *platformType = v
+ }
+
if *versionFlag {
fmt.Printf("WuzAPI version %s\n", version)
os.Exit(0)
diff --git a/wmiau.go b/wmiau.go
index f34012ac..d04136ec 100644
--- a/wmiau.go
+++ b/wmiau.go
@@ -366,6 +366,64 @@ func parseJID(arg string) (types.JID, bool) {
}
}
+// getPlatformTypeEnum converts a platform type string to the corresponding DeviceProps enum
+// Returns DESKTOP as default if the string doesn't match any known type
+func getPlatformTypeEnum(platformType string) *waCompanionReg.DeviceProps_PlatformType {
+ platformType = strings.ToUpper(strings.TrimSpace(platformType))
+
+ switch platformType {
+ case "UNKNOWN":
+ return waCompanionReg.DeviceProps_UNKNOWN.Enum()
+ case "CHROME":
+ return waCompanionReg.DeviceProps_CHROME.Enum()
+ case "FIREFOX":
+ return waCompanionReg.DeviceProps_FIREFOX.Enum()
+ case "IE":
+ return waCompanionReg.DeviceProps_IE.Enum()
+ case "OPERA":
+ return waCompanionReg.DeviceProps_OPERA.Enum()
+ case "SAFARI":
+ return waCompanionReg.DeviceProps_SAFARI.Enum()
+ case "EDGE":
+ return waCompanionReg.DeviceProps_EDGE.Enum()
+ case "DESKTOP":
+ return waCompanionReg.DeviceProps_DESKTOP.Enum()
+ case "IPAD":
+ return waCompanionReg.DeviceProps_IPAD.Enum()
+ case "ANDROID_TABLET":
+ return waCompanionReg.DeviceProps_ANDROID_TABLET.Enum()
+ case "OHANA":
+ return waCompanionReg.DeviceProps_OHANA.Enum()
+ case "ALOHA":
+ return waCompanionReg.DeviceProps_ALOHA.Enum()
+ case "CATALINA":
+ return waCompanionReg.DeviceProps_CATALINA.Enum()
+ case "TCL_TV":
+ return waCompanionReg.DeviceProps_TCL_TV.Enum()
+ case "IOS_PHONE":
+ return waCompanionReg.DeviceProps_IOS_PHONE.Enum()
+ case "IOS_CATALYST":
+ return waCompanionReg.DeviceProps_IOS_CATALYST.Enum()
+ case "ANDROID_PHONE":
+ return waCompanionReg.DeviceProps_ANDROID_PHONE.Enum()
+ case "ANDROID_AMBIGUOUS":
+ return waCompanionReg.DeviceProps_ANDROID_AMBIGUOUS.Enum()
+ case "WEAR_OS":
+ return waCompanionReg.DeviceProps_WEAR_OS.Enum()
+ case "AR_WRIST":
+ return waCompanionReg.DeviceProps_AR_WRIST.Enum()
+ case "AR_DEVICE":
+ return waCompanionReg.DeviceProps_AR_DEVICE.Enum()
+ case "UWP":
+ return waCompanionReg.DeviceProps_UWP.Enum()
+ case "VR":
+ return waCompanionReg.DeviceProps_VR.Enum()
+ default:
+ log.Warn().Str("platformType", platformType).Msg("Unknown platform type, defaulting to DESKTOP")
+ return waCompanionReg.DeviceProps_DESKTOP.Enum()
+ }
+}
+
func (s *server) startClient(userID string, textjid string, token string, subscriptions []string) {
log.Info().Str("userid", userID).Str("jid", textjid).Msg("Starting websocket connection to Whatsapp")
@@ -407,7 +465,7 @@ func (s *server) startClient(userID string, textjid string, token string, subscr
// Now we can use the client with the manager
clientManager.SetWhatsmeowClient(userID, client)
- store.DeviceProps.PlatformType = waCompanionReg.DeviceProps_DESKTOP.Enum()
+ store.DeviceProps.PlatformType = getPlatformTypeEnum(*platformType)
store.DeviceProps.Os = osName
mycli := MyClient{client, 1, userID, token, subscriptions, s.db, s}
|