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
9 changes: 5 additions & 4 deletions src/SampleApp/Sample/Sample.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@
<!-- <PackageReference Include="Microsoft.ApplicationInsights.WorkerService" Version="2.22.0" /> -->
<!-- <PackageReference Include="Microsoft.Azure.Functions.Worker.ApplicationInsights" Version="2.0.0" /> -->
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="2.0.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore" Version="2.0.1" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="2.0.2" />
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="9.5.0" />
<PackageReference Include="Microsoft.Extensions.ServiceDiscovery" Version="9.3.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore" Version="2.0.2" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="2.0.5" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.6" />
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="9.6.0" />
<PackageReference Include="Microsoft.Extensions.ServiceDiscovery" Version="9.3.1" />
<PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.12.0" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.12.0" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.12.0" />
Expand Down
7 changes: 4 additions & 3 deletions src/WhatsApp/AzureFunctionsWebhook.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@ public async Task<IActionResult> Message([HttpTrigger(AuthorizationLevel.Anonymo

if (await WhatsApp.Message.DeserializeAsync(json) is { } message)
{
if (functionOptions.ReadOnMessage is true && message.Type == MessageType.Content)
// Ignored since this can be an old, deleted message, for example
await whatsapp.MarkReadAsync(message.Service.Id, message.Id).Ignore();
// If we got a user message, we can send progress updates as configured. We ignore exceptions in the
// operation since it can be a notification for an old message or it may have been deleted by the user.
if (message is UserMessage user)
await user.SendProgress(whatsapp, functionOptions.ReadOnMessage is true, functionOptions.TypingOnMessage is true).Ignore();

// Ensure idempotent processing
var table = tableClient.GetTableClient("WhatsAppWebhook");
Expand Down
10 changes: 6 additions & 4 deletions src/WhatsApp/PipelineRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

namespace Devlooped.WhatsApp;

class PipelineRunner(TableServiceClient tableClient,
class PipelineRunner(
TableServiceClient tableClient,
IWhatsAppClient whatsapp,
IWhatsAppHandler handler,
IOptions<WhatsAppOptions> functionOptions,
Expand All @@ -18,9 +19,10 @@ public async Task ProcessAsync(string json)

if (await Message.DeserializeAsync(json) is { } message)
{
if (functionOptions.ReadOnProcess is true && message.Type == MessageType.Content)
// Ignored since this can be an old, deleted message, for example
await whatsapp.MarkReadAsync(message.Service.Id, message.Id).Ignore();
// If we got a user message, we can send progress updates as configured. We ignore exceptions in the
// operation since it can be a notification for an old message or it may have been deleted by the user.
if (message is UserMessage user)
await user.SendProgress(whatsapp, functionOptions.ReadOnProcess is true, functionOptions.TypingOnProcess is true).Ignore();

// Ensure idempotent processing at dequeue time, since we might have been called
// multiple times for the same message by WhatsApp (Message method) while processing was still
Expand Down
41 changes: 41 additions & 0 deletions src/WhatsApp/UserMessageExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
namespace Devlooped.WhatsApp;

static class UserMessageExtensions
{
public static async Task SendProgress(this UserMessage message, IWhatsAppClient client, bool markRead, bool sendTyping)
{
// These actions are ignored for exceptions since they may be triggered for an old, deleted message, for example.
if (sendTyping is true)
{
if (markRead)
{
await client.SendAsync(message.Service.Id, new
{
messaging_product = "whatsapp",
status = "read",
message_id = message.Id,
typing_indicator = new
{
type = "text"
}
}).Ignore();
}
else
{
await client.SendAsync(message.Service.Id, new
{
messaging_product = "whatsapp",
message_id = message.Id,
typing_indicator = new
{
type = "text"
}
}).Ignore();
}
}
else
{
await client.MarkReadAsync(message.Service.Id, message.Id).Ignore();
}
}
}
16 changes: 14 additions & 2 deletions src/WhatsApp/WhatsAppOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,27 @@
public class WhatsAppOptions
{
/// <summary>
/// Mark messages as read when received in the WhatsApp webhook endpoint.
/// Mark messages as read when received in the WhatsApp webhook endpoint.
/// Defaults to <c>true</c>.
/// </summary>
public bool? ReadOnMessage { get; set; }
public bool? ReadOnMessage { get; set; } = true;

/// <summary>
/// Send a typing indicator status when message is received in the
/// WhatsApp webhook endpoint.
/// </summary>
public bool? TypingOnMessage { get; set; } = true;

/// <summary>
/// Mark messages as read when processing is started.
/// </summary>
public bool? ReadOnProcess { get; set; }

/// <summary>
/// Send a typing indicator status when message processing begins.
/// </summary>
public bool? TypingOnProcess { get; set; }

/// <summary>
/// An optional emoji to react with when a message is received
/// in the WhatsApp webhook endpoint.
Expand Down