From 8cc109446952d208d4f38675a08f078077b7ea22 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Mon, 18 Dec 2023 16:38:12 -0600 Subject: [PATCH] Add RichType to IRichMessage --- .../Messaging/IRichContentService.cs | 6 +++ .../Messaging/IRichMessage.cs | 1 + .../Models/RichContent/QuickReplyElement.cs | 19 ++++--- .../Models/RichContent/QuickReplyMessage.cs | 17 ++++--- .../Messaging/Models/RichContent/Recipient.cs | 8 ++- .../Models/RichContent/RichContent.cs | 21 ++++---- .../Models/RichContent/SenderActionMessage.cs | 20 ++++---- .../Template/ButtonTemplateMessage.cs | 51 ++++++++++--------- .../Template/CouponTemplateMessage.cs | 2 + .../Template/GenericTemplateMessage.cs | 10 +++- .../Template/MultiSelectTemplateMessage.cs | 30 ++++++----- .../Template/ProductTemplateMessage.cs | 10 +++- .../Template/TemplateMessageBase.cs | 12 ----- .../Models/RichContent/TextMessage.cs | 3 ++ .../BotSharp.Core/BotSharpCoreExtensions.cs | 6 ++- .../Messaging/RichContentService.cs | 37 ++++++++++++++ .../Hooks/ChatHubConversationHook.cs | 7 ++- .../MessagingModels/AttachmentMessage.cs | 3 ++ .../Services/ConditionalSerialization.cs | 24 +++++++++ .../Services/MessageHandleService.cs | 7 ++- 20 files changed, 190 insertions(+), 104 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Messaging/IRichContentService.cs delete mode 100644 src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/TemplateMessageBase.cs create mode 100644 src/Infrastructure/BotSharp.Core/Messaging/RichContentService.cs create mode 100644 src/Plugins/BotSharp.Plugin.MetaMessenger/Services/ConditionalSerialization.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/IRichContentService.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/IRichContentService.cs new file mode 100644 index 00000000..1986bcb8 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/IRichContentService.cs @@ -0,0 +1,6 @@ +namespace BotSharp.Abstraction.Messaging; + +public interface IRichContentService +{ + List ConvertToMessages(string content); +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/IRichMessage.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/IRichMessage.cs index 0fce3f4c..c81f7e91 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/IRichMessage.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/IRichMessage.cs @@ -3,4 +3,5 @@ namespace BotSharp.Abstraction.Messaging; public interface IRichMessage { string Text { get; set; } + string RichType => "text"; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/QuickReplyElement.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/QuickReplyElement.cs index a831c269..4ad88ab9 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/QuickReplyElement.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/QuickReplyElement.cs @@ -1,15 +1,14 @@ +namespace BotSharp.Abstraction.Messaging.Models.RichContent; -namespace BotSharp.Abstraction.Messaging.Models.RichContent +public class QuickReplyElement { - public class QuickReplyElement - { - [JsonPropertyName("content_type")] - public string ContentType { get; set; } = "text"; + [JsonPropertyName("content_type")] + public string ContentType { get; set; } = "text"; - public string Title { get; set; } = string.Empty; - public string? Payload { get; set; } + public string Title { get; set; } = string.Empty; + public string? Payload { get; set; } - [JsonPropertyName("image_url")] - public string? ImageUrl { get; set; } - } + [JsonPropertyName("image_url")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? ImageUrl { get; set; } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/QuickReplyMessage.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/QuickReplyMessage.cs index b0081c81..0ffd4d44 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/QuickReplyMessage.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/QuickReplyMessage.cs @@ -1,10 +1,11 @@ -namespace BotSharp.Abstraction.Messaging.Models.RichContent -{ - public class QuickReplyMessage : IRichMessage - { - public string Text { get; set; } = string.Empty; +namespace BotSharp.Abstraction.Messaging.Models.RichContent; - [JsonPropertyName("quick_replies")] - public List QuickReplies { get; set; } = new List(); - } +public class QuickReplyMessage : IRichMessage +{ + [JsonPropertyName("rich_type")] + public string RichType => "quick_reply"; + public string Text { get; set; } = string.Empty; + + [JsonPropertyName("quick_replies")] + public List QuickReplies { get; set; } = new List(); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Recipient.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Recipient.cs index 5c77924a..bdf7c8aa 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Recipient.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Recipient.cs @@ -1,8 +1,6 @@ +namespace BotSharp.Abstraction.Messaging.Models.RichContent; -namespace BotSharp.Abstraction.Messaging.Models.RichContent +public class Recipient { - public class Recipient - { - public string Id { get; set; } = string.Empty; - } + public string Id { get; set; } = string.Empty; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/RichContent.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/RichContent.cs index 47c47ab5..61a89af8 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/RichContent.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/RichContent.cs @@ -1,13 +1,12 @@ -namespace BotSharp.Abstraction.Messaging.Models.RichContent +namespace BotSharp.Abstraction.Messaging.Models.RichContent; + +public class RichContent where T : IRichMessage { - public class RichContent where T : IRichMessage - { - public Recipient Recipient { get; set; } = new Recipient(); - /// - /// RESPONSE - /// - [JsonPropertyName("messaging_type")] - public string MessagingType => "RESPONSE"; - public T Message { get; set; } - } + public Recipient Recipient { get; set; } = new Recipient(); + /// + /// RESPONSE + /// + [JsonPropertyName("messaging_type")] + public string MessagingType => "RESPONSE"; + public T Message { get; set; } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/SenderActionMessage.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/SenderActionMessage.cs index 6b652e4e..58f0627e 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/SenderActionMessage.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/SenderActionMessage.cs @@ -1,15 +1,13 @@ +namespace BotSharp.Abstraction.Messaging.Models.RichContent; -namespace BotSharp.Abstraction.Messaging.Models.RichContent +public class SenderActionMessage { - public class SenderActionMessage - { - /* - * Requests to display sender action should only include the sender_action parameter and the recipient object. - * All other Send API properties, such as text and templates, should be sent in a separate request. - */ - public Recipient Recipient { get; set; } = new Recipient(); + /* + * Requests to display sender action should only include the sender_action parameter and the recipient object. + * All other Send API properties, such as text and templates, should be sent in a separate request. + */ + public Recipient Recipient { get; set; } = new Recipient(); - [JsonPropertyName("sender_action")] - public string SenderAction { get; set; } = string.Empty; - } + [JsonPropertyName("sender_action")] + public string SenderAction { get; set; } = string.Empty; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ButtonTemplateMessage.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ButtonTemplateMessage.cs index 4acac72b..8531f185 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ButtonTemplateMessage.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ButtonTemplateMessage.cs @@ -1,30 +1,35 @@ -namespace BotSharp.Abstraction.Messaging.Models.RichContent.Template +namespace BotSharp.Abstraction.Messaging.Models.RichContent.Template; + +/// +/// https://developers.facebook.com/docs/messenger-platform/send-messages/buttons +/// +public class ButtonTemplateMessage : IRichMessage, ITemplateMessage +{ + [JsonPropertyName("rich_type")] + public string RichType => "button_template"; + + [JsonPropertyName("text")] + public string Text { get; set; } = string.Empty; + + [JsonPropertyName("template_type")] + public string TemplateType => "button"; + + [JsonPropertyName("buttons")] + public ButtonElement[] Buttons { get; set; } = new ButtonElement[0]; +} + +public class ButtonElement { /// - /// https://developers.facebook.com/docs/messenger-platform/send-messages/buttons + /// web_url, postback, phone_number /// - public class ButtonTemplateMessage : IRichMessage - { - public string Text { get; set; } = string.Empty; + public string Type { get; set; } = "web_url"; - [JsonPropertyName("template_type")] - public string TemplateType => "button"; - public List Buttons { get; set; } = new List(); - } + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? Url { get; set; } - public class ButtonElement - { - /// - /// web_url, postback, phone_number - /// - public string Type { get; set; } = "web_url"; + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? Payload { get; set; } - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public string? Url { get; set; } - - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public string? Payload { get; set; } - - public string Title { get; set; } = string.Empty; - } + public string Title { get; set; } = string.Empty; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/CouponTemplateMessage.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/CouponTemplateMessage.cs index 8fb74efd..dd5428fb 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/CouponTemplateMessage.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/CouponTemplateMessage.cs @@ -6,6 +6,8 @@ namespace BotSharp.Abstraction.Messaging.Models.RichContent.Template; /// public class CouponTemplateMessage : IRichMessage, ITemplateMessage { + [JsonPropertyName("rich_type")] + public string RichType => "coupon_template"; [JsonIgnore] public string Text { get; set; } public string Title { get; set; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/GenericTemplateMessage.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/GenericTemplateMessage.cs index 6bb78fc3..f2617a13 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/GenericTemplateMessage.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/GenericTemplateMessage.cs @@ -1,9 +1,15 @@ namespace BotSharp.Abstraction.Messaging.Models.RichContent.Template; -public class GenericTemplateMessage : TemplateMessageBase, IRichMessage, ITemplateMessage +public class GenericTemplateMessage : IRichMessage, ITemplateMessage { + [JsonPropertyName("rich_type")] + public string RichType => "generic_template"; + + [JsonIgnore] + public string Text { get; set; } = string.Empty; + [JsonPropertyName("template_type")] - public override string TemplateType => "generic"; + public string TemplateType => "generic"; } public class GenericElement diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/MultiSelectTemplateMessage.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/MultiSelectTemplateMessage.cs index df8e1183..47ce576a 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/MultiSelectTemplateMessage.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/MultiSelectTemplateMessage.cs @@ -1,18 +1,20 @@ -namespace BotSharp.Abstraction.Messaging.Models.RichContent.Template +namespace BotSharp.Abstraction.Messaging.Models.RichContent.Template; + +public class MultiSelectTemplateMessage : IRichMessage, ITemplateMessage { - public class MultiSelectTemplateMessage : IRichMessage - { - public string Text { get; set; } = string.Empty; + [JsonPropertyName("rich_type")] + public string RichType => "multi-select_template"; - [JsonPropertyName("template_type")] - public string TemplateType => "multi-select"; - public List Options { get; set; } = new List(); - } + public string Text { get; set; } = string.Empty; - public class OptionElement - { - public string Title { get; set; } = string.Empty; - public string Type { get; set; } = string.Empty; - public string? Payload { get; set; } - } + [JsonPropertyName("template_type")] + public string TemplateType => "multi-select"; + public List Options { get; set; } = new List(); +} + +public class OptionElement +{ + public string Title { get; set; } = string.Empty; + public string Type { get; set; } = string.Empty; + public string? Payload { get; set; } } \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ProductTemplateMessage.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ProductTemplateMessage.cs index ccdd66a5..879da7c6 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ProductTemplateMessage.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ProductTemplateMessage.cs @@ -1,9 +1,15 @@ namespace BotSharp.Abstraction.Messaging.Models.RichContent.Template; -public class ProductTemplateMessage : TemplateMessageBase, IRichMessage +public class ProductTemplateMessage : IRichMessage, ITemplateMessage { + [JsonPropertyName("rich_type")] + public string RichType => "generic_template"; + + [JsonIgnore] + public string Text { get; set; } = string.Empty; + [JsonPropertyName("template_type")] - public override string TemplateType => "product"; + public string TemplateType => "product"; } public class ProductElement diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/TemplateMessageBase.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/TemplateMessageBase.cs deleted file mode 100644 index b2d568cc..00000000 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/TemplateMessageBase.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace BotSharp.Abstraction.Messaging.Models.RichContent.Template -{ - public class TemplateMessageBase - { - [JsonIgnore] - public string Text { get; set; } - - [JsonPropertyName("template_type")] - public virtual string TemplateType => string.Empty; - public T[] Elements { get; set; } - } -} diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/TextMessage.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/TextMessage.cs index dfb611d3..cf14ca9b 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/TextMessage.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/TextMessage.cs @@ -2,6 +2,9 @@ namespace BotSharp.Abstraction.Messaging.Models.RichContent; public class TextMessage : IRichMessage { + [JsonPropertyName("rich_type")] + public string RichType => "text"; + public string Text { get; set; } = string.Empty; public TextMessage(string text) diff --git a/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs b/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs index bfb8dca1..8437fbb5 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs +++ b/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs @@ -20,7 +20,8 @@ using BotSharp.Abstraction.MLTasks.Settings; using BotSharp.Abstraction.Planning; using BotSharp.Core.Planning; using BotSharp.Abstraction.MLTasks; -using static Dapper.SqlMapper; +using BotSharp.Abstraction.Messaging; +using BotSharp.Core.Messaging; namespace BotSharp.Core; @@ -98,6 +99,9 @@ public static class BotSharpCoreExtensions services.AddScoped(); + // Rich content messaging + services.AddScoped(); + // Evaluation var evalSetting = new EvaluatorSetting(); config.Bind("Evaluator", evalSetting); diff --git a/src/Infrastructure/BotSharp.Core/Messaging/RichContentService.cs b/src/Infrastructure/BotSharp.Core/Messaging/RichContentService.cs new file mode 100644 index 00000000..6bfb60ad --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/Messaging/RichContentService.cs @@ -0,0 +1,37 @@ +using BotSharp.Abstraction.Messaging; +using BotSharp.Abstraction.Messaging.Models.RichContent; + +namespace BotSharp.Core.Messaging; + +public class RichContentService : IRichContentService +{ + public List ConvertToMessages(string content) + { + var messages = new List(); + var options = new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true + }; + var tempMessages = JsonSerializer.Deserialize(content, options); + + foreach (var m in tempMessages) + { + var richType = "text"; + if (m.RootElement.TryGetProperty("rich_type", out var element)) + { + richType = element.GetString(); + } + + if (richType == "text") + { + messages.Add(JsonSerializer.Deserialize(m.RootElement.ToString(), options)); + } + else if (richType == "quick_reply") + { + messages.Add(JsonSerializer.Deserialize(m.RootElement.ToString(), options)); + } + } + + return messages.ToList(); + } +} diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubConversationHook.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubConversationHook.cs index 939bc20d..766ca858 100644 --- a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubConversationHook.cs +++ b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubConversationHook.cs @@ -1,3 +1,4 @@ +using BotSharp.Abstraction.Messaging; using BotSharp.Abstraction.Messaging.Models.RichContent; using Microsoft.AspNetCore.SignalR; @@ -27,10 +28,8 @@ public class ChatHubConversationHook : ConversationHookBase var welcomeTemplate = agent.Templates.FirstOrDefault(x => x.Name == "welcome"); if (welcomeTemplate != null) { - var messages = JsonSerializer.Deserialize(welcomeTemplate.Content, new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true - }); + var richContentService = _services.GetRequiredService(); + var messages = richContentService.ConvertToMessages(welcomeTemplate.Content); foreach (var message in messages) { diff --git a/src/Plugins/BotSharp.Plugin.MetaMessenger/MessagingModels/AttachmentMessage.cs b/src/Plugins/BotSharp.Plugin.MetaMessenger/MessagingModels/AttachmentMessage.cs index 08aaad34..e70e172a 100644 --- a/src/Plugins/BotSharp.Plugin.MetaMessenger/MessagingModels/AttachmentMessage.cs +++ b/src/Plugins/BotSharp.Plugin.MetaMessenger/MessagingModels/AttachmentMessage.cs @@ -5,6 +5,9 @@ namespace BotSharp.Plugin.MetaMessenger.MessagingModels; /// public class AttachmentMessage : IRichMessage { + [JsonPropertyName("rich_type")] + public string RichType => "attachment"; + [JsonIgnore] public string Text { get; set; } diff --git a/src/Plugins/BotSharp.Plugin.MetaMessenger/Services/ConditionalSerialization.cs b/src/Plugins/BotSharp.Plugin.MetaMessenger/Services/ConditionalSerialization.cs new file mode 100644 index 00000000..0487a960 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.MetaMessenger/Services/ConditionalSerialization.cs @@ -0,0 +1,24 @@ +using System.Text.Json.Serialization.Metadata; + +namespace BotSharp.Plugin.MetaMessenger.Services; + +public static class ConditionalSerialization +{ + /// + /// https://devblogs.microsoft.com/dotnet/system-text-json-in-dotnet-7/#example-conditional-serialization + /// + /// + public static void IgnoreRichType(JsonTypeInfo typeInfo) + { + if (typeInfo.Type.GetInterface(nameof(IRichMessage)) == null) + return; + + foreach (JsonPropertyInfo propertyInfo in typeInfo.Properties) + { + if (propertyInfo.Name == "rich_type") + { + propertyInfo.ShouldSerialize = static (obj, value) => false; + } + } + } +} diff --git a/src/Plugins/BotSharp.Plugin.MetaMessenger/Services/MessageHandleService.cs b/src/Plugins/BotSharp.Plugin.MetaMessenger/Services/MessageHandleService.cs index 74c1c8f3..bf83ea70 100644 --- a/src/Plugins/BotSharp.Plugin.MetaMessenger/Services/MessageHandleService.cs +++ b/src/Plugins/BotSharp.Plugin.MetaMessenger/Services/MessageHandleService.cs @@ -2,6 +2,7 @@ using BotSharp.Abstraction.Agents.Enums; using BotSharp.Abstraction.Conversations.Enums; using BotSharp.Abstraction.Messaging.Models.RichContent; using BotSharp.Abstraction.Utilities; +using System.Text.Json.Serialization.Metadata; namespace BotSharp.Plugin.MetaMessenger.Services; @@ -19,7 +20,11 @@ public class MessageHandleService Converters = { new RichContentJsonConverter(), - new TemplateMessageJsonConverter() + new TemplateMessageJsonConverter(), + }, + TypeInfoResolver = new DefaultJsonTypeInfoResolver + { + Modifiers = { ConditionalSerialization.IgnoreRichType } } }; }