refine states

This commit is contained in:
Jicheng Lu 2024-04-02 11:38:18 -05:00
parent a6d57e417c
commit 7ae9e3ef60
9 changed files with 84 additions and 28 deletions

View file

@ -22,6 +22,7 @@
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
<PackageReference Include="System.Text.Json" Version="8.0.1" />
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />

View file

@ -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";
}

View file

@ -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";
}

View file

@ -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<string, string> GetStates();
IConversationStateService SetState<T>(string name, T value, bool isNeedVersion = true, int activeRounds = -1);
IConversationStateService SetState<T>(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();

View file

@ -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}";
}
}

View file

@ -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<GenericTemplateMessage<GenericElement>>(jsonText, options);
}
else if (elementType == typeof(ButtonElement).Name)
{
res = JsonSerializer.Deserialize<GenericTemplateMessage<ButtonElement>>(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<GenericTemplateMessage<GenericElement>>(jsonText, options);
}
else if (elementType == typeof(ButtonElement).Name)
{
res = JsonSerializer.Deserialize<GenericTemplateMessage<ButtonElement>>(jsonText, options);
var targetType = wrapperType.MakeGenericType(genericType);
res = JsonConvert.DeserializeObject(jsonText, targetType) as ITemplateMessage;
}
}
}

View file

@ -9,7 +9,7 @@ public class RichContentJsonConverter : JsonConverter<IRichMessage>
{
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;
}

View file

@ -9,7 +9,7 @@ public class TemplateMessageJsonConverter : JsonConverter<ITemplateMessage>
{
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;
}

View file

@ -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
/// <param name="value"></param>
/// <param name="isNeedVersion">whether the state is related to message or not</param>
/// <returns></returns>
public IConversationStateService SetState<T>(string name, T value, bool isNeedVersion = true, int activeRounds = -1)
public IConversationStateService SetState<T>(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<IRoutingContext>();
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);
}
}
}