Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 101 additions & 0 deletions src/Tests/Content/WhatsApp/Contacts.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
{
"object": "whatsapp_business_account",
"entry": [
{
"id": "123456789012345",
"changes": [
{
"value": {
"messaging_product": "whatsapp",
"metadata": {
"display_phone_number": "1234567890",
"phone_number_id": "987654321098765"
},
"contacts": [
{
"profile": { "name": "User1" },
"wa_id": "1111111111111"
}
],
"messages": [
{
"from": "1111111111111",
"id": "wamid.ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890",
"timestamp": "1234567890",
"type": "contacts",
"contacts": [
{
"emails": [
{
"email": "user1@example.com",
"type": "Other"
}
],
"name": {
"first_name": "First1",
"formatted_name": "First1 Last1"
},
"phones": [
{
"phone": "+1 123-456-7890",
"wa_id": "1111111111111",
"type": "MOBILE"
}
]
},
{
"emails": [
{
"email": "user2@example.com",
"type": "Other"
},
{
"email": "user2@example.com",
"type": "Other"
}
],
"name": {
"first_name": "First2",
"formatted_name": "First2 Last2"
},
"phones": [
{
"phone": "+1 987-654-3210",
"wa_id": "2222222222222",
"type": "MOBILE"
}
]
},
{
"emails": [
{
"email": "user3@example.com",
"type": "HOME"
},
{
"email": "user3@example.com",
"type": "HOME"
}
],
"name": {
"first_name": "First3",
"formatted_name": "First3 Last3"
},
"phones": [
{
"phone": "+1 555-555-5555",
"wa_id": "3333333333333",
"type": "WhatsApp"
}
]
}
]
}
]
},
"field": "messages"
}
]
}
]
}
32 changes: 28 additions & 4 deletions src/Tests/WhatsAppModelTests.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
using Xunit.Abstractions;

namespace Devlooped.WhatsApp;
namespace Devlooped.WhatsApp;

