diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs index 6a04e796..ddf985b4 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs @@ -61,4 +61,6 @@ public interface IConversationService Task GetConversationRecordOrCreateNew(string agentId); bool IsConversationMode(); + + void SaveStates(); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs index 5a513902..d7ac7d04 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs @@ -3,6 +3,7 @@ using BotSharp.Abstraction.Plugins.Models; using BotSharp.Abstraction.Repositories.Filters; using BotSharp.Abstraction.Roles.Models; using BotSharp.Abstraction.Shared; +using BotSharp.Abstraction.Statistics.Enums; using BotSharp.Abstraction.Statistics.Models; using BotSharp.Abstraction.Tasks.Models; using BotSharp.Abstraction.Translation.Models; @@ -120,8 +121,10 @@ public interface IBotSharpRepository : IHaveServiceProvider #endregion #region Statistics - BotSharpStats? GetGlobalStats(string category, string group, DateTime recordTime) => throw new NotImplementedException(); - bool SaveGlobalStats(BotSharpStats body) => throw new NotImplementedException(); + BotSharpStats? GetGlobalStats(string metric, string dimension, DateTime recordTime, StatsInterval interval) + => throw new NotImplementedException(); + bool SaveGlobalStats(BotSharpStats body) + => throw new NotImplementedException(); #endregion diff --git a/src/Infrastructure/BotSharp.Abstraction/Statistics/Enums/StatsInterval.cs b/src/Infrastructure/BotSharp.Abstraction/Statistics/Enums/StatsInterval.cs new file mode 100644 index 00000000..7f3233bf --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Statistics/Enums/StatsInterval.cs @@ -0,0 +1,9 @@ +namespace BotSharp.Abstraction.Statistics.Enums; + +public enum StatsInterval +{ + Hour = 1, + Day = 2, + Week = 3, + Month = 4 +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Statistics/Models/BotSharpStats.cs b/src/Infrastructure/BotSharp.Abstraction/Statistics/Models/BotSharpStats.cs index d9735d3a..ef7df44f 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Statistics/Models/BotSharpStats.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Statistics/Models/BotSharpStats.cs @@ -1,34 +1,80 @@ +using BotSharp.Abstraction.Statistics.Enums; + namespace BotSharp.Abstraction.Statistics.Models; public class BotSharpStats { - [JsonPropertyName("category")] - public string Category { get; set; } = null!; + [JsonPropertyName("metric")] + public string Metric { get; set; } = null!; - [JsonPropertyName("group")] - public string Group { get; set; } = null!; + [JsonPropertyName("dimension")] + public string Dimension { get; set; } = null!; [JsonPropertyName("data")] public IDictionary Data { get; set; } = new Dictionary(); - private DateTime innerRecordTime; - [JsonPropertyName("record_time")] - public DateTime RecordTime + public DateTime RecordTime { get; set; } = DateTime.UtcNow; + + [JsonIgnore] + public StatsInterval IntervalType { get; set; } + + [JsonPropertyName("interval")] + public string Interval { get { - return innerRecordTime; - } + return IntervalType.ToString(); + } set { - var date = new DateTime(value.Year, value.Month, value.Day, value.Hour, 0, 0); - innerRecordTime = DateTime.SpecifyKind(date, DateTimeKind.Utc); + if (Enum.TryParse(value, out StatsInterval type)) + { + IntervalType = type; + } } } + [JsonPropertyName("start_time")] + public DateTime StartTime { get; set; } + + [JsonPropertyName("end_time")] + public DateTime EndTime { get; set; } + public override string ToString() { - return $"{Category}-{Group}: {Data?.Count ?? 0} ({RecordTime})"; + return $"{Metric}-{Dimension} ({Interval}): {Data?.Count ?? 0}"; + } + + public static (DateTime, DateTime) BuildTimeInterval(DateTime recordTime, StatsInterval interval) + { + DateTime startTime = recordTime; + DateTime endTime = DateTime.UtcNow; + + switch (interval) + { + case StatsInterval.Hour: + startTime = new DateTime(recordTime.Year, recordTime.Month, recordTime.Day, recordTime.Hour, 0, 0); + endTime = startTime.AddHours(1); + break; + case StatsInterval.Week: + var dayOfWeek = startTime.DayOfWeek; + var firstDayOfWeek = startTime.AddDays(-(int)dayOfWeek); + startTime = new DateTime(firstDayOfWeek.Year, firstDayOfWeek.Month, firstDayOfWeek.Day, 0, 0, 0); + endTime = startTime.AddDays(7); + break; + case StatsInterval.Month: + startTime = new DateTime(recordTime.Year, recordTime.Month, 1); + endTime = startTime.AddMonths(1); + break; + default: + startTime = new DateTime(recordTime.Year, recordTime.Month, recordTime.Day, 0, 0, 0); + endTime = startTime.AddDays(1); + break; + } + + startTime = DateTime.SpecifyKind(startTime, DateTimeKind.Utc); + endTime = DateTime.SpecifyKind(endTime, DateTimeKind.Utc); + return (startTime, endTime); } } \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Abstraction/Statistics/Models/BotSharpStatsInput.cs b/src/Infrastructure/BotSharp.Abstraction/Statistics/Models/BotSharpStatsInput.cs index d1ccbdbd..0058872e 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Statistics/Models/BotSharpStatsInput.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Statistics/Models/BotSharpStatsInput.cs @@ -1,9 +1,12 @@ +using BotSharp.Abstraction.Statistics.Enums; + namespace BotSharp.Abstraction.Statistics.Models; public class BotSharpStatsInput { - public string Category { get; set; } - public string Group { get; set; } + public string Metric { get; set; } + public string Dimension { get; set; } public List Data { get; set; } = []; - public DateTime RecordTime { get; set; } + public DateTime RecordTime { get; set; } = DateTime.UtcNow; + public StatsInterval IntervalType { get; set; } = StatsInterval.Day; } diff --git a/src/Infrastructure/BotSharp.Core.Crontab/Services/CrontabEventSubscription.cs b/src/Infrastructure/BotSharp.Core.Crontab/Services/CrontabEventSubscription.cs index fa3b372d..099b9312 100644 --- a/src/Infrastructure/BotSharp.Core.Crontab/Services/CrontabEventSubscription.cs +++ b/src/Infrastructure/BotSharp.Core.Crontab/Services/CrontabEventSubscription.cs @@ -1,7 +1,6 @@ using BotSharp.Abstraction.Infrastructures.Events; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using System.Runtime.InteropServices; namespace BotSharp.Core.Crontab.Services; @@ -22,6 +21,7 @@ public class CrontabEventSubscription : BackgroundService using (var scope = _services.CreateScope()) { + var publisher = scope.ServiceProvider.GetRequiredService(); var subscriber = scope.ServiceProvider.GetRequiredService(); var cron = scope.ServiceProvider.GetRequiredService(); var crons = await cron.GetCrontable(); @@ -29,15 +29,20 @@ public class CrontabEventSubscription : BackgroundService { _ = Task.Run(async () => { + // Clean unhandled messages + await publisher.RemoveAsync($"Crontab:{item.Title}", count: 100); + await subscriber.SubscribeAsync($"Crontab:{item.Title}", "Crontab", port: 0, - priorityEnabled: false, async (sender, args) => + priorityEnabled: false, + async (sender, args) => { var scope = _services.CreateScope(); cron = scope.ServiceProvider.GetRequiredService(); await cron.ScheduledTimeArrived(item); - }, stoppingToken: stoppingToken); + }, + stoppingToken: stoppingToken); }); } } diff --git a/src/Infrastructure/BotSharp.Core.Rules/Engines/IRuleEngine.cs b/src/Infrastructure/BotSharp.Core.Rules/Engines/IRuleEngine.cs index 45eae430..2d4c1111 100644 --- a/src/Infrastructure/BotSharp.Core.Rules/Engines/IRuleEngine.cs +++ b/src/Infrastructure/BotSharp.Core.Rules/Engines/IRuleEngine.cs @@ -1,6 +1,8 @@ +using BotSharp.Abstraction.Models; + namespace BotSharp.Core.Rules.Engines; public interface IRuleEngine { - Task Triggered(IRuleTrigger trigger, string data); + Task Triggered(IRuleTrigger trigger, string data, List? states = null); } diff --git a/src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs b/src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs index feea122f..f545522f 100644 --- a/src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs +++ b/src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs @@ -18,7 +18,7 @@ public class RuleEngine : IRuleEngine _logger = logger; } - public async Task Triggered(IRuleTrigger trigger, string data) + public async Task Triggered(IRuleTrigger trigger, string data, List? states = null) { // Pull all user defined rules var agentService = _services.GetRequiredService(); @@ -36,10 +36,11 @@ public class RuleEngine : IRuleEngine // Trigger the agents var instructService = _services.GetRequiredService(); - var convService = _services.GetRequiredService(); + foreach (var agent in preFilteredAgents) { + var convService = _services.GetRequiredService(); var conv = await convService.NewConversation(new Conversation { Channel = trigger.Channel, @@ -49,18 +50,25 @@ public class RuleEngine : IRuleEngine var message = new RoleDialogModel(AgentRole.User, data); - var states = new List + var allStates = new List { - new("channel", trigger.Channel), - new("channel_id", trigger.EntityId) + new("channel", trigger.Channel) }; - convService.SetConversationId(conv.Id, states); + + if (states != null) + { + allStates.AddRange(states); + } + + convService.SetConversationId(conv.Id, allStates); await convService.SendMessage(agent.Id, message, null, msg => Task.CompletedTask); + convService.SaveStates(); + /*foreach (var rule in agent.Rules) { var userSay = $"===Input data with Before and After values===\r\n{data}\r\n\r\n===Trigger Criteria===\r\n{rule.Criteria}\r\n\r\nJust output 1 or 0 without explanation: "; diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.TruncateMessage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.TruncateMessage.cs index 1a75d717..05d04f25 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.TruncateMessage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.TruncateMessage.cs @@ -1,6 +1,6 @@ namespace BotSharp.Core.Conversations.Services; -public partial class ConversationService : IConversationService +public partial class ConversationService { public async Task TruncateConversation(string conversationId, string messageId, string? newMessageId = null) { diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.UpdateBreakpoint.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.UpdateBreakpoint.cs index 75a0bbfb..203fa885 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.UpdateBreakpoint.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.UpdateBreakpoint.cs @@ -2,7 +2,7 @@ using BotSharp.Abstraction.Infrastructures.Enums; namespace BotSharp.Core.Conversations.Services; -public partial class ConversationService : IConversationService +public partial class ConversationService { public async Task UpdateBreakpoint(bool resetStates = false, string? reason = null, params string[] excludedStates) { diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs index 7e498afe..0c02918e 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs @@ -216,4 +216,9 @@ public partial class ConversationService : IConversationService var agent = db.GetAgent(routingCtx.EntryAgentId, basicsOnly: true); return agent?.MaxMessageCount; } + + public void SaveStates() + { + _state.Save(); + } } diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs index eedcdf9a..d11d0a31 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs @@ -211,11 +211,11 @@ public class ConversationStateService : IConversationStateService, IDisposable { if (_conversationId == null) { + Reset(); return; } var states = new List(); - foreach (var pair in _curStates) { var key = pair.Key; @@ -244,6 +244,7 @@ public class ConversationStateService : IConversationStateService, IDisposable } _db.UpdateConversationStates(_conversationId, states); + Reset(); _logger.LogInformation($"Saved states of conversation {_conversationId}"); } @@ -421,4 +422,10 @@ public class ConversationStateService : IConversationStateService, IDisposable { _curStates.Clear(); } + + private void Reset() + { + _curStates.Clear(); + _historyStates.Clear(); + } } diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/TokenStatistics.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/TokenStatistics.cs index 69462b40..ade97592 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/TokenStatistics.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/TokenStatistics.cs @@ -62,9 +62,10 @@ public class TokenStatistics : ITokenStatistics var globalStats = _services.GetRequiredService(); var body = new BotSharpStatsInput { - Category = StatsCategory.AgentLlmCost, - Group = message.CurrentAgentId, + Metric = StatsCategory.AgentLlmCost, + Dimension = message.CurrentAgentId, RecordTime = DateTime.UtcNow, + IntervalType = StatsInterval.Day, Data = [ new StatsKeyValuePair("prompt_token_count_total", stats.PromptCount), new StatsKeyValuePair("completion_token_count_total", stats.CompletionCount), diff --git a/src/Infrastructure/BotSharp.Core/Infrastructures/Events/RedisPublisher.cs b/src/Infrastructure/BotSharp.Core/Infrastructures/Events/RedisPublisher.cs index 5c49f328..56cefcc1 100644 --- a/src/Infrastructure/BotSharp.Core/Infrastructures/Events/RedisPublisher.cs +++ b/src/Infrastructure/BotSharp.Core/Infrastructures/Events/RedisPublisher.cs @@ -182,7 +182,10 @@ public class RedisPublisher : IEventPublisher var db = _redis.GetDatabase(); var entries = await db.StreamRangeAsync(channel, "-", "+", count: count, messageOrder: Order.Ascending); - var deletedCount = await db.StreamDeleteAsync(channel, entries.Select(x => x.Id).ToArray()); - _logger.LogWarning($"Deleted {deletedCount} messages from Redis stream {channel}"); + if (entries.Length > 0) + { + var deletedCount = await db.StreamDeleteAsync(channel, entries.Select(x => x.Id).ToArray()); + _logger.LogWarning($"Deleted {deletedCount} messages from Redis stream {channel}"); + } } } diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Stats.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Stats.cs index 0735c7f7..de5ebf65 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Stats.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Stats.cs @@ -4,28 +4,34 @@ namespace BotSharp.Core.Repository; public partial class FileRepository { - public BotSharpStats? GetGlobalStats(string category, string group, DateTime recordTime) + public BotSharpStats? GetGlobalStats(string metric, string dimension, DateTime recordTime, StatsInterval interval) { var baseDir = Path.Combine(_dbSettings.FileRepository, STATS_FOLDER); - var dir = Path.Combine(baseDir, category, recordTime.Year.ToString(), recordTime.Month.ToString("D2")); + var (startTime, endTime) = BotSharpStats.BuildTimeInterval(recordTime, interval); + var dir = Path.Combine(baseDir, metric, startTime.Year.ToString(), startTime.Month.ToString("D2")); if (!Directory.Exists(dir)) return null; var file = Directory.GetFiles(dir).FirstOrDefault(x => Path.GetFileName(x) == STATS_FILE); if (file == null) return null; - var time = BuildRecordTime(recordTime); var text = File.ReadAllText(file); var list = JsonSerializer.Deserialize>(text, _options); - var found = list?.FirstOrDefault(x => x.Category.IsEqualTo(category) - && x.Group.IsEqualTo(group) - && x.RecordTime == time); + var found = list?.FirstOrDefault(x => x.Metric.IsEqualTo(metric) + && x.Dimension.IsEqualTo(dimension) + && x.StartTime == startTime + && x.EndTime == endTime); + return found; } public bool SaveGlobalStats(BotSharpStats body) { var baseDir = Path.Combine(_dbSettings.FileRepository, STATS_FOLDER); - var dir = Path.Combine(baseDir, body.Category, body.RecordTime.Year.ToString(), body.RecordTime.Month.ToString("D2")); + var (startTime, endTime) = BotSharpStats.BuildTimeInterval(body.RecordTime, body.IntervalType); + body.StartTime = startTime; + body.EndTime = endTime; + + var dir = Path.Combine(baseDir, body.Metric, startTime.Year.ToString(), startTime.Month.ToString("D2")); if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); @@ -39,19 +45,22 @@ public partial class FileRepository } else { - var time = BuildRecordTime(body.RecordTime); var text = File.ReadAllText(file); var list = JsonSerializer.Deserialize>(text, _options); - var found = list?.FirstOrDefault(x => x.Category.IsEqualTo(body.Category) - && x.Group.IsEqualTo(body.Group) - && x.RecordTime == time); + var found = list?.FirstOrDefault(x => x.Metric.IsEqualTo(body.Metric) + && x.Dimension.IsEqualTo(body.Dimension) + && x.StartTime == startTime + && x.EndTime == endTime); if (found != null) { - found.Category = body.Category; - found.Group = body.Group; + found.Metric = body.Metric; + found.Dimension = body.Dimension; found.Data = body.Data; found.RecordTime = body.RecordTime; + found.StartTime = body.StartTime; + found.EndTime = body.EndTime; + found.Interval = body.Interval; } else if (list != null) { @@ -67,12 +76,4 @@ public partial class FileRepository return true; } - - #region Private methods - private DateTime BuildRecordTime(DateTime date) - { - var recordDate = new DateTime(date.Year, date.Month, date.Day, date.Hour, 0, 0); - return DateTime.SpecifyKind(recordDate, DateTimeKind.Utc); - } - #endregion } diff --git a/src/Infrastructure/BotSharp.Core/Statistics/Services/BotSharpStatsService.cs b/src/Infrastructure/BotSharp.Core/Statistics/Services/BotSharpStatsService.cs index d30b85de..fca0697f 100644 --- a/src/Infrastructure/BotSharp.Core/Statistics/Services/BotSharpStatsService.cs +++ b/src/Infrastructure/BotSharp.Core/Statistics/Services/BotSharpStatsService.cs @@ -29,8 +29,9 @@ public class BotSharpStatsService : IBotSharpStatsService if (!_settings.Enabled || string.IsNullOrEmpty(resourceKey) || input == null - || string.IsNullOrEmpty(input.Category) - || string.IsNullOrEmpty(input.Group)) + || string.IsNullOrEmpty(input.Metric) + || string.IsNullOrEmpty(input.Dimension) + || input.Data.IsNullOrEmpty()) { return false; } @@ -39,14 +40,15 @@ public class BotSharpStatsService : IBotSharpStatsService var res = locker.Lock(resourceKey, () => { var db = _services.GetRequiredService(); - var body = db.GetGlobalStats(input.Category, input.Group, input.RecordTime); + var body = db.GetGlobalStats(input.Metric, input.Dimension, input.RecordTime, input.IntervalType); if (body == null) { var stats = new BotSharpStats { - Category = input.Category, - Group = input.Group, + Metric = input.Metric, + Dimension = input.Dimension, RecordTime = input.RecordTime, + IntervalType = input.IntervalType, Data = input.Data.ToDictionary(x => x.Key, x => x.Value) }; db.SaveGlobalStats(stats); @@ -85,7 +87,7 @@ public class BotSharpStatsService : IBotSharpStatsService } catch (Exception ex) { - _logger.LogError($"Error when updating global stats {input.Category}-{input.Group}. {ex.Message}\r\n{ex.InnerException}"); + _logger.LogError($"Error when updating global stats {input.Metric}-{input.Dimension}. {ex.Message}\r\n{ex.InnerException}"); return false; } } diff --git a/src/Infrastructure/BotSharp.Logger/Hooks/GlobalStatsConversationHook.cs b/src/Infrastructure/BotSharp.Logger/Hooks/GlobalStatsConversationHook.cs index d0e6675b..41fd5852 100644 --- a/src/Infrastructure/BotSharp.Logger/Hooks/GlobalStatsConversationHook.cs +++ b/src/Infrastructure/BotSharp.Logger/Hooks/GlobalStatsConversationHook.cs @@ -31,12 +31,13 @@ public class GlobalStatsConversationHook : ConversationHookBase var body = new BotSharpStatsInput { - Category = StatsCategory.AgentCall, - Group = message.CurrentAgentId, + Metric = StatsCategory.AgentCall, + Dimension = message.CurrentAgentId, + RecordTime = DateTime.UtcNow, + IntervalType = StatsInterval.Day, Data = [ new StatsKeyValuePair("agent_call_count", 1) - ], - RecordTime = DateTime.UtcNow + ] }; globalStats.UpdateStats("global-agent-call", body); } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/GlobalStatisticsDocument.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/GlobalStatisticsDocument.cs index 74ca55ef..35d04f8e 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/GlobalStatisticsDocument.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/GlobalStatisticsDocument.cs @@ -1,9 +1,14 @@ +using BotSharp.Abstraction.Statistics.Enums; + namespace BotSharp.Plugin.MongoStorage.Collections; public class GlobalStatisticsDocument : MongoBase { - public string Category { get; set; } - public string Group { get; set; } + public string Metric { get; set; } + public string Dimension { get; set; } public IDictionary Data { get; set; } = new Dictionary(); public DateTime RecordTime { get; set; } + public DateTime StartTime { get; set; } + public DateTime EndTime { get; set; } + public string Interval { get; set; } } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Stats.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Stats.cs index 44e667ca..553e5175 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Stats.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Stats.cs @@ -1,19 +1,21 @@ +using BotSharp.Abstraction.Statistics.Enums; using BotSharp.Abstraction.Statistics.Models; namespace BotSharp.Plugin.MongoStorage.Repository; public partial class MongoRepository { - public BotSharpStats? GetGlobalStats(string category, string group, DateTime recordTime) + public BotSharpStats? GetGlobalStats(string metric, string dimension, DateTime recordTime, StatsInterval interval) { - var time = BuildRecordTime(recordTime); + var (startTime, endTime) = BotSharpStats.BuildTimeInterval(recordTime, interval); var builder = Builders.Filter; var filters = new List>() { - builder.Eq(x => x.Category, category), - builder.Eq(x => x.Group, group), - builder.Eq(x => x.RecordTime, time) + builder.Eq(x => x.Metric, metric), + builder.Eq(x => x.Dimension, dimension), + builder.Eq(x => x.StartTime, startTime), + builder.Eq(x => x.EndTime, endTime) }; var filterDef = builder.And(filters); @@ -22,41 +24,44 @@ public partial class MongoRepository return new BotSharpStats { - Category = found.Category, - Group = found.Group, + Metric = found.Metric, + Dimension = found.Dimension, Data = found.Data, - RecordTime = found.RecordTime + RecordTime = found.RecordTime, + StartTime = startTime, + EndTime = endTime, + Interval = interval.ToString() }; } public bool SaveGlobalStats(BotSharpStats body) { - var time = BuildRecordTime(body.RecordTime); + var (startTime, endTime) = BotSharpStats.BuildTimeInterval(body.RecordTime, body.IntervalType); + body.RecordTime = DateTime.SpecifyKind(body.RecordTime, DateTimeKind.Utc); + body.StartTime = startTime; + body.EndTime = endTime; + var builder = Builders.Filter; var filters = new List>() { - builder.Eq(x => x.Category, body.Category), - builder.Eq(x => x.Group, body.Group), - builder.Eq(x => x.RecordTime, time) + builder.Eq(x => x.Metric, body.Metric), + builder.Eq(x => x.Dimension, body.Dimension), + builder.Eq(x => x.StartTime, startTime), + builder.Eq(x => x.EndTime, endTime) }; var filterDef = builder.And(filters); var updateDef = Builders.Update .SetOnInsert(x => x.Id, Guid.NewGuid().ToString()) - .Set(x => x.Category, body.Category) - .Set(x => x.Group, body.Group) + .Set(x => x.Metric, body.Metric) + .Set(x => x.Dimension, body.Dimension) .Set(x => x.Data, body.Data) - .Set(x => x.RecordTime, time); + .Set(x => x.StartTime, body.StartTime) + .Set(x => x.EndTime, body.EndTime) + .Set(x => x.Interval, body.Interval) + .Set(x => x.RecordTime, body.RecordTime); _dc.GlobalStatistics.UpdateOne(filterDef, updateDef, _options); return true; } - - #region Private methods - private DateTime BuildRecordTime(DateTime date) - { - var recordDate = new DateTime(date.Year, date.Month, date.Day, date.Hour, 0, 0); - return DateTime.SpecifyKind(recordDate, DateTimeKind.Utc); - } - #endregion }