package whatsapp import ( "context" "testing" "time" "github.com/aldinokemal/go-whatsapp-web-multidevice/config" "go.mau.fi/whatsmeow/proto/waCommon" "go.mau.fi/whatsmeow/proto/waE2E" "go.mau.fi/whatsmeow/types" "go.mau.fi/whatsmeow/types/events" ) func TestBuildEventPayloadIncludesIsFromMe(t *testing.T) { evt := &events.Message{ Info: types.MessageInfo{ MessageSource: types.MessageSource{ Chat: types.NewJID("123", types.DefaultUserServer), Sender: types.NewJID("123", types.DefaultUserServer), IsFromMe: true, }, ID: "MSG123", Timestamp: time.Date(2026, time.February, 8, 10, 0, 0, 0, time.UTC), }, Message: &waE2E.Message{ Conversation: protoString("hello"), }, } eventType, payload, err := buildEventPayload(context.Background(), nil, evt) if err != nil { t.Fatalf("expected no error, got %v", err) } if eventType != EventTypeMessage { t.Fatalf("expected event type %s, got %s", EventTypeMessage, eventType) } if value, ok := payload["is_from_me"]; !ok { t.Fatalf("expected is_from_me in payload") } else if isFromMe, ok := value.(bool); !ok || !isFromMe { t.Fatalf("expected is_from_me=true, got %v", value) } } func TestBuildEventPayloadRevokedIncludesIsFromMe(t *testing.T) { key := &waCommon.MessageKey{ RemoteJID: protoString("123@s.whatsapp.net"), FromMe: protoBool(true), ID: protoString("REV123"), } evt := &events.Message{ Info: types.MessageInfo{ MessageSource: types.MessageSource{ Chat: types.NewJID("123", types.DefaultUserServer), Sender: types.NewJID("123", types.DefaultUserServer), IsFromMe: true, }, ID: "MSG124", Timestamp: time.Date(2026, time.February, 8, 10, 0, 0, 0, time.UTC), }, Message: &waE2E.Message{ ProtocolMessage: &waE2E.ProtocolMessage{ Type: protoProtocolMessageType(waE2E.ProtocolMessage_REVOKE), Key: key, }, }, } eventType, payload, err := buildEventPayload(context.Background(), nil, evt) if err != nil { t.Fatalf("expected no error, got %v", err) } if eventType != EventTypeMessageRevoked { t.Fatalf("expected event type %s, got %s", EventTypeMessageRevoked, eventType) } if value, ok := payload["is_from_me"]; !ok { t.Fatalf("expected is_from_me in payload") } else if isFromMe, ok := value.(bool); !ok || !isFromMe { t.Fatalf("expected is_from_me=true, got %v", value) } } func TestBuildEventPayloadReactionIncludesTargetMessageID(t *testing.T) { evt := reactionEventForTest("reaction-event-1", "MSG100", "\U0001f44d") eventType, payload, err := buildEventPayload(context.Background(), nil, evt) if err != nil { t.Fatalf("expected no error, got %v", err) } if eventType != EventTypeMessageReaction { t.Fatalf("expected event type %s, got %s", EventTypeMessageReaction, eventType) } if got := payload["reaction"]; got != "\U0001f44d" { t.Fatalf("expected reaction payload, got %v", got) } if got := payload["reacted_message_id"]; got != "MSG100" { t.Fatalf("expected reacted message id MSG100, got %v", got) } } func TestBuildEventPayloadReactionWithoutKeyDoesNotPanic(t *testing.T) { evt := reactionEventForTest("reaction-event-2", "MSG101", "\U0001f44d") evt.Message.ReactionMessage.Key = nil eventType, payload, err := buildEventPayload(context.Background(), nil, evt) if err != nil { t.Fatalf("expected no error, got %v", err) } if eventType != EventTypeMessageReaction { t.Fatalf("expected event type %s, got %s", EventTypeMessageReaction, eventType) } if _, ok := payload["reacted_message_id"]; ok { t.Fatalf("expected no reacted_message_id when reaction key is missing") } } func protoString(value string) *string { return &value } func protoBool(value bool) *bool { return &value } func protoProtocolMessageType(value waE2E.ProtocolMessage_Type) *waE2E.ProtocolMessage_Type { return &value } func TestBuildEventPayloadImageWithCaption(t *testing.T) { config.WhatsappAutoDownloadMedia = false caption := "Check this out!" evt := &events.Message{ Info: types.MessageInfo{ MessageSource: types.MessageSource{ Chat: types.NewJID("123", types.DefaultUserServer), Sender: types.NewJID("456", types.DefaultUserServer), IsFromMe: false, }, ID: "MSG200", Timestamp: time.Date(2026, time.February, 8, 10, 0, 0, 0, time.UTC), }, Message: &waE2E.Message{ ImageMessage: &waE2E.ImageMessage{ Caption: &caption, }, }, } eventType, payload, err := buildEventPayload(context.Background(), nil, evt) if err != nil { t.Fatalf("expected no error, got %v", err) } if eventType != EventTypeMessage { t.Fatalf("expected event type %s, got %s", EventTypeMessage, eventType) } body, ok := payload["body"] if !ok { t.Fatal("expected body in payload for image with caption") } if body != "Check this out!" { t.Fatalf("expected body='Check this out!', got %v", body) } } func TestBuildEventPayloadVideoWithCaption(t *testing.T) { config.WhatsappAutoDownloadMedia = false caption := "Watch this video" evt := &events.Message{ Info: types.MessageInfo{ MessageSource: types.MessageSource{ Chat: types.NewJID("123", types.DefaultUserServer), Sender: types.NewJID("456", types.DefaultUserServer), IsFromMe: false, }, ID: "MSG201", Timestamp: time.Date(2026, time.February, 8, 10, 0, 0, 0, time.UTC), }, Message: &waE2E.Message{ VideoMessage: &waE2E.VideoMessage{ Caption: &caption, }, }, } eventType, payload, err := buildEventPayload(context.Background(), nil, evt) if err != nil { t.Fatalf("expected no error, got %v", err) } if eventType != EventTypeMessage { t.Fatalf("expected event type %s, got %s", EventTypeMessage, eventType) } body, ok := payload["body"] if !ok { t.Fatal("expected body in payload for video with caption") } if body != "Watch this video" { t.Fatalf("expected body='Watch this video', got %v", body) } } func TestBuildEventPayloadImageWithoutCaption(t *testing.T) { config.WhatsappAutoDownloadMedia = false evt := &events.Message{ Info: types.MessageInfo{ MessageSource: types.MessageSource{ Chat: types.NewJID("123", types.DefaultUserServer), Sender: types.NewJID("456", types.DefaultUserServer), IsFromMe: false, }, ID: "MSG202", Timestamp: time.Date(2026, time.February, 8, 10, 0, 0, 0, time.UTC), }, Message: &waE2E.Message{ ImageMessage: &waE2E.ImageMessage{}, }, } _, payload, err := buildEventPayload(context.Background(), nil, evt) if err != nil { t.Fatalf("expected no error, got %v", err) } if _, ok := payload["body"]; ok { t.Fatal("expected no body in payload for image without caption") } } func TestBuildEventPayloadDocumentWithCaption(t *testing.T) { config.WhatsappAutoDownloadMedia = false caption := "Important document" evt := &events.Message{ Info: types.MessageInfo{ MessageSource: types.MessageSource{ Chat: types.NewJID("123", types.DefaultUserServer), Sender: types.NewJID("456", types.DefaultUserServer), IsFromMe: false, }, ID: "MSG203", Timestamp: time.Date(2026, time.February, 8, 10, 0, 0, 0, time.UTC), }, Message: &waE2E.Message{ DocumentMessage: &waE2E.DocumentMessage{ Caption: &caption, }, }, } eventType, payload, err := buildEventPayload(context.Background(), nil, evt) if err != nil { t.Fatalf("expected no error, got %v", err) } if eventType != EventTypeMessage { t.Fatalf("expected event type %s, got %s", EventTypeMessage, eventType) } body, ok := payload["body"] if !ok { t.Fatal("expected body in payload for document with caption") } if body != "Important document" { t.Fatalf("expected body='Important document', got %v", body) } } func TestBuildEventPayloadContactIncludesPhoneNumber(t *testing.T) { name := "Alice" vcard := "BEGIN:VCARD\nVERSION:3.0\nN:;Alice;;;\nFN:Alice\nTEL;type=Mobile:+62 812 3456 7890\nEND:VCARD" evt := &events.Message{ Info: types.MessageInfo{ MessageSource: types.MessageSource{ Chat: types.NewJID("123", types.DefaultUserServer), Sender: types.NewJID("456", types.DefaultUserServer), IsFromMe: false, }, ID: "MSG204", Timestamp: time.Date(2026, time.February, 8, 10, 0, 0, 0, time.UTC), }, Message: &waE2E.Message{ ContactMessage: &waE2E.ContactMessage{ DisplayName: &name, Vcard: &vcard, }, }, } _, payload, err := buildEventPayload(context.Background(), nil, evt) if err != nil { t.Fatalf("expected no error, got %v", err) } contact, ok := payload["contact"].(webhookContactPayload) if !ok { t.Fatalf("expected contact payload to be webhookContactPayload, got %T", payload["contact"]) } if contact.DisplayName != "Alice" { t.Fatalf("expected display name Alice, got %q", contact.DisplayName) } if contact.PhoneNumber != "+62 812 3456 7890" { t.Fatalf("expected phone number from vCard, got %q", contact.PhoneNumber) } } func TestBuildEventPayloadContactsArrayIncludesPhoneNumbers(t *testing.T) { nameOne := "Alice" vcardOne := "BEGIN:VCARD\nVERSION:3.0\nN:;Alice;;;\nFN:Alice\nTEL;type=Mobile:+62 812 3456 7890\nEND:VCARD" nameTwo := "Bob" vcardTwo := "BEGIN:VCARD\nVERSION:3.0\nN:;Bob;;;\nFN:Bob\nTEL;type=Mobile:+62 813 9876 5432\nEND:VCARD" evt := &events.Message{ Info: types.MessageInfo{ MessageSource: types.MessageSource{ Chat: types.NewJID("123", types.DefaultUserServer), Sender: types.NewJID("456", types.DefaultUserServer), IsFromMe: false, }, ID: "MSG205", Timestamp: time.Date(2026, time.February, 8, 10, 0, 0, 0, time.UTC), }, Message: &waE2E.Message{ ContactsArrayMessage: &waE2E.ContactsArrayMessage{ Contacts: []*waE2E.ContactMessage{ { DisplayName: &nameOne, Vcard: &vcardOne, }, { DisplayName: &nameTwo, Vcard: &vcardTwo, }, }, }, }, } _, payload, err := buildEventPayload(context.Background(), nil, evt) if err != nil { t.Fatalf("expected no error, got %v", err) } contacts, ok := payload["contacts_array"].([]webhookContactPayload) if !ok { t.Fatalf("expected contacts_array to be []webhookContactPayload, got %T", payload["contacts_array"]) } if len(contacts) != 2 { t.Fatalf("expected 2 contacts, got %d", len(contacts)) } if contacts[0].PhoneNumber != "+62 812 3456 7890" { t.Fatalf("expected first phone number, got %q", contacts[0].PhoneNumber) } if contacts[1].PhoneNumber != "+62 813 9876 5432" { t.Fatalf("expected second phone number, got %q", contacts[1].PhoneNumber) } }