diff --git a/src/SampleApp/Sample/ProcessHandler.cs b/src/SampleApp/Sample/ProcessHandler.cs new file mode 100644 index 0000000..43cac76 --- /dev/null +++ b/src/SampleApp/Sample/ProcessHandler.cs @@ -0,0 +1,59 @@ +using System.Runtime.CompilerServices; +using System.Text.Json; +using Devlooped.WhatsApp; +using Microsoft.Extensions.Logging; + +class ProcessHandler(ILogger logger, JsonSerializerOptions options) : IWhatsAppHandler +{ + public async IAsyncEnumerable HandleAsync(IEnumerable messages, [EnumeratorCancellation] CancellationToken cancellation = default) + { + // Avoid warning CS1998 // Async method lacks 'await' operators and will run synchronously + await Task.CompletedTask; + + var message = messages.Last(); + logger.LogInformation("💬 Received message: {Message}", message); + + if (message is ErrorMessage error) + { + // Reengagement error, we need to invite the user. + if (error.Error.Code == 131047) + { + // Showcases how to use a pre-declared template response to reengage the user. + yield return error.Template("reengagement", "es_AR"); + } + else + { + logger.LogWarning("⚠️ Unknown error message received: {Error}", message); + } + } + else if (message is InteractiveMessage interactive) + { + logger.LogWarning("👤 chose {Button} ({Title})", interactive.Button.Id, interactive.Button.Title); + yield return interactive.Reply($"👤 chose: {interactive.Button.Title} ({interactive.Button.Id})"); + } + else if (message is ReactionMessage reaction) + { + logger.LogInformation("👤 reaction: {Reaction}", reaction.Emoji); + yield return reaction.Reply($"👤 reaction: {reaction.Emoji}"); + } + else if (message is StatusMessage status) + { + logger.LogInformation("☑️ status: {Status}", status.Status); + } + else if (message is ContentMessage content) + { + yield return content.React("🧠"); + + // simulate some hard work at hand, like doing some LLM-stuff :) + //await Task.Delay(2000); + yield return content.Reply( + $"☑️ Got your {content.Content.Type}:\r\n{JsonSerializer.Serialize(content, options)}", + new Button("btn_good", "👍"), + new Button("btn_bad", "👎")); + } + else if (message is UnsupportedMessage unsupported) + { + logger.LogWarning("⚠️ {Message}", unsupported); + } + } +} \ No newline at end of file diff --git a/src/SampleApp/Sample/Program.cs b/src/SampleApp/Sample/Program.cs index 13289fb..fbc19d9 100644 --- a/src/SampleApp/Sample/Program.cs +++ b/src/SampleApp/Sample/Program.cs @@ -1,5 +1,4 @@ -using System.Runtime.CompilerServices; -using System.Text.Json; +using System.Text.Json; using System.Text.Json.Serialization; using Devlooped; using Devlooped.WhatsApp; @@ -42,67 +41,11 @@ throw new InvalidOperationException("Missing required App:Storage connection string.")); builder.Services - .AddWhatsApp, JsonSerializerOptions>(builder.Configuration, ProcessMessagesAsync) + .AddWhatsApp(builder.Configuration) // Matches what we use in ConfigureOpenTelemetry .UseOpenTelemetry(builder.Environment.ApplicationName) .UseLogging() .UseStorage() .UseConversation(); -builder.Build().Run(); - -static async IAsyncEnumerable ProcessMessagesAsync( - ILogger logger, - JsonSerializerOptions options, - IEnumerable messages, - [EnumeratorCancellation] CancellationToken cancellationToken) -{ - // Avoid warning CS1998 // Async method lacks 'await' operators and will run synchronously - await Task.CompletedTask; - - var message = messages.Last(); - logger.LogInformation("💬 Received message: {Message}", message); - - if (message is ErrorMessage error) - { - // Reengagement error, we need to invite the user. - if (error.Error.Code == 131047) - { - // Showcases how to use a pre-declared template response to reengage the user. - yield return error.Template("reengagement", "es_AR"); - } - else - { - logger.LogWarning("⚠️ Unknown error message received: {Error}", message); - } - } - else if (message is InteractiveMessage interactive) - { - logger.LogWarning("👤 chose {Button} ({Title})", interactive.Button.Id, interactive.Button.Title); - yield return interactive.Reply($"👤 chose: {interactive.Button.Title} ({interactive.Button.Id})"); - } - else if (message is ReactionMessage reaction) - { - logger.LogInformation("👤 reaction: {Reaction}", reaction.Emoji); - yield return reaction.Reply($"👤 reaction: {reaction.Emoji}"); - } - else if (message is StatusMessage status) - { - logger.LogInformation("☑️ status: {Status}", status.Status); - } - else if (message is ContentMessage content) - { - yield return content.React("🧠"); - - // simulate some hard work at hand, like doing some LLM-stuff :) - //await Task.Delay(2000); - yield return content.Reply( - $"☑️ Got your {content.Content.Type}:\r\n{JsonSerializer.Serialize(content, options)}", - new Button("btn_good", "👍"), - new Button("btn_bad", "👎")); - } - else if (message is UnsupportedMessage unsupported) - { - logger.LogWarning("⚠️ {Message}", unsupported); - } -} +builder.Build().Run(); \ No newline at end of file diff --git a/src/WhatsApp/WhatsAppServiceCollectionExtensions.cs b/src/WhatsApp/WhatsAppServiceCollectionExtensions.cs index d254bd6..244ff70 100644 --- a/src/WhatsApp/WhatsAppServiceCollectionExtensions.cs +++ b/src/WhatsApp/WhatsAppServiceCollectionExtensions.cs @@ -126,9 +126,12 @@ public static WhatsAppHandlerBuilder AddWhatsApp( ServiceLifetime lifetime = ServiceLifetime.Singleton) where THandler : class, IWhatsAppHandler { - collection.Add(new ServiceDescriptor(typeof(IWhatsAppHandler), services => services.GetRequiredService(), lifetime)); + if (collection.FirstOrDefault(x => x.ServiceType == typeof(THandler)) == null) + { + collection.Add(new ServiceDescriptor(typeof(THandler), typeof(THandler), lifetime)); + } - return collection.AddWhatsApp(configuration, services => services.GetRequiredService(), lifetime); + return collection.AddWhatsApp(configuration, services => services.GetRequiredService(), lifetime); } ///