diff --git a/src/Infrastructure/BotSharp.Abstraction/BotSharp.Abstraction.csproj b/src/Infrastructure/BotSharp.Abstraction/BotSharp.Abstraction.csproj index bbade11e..65424adb 100644 --- a/src/Infrastructure/BotSharp.Abstraction/BotSharp.Abstraction.csproj +++ b/src/Infrastructure/BotSharp.Abstraction/BotSharp.Abstraction.csproj @@ -22,6 +22,7 @@ + diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/StateDataType.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/StateDataType.cs new file mode 100644 index 00000000..20d635f4 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/StateDataType.cs @@ -0,0 +1,10 @@ +namespace BotSharp.Abstraction.Conversations.Enums; + +public class StateDataType +{ + public const string String = "string"; + public const string Boolean = "boolean"; + public const string Number = "number"; + public const string Currency = "currency"; + public const string Date = "date"; +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/StateSource.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/StateSource.cs new file mode 100644 index 00000000..31f32e16 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/StateSource.cs @@ -0,0 +1,8 @@ +namespace BotSharp.Abstraction.Conversations.Enums; + +public class StateSource +{ + public const string External = "external"; + public const string Application = "application"; + public const string User = "user"; +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStateService.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStateService.cs index 07eab6c1..d71111fd 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStateService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStateService.cs @@ -1,3 +1,4 @@ +using BotSharp.Abstraction.Conversations.Enums; using System.Text.Json; namespace BotSharp.Abstraction.Conversations; @@ -12,7 +13,8 @@ public interface IConversationStateService string GetState(string name, string defaultValue = ""); bool ContainsState(string name); Dictionary GetStates(); - IConversationStateService SetState(string name, T value, bool isNeedVersion = true, int activeRounds = -1); + IConversationStateService SetState(string name, T value, bool isNeedVersion = true, + int activeRounds = -1, string valueType = StateDataType.String, string source = StateSource.User); void SaveStateByArgs(JsonDocument args); void CleanStates(); void Save(); diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/StateKeyValue.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/StateKeyValue.cs index 53618242..c70dbf03 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/StateKeyValue.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/StateKeyValue.cs @@ -1,3 +1,5 @@ +using BotSharp.Abstraction.Conversations.Enums; + namespace BotSharp.Abstraction.Conversations.Models; public class StateKeyValue @@ -16,6 +18,12 @@ public class StateKeyValue Key = key; Values = values; } + + public override string ToString() + { + var lastValue = Values.LastOrDefault(); + return $"{Key} => ({lastValue?.ToString()})"; + } } public class StateValue @@ -30,6 +38,12 @@ public class StateValue [JsonPropertyName("active_rounds")] public int ActiveRounds { get; set; } + [JsonPropertyName("data_type")] + public string DataType { get; set; } = StateDataType.String; + + [JsonPropertyName("source")] + public string Source { get; set; } + [JsonPropertyName("update_time")] public DateTime UpdateTime { get; set; } @@ -37,4 +51,11 @@ public class StateValue { } + + public override string ToString() + { + var isActive = Active ? "Yes" : "No"; + var activeRounds = ActiveRounds <= 0 ? "infinity" : ActiveRounds.ToString(); + return $"Data: {Data}, Active: {isActive}, Active rounds: {activeRounds}, Source: {Source}"; + } } \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/MessageParser.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/BotSharpMessageParser.cs similarity index 75% rename from src/Infrastructure/BotSharp.Abstraction/Messaging/MessageParser.cs rename to src/Infrastructure/BotSharp.Abstraction/Messaging/BotSharpMessageParser.cs index b739709c..6697ef34 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/MessageParser.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/BotSharpMessageParser.cs @@ -3,10 +3,13 @@ using BotSharp.Abstraction.Messaging.Enums; using BotSharp.Abstraction.Messaging.Models.RichContent.Template; using BotSharp.Abstraction.Messaging.Models.RichContent; using System.Text.Json; +using System.Reflection; +using Newtonsoft.Json; +using JsonSerializer = System.Text.Json.JsonSerializer; namespace BotSharp.Core.Messaging; -public static class MessageParser +public static class BotSharpMessageParser { public static IRichMessage? ParseRichMessage(JsonElement root, JsonSerializerOptions options) @@ -43,13 +46,13 @@ public static class MessageParser if (root.TryGetProperty("element_type", out element)) { var elementType = element.GetString(); - if (elementType == typeof(GenericElement).Name) + var wrapperType = typeof(GenericTemplateMessage<>); + var genericType = Assembly.GetExecutingAssembly().GetTypes().FirstOrDefault(x => x.Name == elementType); + + if (wrapperType != null && genericType != null) { - res = JsonSerializer.Deserialize>(jsonText, options); - } - else if (elementType == typeof(ButtonElement).Name) - { - res = JsonSerializer.Deserialize>(jsonText, options); + var targetType = wrapperType.MakeGenericType(genericType); + res = JsonConvert.DeserializeObject(jsonText, targetType) as IRichMessage; } } } @@ -88,13 +91,13 @@ public static class MessageParser if (root.TryGetProperty("element_type", out element)) { var elementType = element.GetString(); - if (elementType == typeof(GenericElement).Name) + var wrapperType = typeof(GenericTemplateMessage<>); + var genericType = Assembly.GetExecutingAssembly().GetTypes().FirstOrDefault(x => x.Name == elementType); + + if (wrapperType != null && genericType != null) { - res = JsonSerializer.Deserialize>(jsonText, options); - } - else if (elementType == typeof(ButtonElement).Name) - { - res = JsonSerializer.Deserialize>(jsonText, options); + var targetType = wrapperType.MakeGenericType(genericType); + res = JsonConvert.DeserializeObject(jsonText, targetType) as ITemplateMessage; } } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/RichContentJsonConverter .cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/RichContentJsonConverter .cs index 98230871..94d12796 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/RichContentJsonConverter .cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/RichContentJsonConverter .cs @@ -9,7 +9,7 @@ public class RichContentJsonConverter : JsonConverter { using var jsonDoc = JsonDocument.ParseValue(ref reader); var root = jsonDoc.RootElement; - var res = MessageParser.ParseRichMessage(root, options); + var res = BotSharpMessageParser.ParseRichMessage(root, options); return res; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/TemplateMessageJsonConverter.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/TemplateMessageJsonConverter.cs index da5fcfd1..84963d39 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/TemplateMessageJsonConverter.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/JsonConverters/TemplateMessageJsonConverter.cs @@ -9,7 +9,7 @@ public class TemplateMessageJsonConverter : JsonConverter { using var jsonDoc = JsonDocument.ParseValue(ref reader); var root = jsonDoc.RootElement; - var res = MessageParser.ParseTemplateMessage(root, options); + var res = BotSharpMessageParser.ParseTemplateMessage(root, options); return res; } diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs index 9356e2f5..32fbb30a 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs @@ -1,3 +1,4 @@ +using BotSharp.Abstraction.Conversations.Enums; using BotSharp.Abstraction.Users.Enums; namespace BotSharp.Core.Conversations.Services; @@ -33,7 +34,8 @@ public class ConversationStateService : IConversationStateService, IDisposable /// /// whether the state is related to message or not /// - public IConversationStateService SetState(string name, T value, bool isNeedVersion = true, int activeRounds = -1) + public IConversationStateService SetState(string name, T value, bool isNeedVersion = true, + int activeRounds = -1, string valueType = StateDataType.String, string source = StateSource.User) { if (value == null) { @@ -56,18 +58,21 @@ public class ConversationStateService : IConversationStateService, IDisposable _logger.LogInformation($"[STATE] {name} = {value}"); var routingCtx = _services.GetRequiredService(); - foreach (var hook in hooks) + if (!ContainsState(name) || preValue != currentValue || preActiveRounds != curActiveRounds) { - hook.OnStateChanged(new StateChangeModel + foreach (var hook in hooks) { - ConversationId = _conversationId, - MessageId = routingCtx.MessageId, - Name = name, - BeforeValue = preValue, - BeforeActiveRounds = preActiveRounds, - AfterValue = currentValue, - AfterActiveRounds = curActiveRounds - }).Wait(); + hook.OnStateChanged(new StateChangeModel + { + ConversationId = _conversationId, + MessageId = routingCtx.MessageId, + Name = name, + BeforeValue = preValue, + BeforeActiveRounds = preActiveRounds, + AfterValue = currentValue, + AfterActiveRounds = curActiveRounds + }).Wait(); + } } var newPair = new StateKeyValue @@ -82,6 +87,8 @@ public class ConversationStateService : IConversationStateService, IDisposable MessageId = routingCtx.MessageId, Active = true, ActiveRounds = curActiveRounds, + DataType = valueType, + Source = source, UpdateTime = DateTime.UtcNow, }; @@ -132,6 +139,8 @@ public class ConversationStateService : IConversationStateService, IDisposable MessageId = curMsgId, Active = false, ActiveRounds = value.ActiveRounds, + DataType = value.DataType, + Source = value.Source, UpdateTime = DateTime.UtcNow }); continue; @@ -192,6 +201,8 @@ public class ConversationStateService : IConversationStateService, IDisposable MessageId = curMsgId, Active = false, ActiveRounds = lastValue.ActiveRounds, + DataType = lastValue.DataType, + Source = lastValue.Source, UpdateTime = utcNow }); } @@ -246,7 +257,7 @@ public class ConversationStateService : IConversationStateService, IDisposable { if (!string.IsNullOrEmpty(property.Value.ToString())) { - SetState(property.Name, property.Value); + SetState(property.Name, property.Value, source: StateSource.Application); } } }