public class WhatsAppModelTests(ITestOutputHelper output)
{
[Theory]
[InlineData(nameof(ContentType.Audio), "927483105672819", "wamid.XYZRandomString123ABC456DEF789GHI==")]
[InlineData(nameof(ContentType.Contact), "927481035162874", "wamid.HBgNNDcyODkwMTIzNDU2NhUCABIYFjE4QTlDMzU2MkJDOTg3RUY2NDg5RTFEMTIzQzVFRAA==")]
[InlineData(nameof(ContentType.Contacts), "123456789012345", "wamid.ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")]
[InlineData(nameof(ContentType.Document), "813947205126374", "wamid.HBgNMTIwMjU1NTk4NzY1NhUCABIYFjE4QTlDMzU2MkJDOTg3RUY2NDg5RTFEMTIzQzVFRAA==")]
[InlineData(nameof(ContentType.Image), "813927405162784", "wamid.HBgNMTIwMjU1NTk4NzY1NhUCABIYFjE4QTlDMzU2MkJDOTg3RUY2NDg5RTFEMTIzQzVFRAA==")]
[InlineData(nameof(ContentType.Location), "813920475601234", "wamid.HBgNMTIwMjk4NzQ1NjM1NhUCABIYFjE5RDhGMzQ2NEJDOTg3RUY2NDg5RTFEMTIzQzVFRAA==")]
Expand Down Expand Up @@ -41,7 +40,7 @@ public async Task DeserializeMessage(string type, string notification, string id

[Theory]
[InlineData(ContentType.Audio)]
[InlineData(ContentType.Contact)]
[InlineData(ContentType.Contacts)]
[InlineData(ContentType.Document)]
[InlineData(ContentType.Image)]
[InlineData(ContentType.Location)]
Expand All @@ -62,6 +61,31 @@ public async Task DeserializeContent(ContentType type)
Assert.Equal(type, content.Content.Type);
}

[Fact]
public async Task DeserializeContacts()
{
var json = await File.ReadAllTextAsync($"Content/WhatsApp/Contacts.json");
var message = await Message.DeserializeAsync(json);

var content = Assert.IsType<ContentMessage>(message);

Assert.Equal(ContentType.Contacts, content.Content.Type);
var contacts = Assert.IsType<ContactsContent>(content.Content);
Assert.Equal(3, contacts.Contacts.Length);

// Assert for first contact
Assert.Equal("First1", contacts.Contacts[0].Name);
Assert.Equal("1111111111111", contacts.Contacts[0].Numbers[0]);

// Assert for second contact
Assert.Equal("First2", contacts.Contacts[1].Name);
Assert.Equal("2222222222222", contacts.Contacts[1].Numbers[0]);

// Assert for third contact
Assert.Equal("First3", contacts.Contacts[2].Name);
Assert.Equal("3333333333333", contacts.Contacts[2].Numbers[0]);
}

[Fact]
public async Task DeserializeErrorStatus()
{
Expand Down
29 changes: 21 additions & 8 deletions src/WhatsApp/Content.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Text.Json;
using System.ComponentModel;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.AI;

Expand All @@ -9,7 +10,7 @@ namespace Devlooped.WhatsApp;
/// </summary>
[JsonPolymorphic]
[JsonDerivedType(typeof(DocumentContent), "document")]
[JsonDerivedType(typeof(ContactContent), "contacts")]
[JsonDerivedType(typeof(ContactsContent), "contacts")]
[JsonDerivedType(typeof(TextContent), "text")]
[JsonDerivedType(typeof(LocationContent), "location")]
[JsonDerivedType(typeof(ImageContent), "image")]
Expand All @@ -29,18 +30,30 @@ public abstract record Content
public abstract ContentType Type { get; }
}

[Obsolete("Use ContactsContent instead.", true)]
[EditorBrowsable(EditorBrowsableState.Never)]
public record ContactContent(string Name, string Surname, string[] Numbers) : ContactsContent([new(Name, Surname, Numbers)])
{
/// <inheritdoc/>
[JsonIgnore]
public override ContentType Type => ContentType.Contact;
}

/// <summary>
/// Content contains contact information.
/// Content contains contact(s) information.
/// </summary>
/// <param name="Name">Name of the contact.</param>
/// <param name="Surname">Surname of the contact.</param>
/// <param name="Numbers">Phone numbers of the contact.</param>
public record ContactContent(string Name, string Surname, string[] Numbers) : Content
public record ContactsContent(Contact[] Contacts) : Content
{
/// <inheritdoc/>
[JsonIgnore]
public override ContentType Type => ContentType.Contact;
public override ContentType Type => ContentType.Contacts;
}

/// <summary>
/// Contact information.
/// </summary>
public record Contact(string Name, string Surname, string[] Numbers)
{
public override string ToString() => $"{Name} {Surname} ({string.Join(", ", Numbers)})";
}

Expand Down
8 changes: 6 additions & 2 deletions src/WhatsApp/ContentType.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
namespace Devlooped.WhatsApp;
using System.ComponentModel;

namespace Devlooped.WhatsApp;

/// <summary>
/// Defines the type of content.
/// </summary>
public enum ContentType
{
Audio,
Contact,
[EditorBrowsable(EditorBrowsableState.Never)]
Contact, // Legacy single-contact type
Contacts,
Document,
Image,
Location,
Expand Down
9 changes: 5 additions & 4 deletions src/WhatsApp/Message.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,11 @@ .value.messages[0] as $msg |
elif $msgType == "contacts" then
{
"$type": "contacts",
"name": $msg.contacts[0].name.first_name,
"surname": $msg.contacts[0].name.last_name,
"numbers": [$msg.contacts[0].phones[] | select(.wa_id? != null) | .wa_id]
}
"contacts": $msg.contacts | map({
"name": .name.first_name,
"surname": .name.last_name,
"numbers": [.phones[] | select(.wa_id? != null) | .wa_id]
}) }
elif $msgType == "text" then
{
"$type": "text",
Expand Down