From 2a31af5911e03064ca08db05cbef0887a6568905 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Mon, 6 May 2024 17:31:52 -0500 Subject: [PATCH] refine file service --- .../Conversations/IConversationService.cs | 10 ++- .../Conversations/Models/RoleDialogModel.cs | 1 + .../Files/IBotSharpFileService.cs | 15 +++- .../Files/Models/BotSharpFile.cs | 3 - .../Repositories/IBotSharpRepository.cs | 2 +- .../ConversationService.SendMessage.cs | 14 ++-- .../ConversationService.TruncateMessage.cs | 10 ++- .../Services/ConversationService.cs | 2 + .../Services/ConversationStorage.cs | 4 -- .../Files/BotSharpFileService.cs | 70 ++++++++++++++++--- .../Repository/BotSharpDbContext.cs | 2 +- .../FileRepository.Conversation.cs | 30 ++++++-- .../Controllers/ConversationController.cs | 17 +++-- .../Controllers/FileController.cs | 7 +- .../WebSocketsMiddleware.cs | 5 +- .../MongoRepository.Conversation.cs | 24 +++++-- 16 files changed, 159 insertions(+), 57 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs index d0a95ef8..9df6ae7f 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs @@ -15,7 +15,15 @@ public interface IConversationService Task> GetLastConversations(); Task> GetIdleConversations(int batchSize, int messageLimit, int bufferHours); Task DeleteConversations(IEnumerable ids); - Task TruncateConversation(string conversationId, string messageId); + + /// + /// Truncate conversation + /// + /// Target conversation id + /// Target message id to delete + /// If not null, delete messages while input a new message; otherwise delete messages only + /// + Task TruncateConversation(string conversationId, string messageId, string? newMessageId = null); Task> GetConversationContentLogs(string conversationId); Task> GetConversationStateLogs(string conversationId); diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs index 8085a2c5..a8e9bc67 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs @@ -86,6 +86,7 @@ public class RoleDialogModel : ITrackableMessage Role = role; Content = text; MessageId = Guid.NewGuid().ToString(); + CreatedAt = DateTime.UtcNow; } public override string ToString() diff --git a/src/Infrastructure/BotSharp.Abstraction/Files/IBotSharpFileService.cs b/src/Infrastructure/BotSharp.Abstraction/Files/IBotSharpFileService.cs index af1e5a9f..9c27d7ff 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Files/IBotSharpFileService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Files/IBotSharpFileService.cs @@ -4,6 +4,17 @@ public interface IBotSharpFileService { string GetDirectory(string conversationId); IEnumerable GetConversationFiles(string conversationId, string messageId); - string? GetMessageFile(string conversationId, string messageId, string fileName, string fileType); - void SaveConversationFiles(string conversationId, List files); + string? GetMessageFile(string conversationId, string messageId, string fileName); + void SaveMessageFiles(string conversationId, string messageId, List files); + + /// + /// Delete files under messages + /// + /// Conversation Id + /// Files in these messages will be deleted + /// The starting message to delete + /// If not null, delete messages while input a new message; otherwise, delete messages only + /// + bool DeleteMessageFiles(string conversationId, IEnumerable messageIds, string targetMessageId, string? newMessageId = null); + bool DeleteConversationFiles(IEnumerable conversationIds); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Files/Models/BotSharpFile.cs b/src/Infrastructure/BotSharp.Abstraction/Files/Models/BotSharpFile.cs index 732085a7..f679d52e 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Files/Models/BotSharpFile.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Files/Models/BotSharpFile.cs @@ -3,9 +3,6 @@ namespace BotSharp.Abstraction.Files.Models; public class BotSharpFile { - [JsonPropertyName("message_id")] - public string MessageId { get; set; } - [JsonPropertyName("file_name")] public string FileName { get; set; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs index a1124975..6c488385 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs @@ -63,7 +63,7 @@ public interface IBotSharpRepository ConversationBreakpoint? GetConversationBreakpoint(string conversationId); List GetLastConversations(); List GetIdleConversations(int batchSize, int messageLimit, int bufferHours); - bool TruncateConversation(string conversationId, string messageId, bool cleanLog = false); + IEnumerable TruncateConversation(string conversationId, string messageId, bool cleanLog = false); #endregion #region Execution Log diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs index dbfdf7dc..f8460e3a 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs @@ -1,5 +1,4 @@ using BotSharp.Abstraction.Messaging; -using BotSharp.Abstraction.Messaging.Enums; using BotSharp.Abstraction.Messaging.Models.RichContent; using BotSharp.Abstraction.Routing.Settings; using System.Drawing; @@ -28,7 +27,6 @@ public partial class ConversationService #endif message.CurrentAgentId = agent.Id; - message.CreatedAt = DateTime.UtcNow; if (string.IsNullOrEmpty(message.SenderId)) { message.SenderId = _user.Id; @@ -48,6 +46,11 @@ public partial class ConversationService routing.Context.SetMessageId(_conversationId, message.MessageId); routing.Context.Push(agent.Id); + // Save message files + var fileService = _services.GetRequiredService(); + fileService.SaveMessageFiles(_conversationId, message.MessageId, message.Files); + message.Files?.Clear(); + // Before chat completion hook foreach (var hook in hooks) { @@ -142,13 +145,6 @@ public partial class ConversationService Message = new TextMessage(response.SecondaryContent ?? response.Content) }; - response.RichContent = new RichContent - { - Recipient = new Recipient { Id = state.GetConversationId() }, - Editor = EditorTypeEnum.File, - Message = new TextMessage(response.SecondaryContent ?? response.Content) - }; - // Patch return function name if (response.PostbackFunctionName != null) { diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.TruncateMessage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.TruncateMessage.cs index e4b81f31..451cdeed 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.TruncateMessage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.TruncateMessage.cs @@ -2,15 +2,19 @@ namespace BotSharp.Core.Conversations.Services; public partial class ConversationService : IConversationService { - public async Task TruncateConversation(string conversationId, string messageId) + public async Task TruncateConversation(string conversationId, string messageId, string? newMessageId = null) { var db = _services.GetRequiredService(); - var isSaved = db.TruncateConversation(conversationId, messageId, true); + var fileService = _services.GetRequiredService(); + var deleteMessageIds = db.TruncateConversation(conversationId, messageId, cleanLog: true); + + fileService.DeleteMessageFiles(conversationId, deleteMessageIds, messageId, newMessageId); + var hooks = _services.GetServices().ToList(); foreach (var hook in hooks) { await hook.OnMessageDeleted(conversationId, messageId); } - return await Task.FromResult(isSaved); + return await Task.FromResult(true); } } diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs index 72b6cde0..4de36b9d 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs @@ -35,7 +35,9 @@ public partial class ConversationService : IConversationService public async Task DeleteConversations(IEnumerable ids) { var db = _services.GetRequiredService(); + var fileService = _services.GetRequiredService(); var isDeleted = db.DeleteConversations(ids); + fileService.DeleteConversationFiles(ids); return await Task.FromResult(isDeleted); } diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs index 5490daae..4ab447b8 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs @@ -1,4 +1,3 @@ -using BotSharp.Abstraction.Files; using BotSharp.Abstraction.Messaging; using BotSharp.Abstraction.Messaging.Models.RichContent; using BotSharp.Abstraction.Options; @@ -26,7 +25,6 @@ public class ConversationStorage : IConversationStorage { var agentId = dialog.CurrentAgentId; var db = _services.GetRequiredService(); - var fileService = _services.GetRequiredService(); var dialogElements = new List(); // Prevent duplicate record to be inserted @@ -78,8 +76,6 @@ public class ConversationStorage : IConversationStorage } db.AppendConversationDialogs(conversationId, dialogElements); - fileService.SaveConversationFiles(conversationId, dialog.Files); - dialog.Files.Clear(); } public List GetDialogs(string conversationId) diff --git a/src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.cs b/src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.cs index b24a33a2..ad687274 100644 --- a/src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.cs +++ b/src/Infrastructure/BotSharp.Core/Files/BotSharpFileService.cs @@ -47,16 +47,16 @@ public class BotSharpFileService : IBotSharpFileService var fileType = extension.Substring(1); var model = new OutputFileModel() { - FileUrl = $"/conversation/{conversationId}/message/{messageId}/file/{fileName}/type/{fileType}", + FileUrl = $"/conversation/{conversationId}/message/{messageId}/file/{fileName}", FileName = fileName, - FileType = extension + FileType = fileType }; outputFiles.Add(model); } return outputFiles; } - public string? GetMessageFile(string conversationId, string messageId, string fileName, string fileType) + public string? GetMessageFile(string conversationId, string messageId, string fileName) { var dir = GetConversationFileDirectory(conversationId, messageId); if (string.IsNullOrEmpty(dir)) @@ -64,23 +64,21 @@ public class BotSharpFileService : IBotSharpFileService return null; } - var targetFile = $"{fileName}.{fileType}"; - var found = Directory.GetFiles(dir).FirstOrDefault(f => Path.GetFileName(f).IsEqualTo(targetFile)); + var found = Directory.GetFiles(dir).FirstOrDefault(f => Path.GetFileNameWithoutExtension(f).IsEqualTo(fileName)); return found; } - public void SaveConversationFiles(string conversationId, List files) + public void SaveMessageFiles(string conversationId, string messageId, List files) { if (files.IsNullOrEmpty()) return; - var messageId = files.FirstOrDefault()?.MessageId; var dir = GetConversationFileDirectory(conversationId, messageId, createNewDir: true); if (string.IsNullOrEmpty(dir)) return; for (int i = 0; i < files.Count; i++) { var file = files[i]; - if (string.IsNullOrEmpty(file.MessageId) || string.IsNullOrEmpty(file.FileData)) + if (string.IsNullOrEmpty(file.FileData)) { continue; } @@ -93,6 +91,52 @@ public class BotSharpFileService : IBotSharpFileService } } + public bool DeleteMessageFiles(string conversationId, IEnumerable messageIds, string targetMessageId, string? newMessageId = null) + { + if (string.IsNullOrEmpty(conversationId) || messageIds == null) return false; + + if (!string.IsNullOrEmpty(targetMessageId) && !string.IsNullOrEmpty(newMessageId)) + { + var prevDir = GetConversationFileDirectory(conversationId, targetMessageId); + var newDir = Path.Combine(_baseDir, CONVERSATION_FOLDER, conversationId, FILE_FOLDER, newMessageId); + + if (Directory.Exists(prevDir)) + { + if (Directory.Exists(newDir)) + { + Directory.Delete(newDir, true); + } + + Directory.Move(prevDir, newDir); + } + } + + foreach ( var messageId in messageIds) + { + var dir = GetConversationFileDirectory(conversationId, messageId); + if (string.IsNullOrEmpty(dir)) continue; + + Thread.Sleep(100); + Directory.Delete(dir, true); + } + + return true; + } + + public bool DeleteConversationFiles(IEnumerable conversationIds) + { + if (conversationIds.IsNullOrEmpty()) return false; + + foreach (var conversationId in conversationIds) + { + var convDir = FindConversationDirectory(conversationId); + if (string.IsNullOrEmpty(convDir)) continue; + + Directory.Delete(convDir, true); + } + return true; + } + #region Private methods private string GetConversationFileDirectory(string? conversationId, string? messageId, bool createNewDir = false) { @@ -116,6 +160,16 @@ public class BotSharpFileService : IBotSharpFileService return dir; } + private string? FindConversationDirectory(string conversationId) + { + if (string.IsNullOrEmpty(conversationId)) return null; + + var dir = Path.Combine(_baseDir, CONVERSATION_FOLDER, conversationId); + if (!Directory.Exists(dir)) return null; + + return dir; + } + private byte[] GetFileBytes(string data) { if (string.IsNullOrEmpty(data)) diff --git a/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs b/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs index 0605c525..ed1d297d 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs @@ -172,7 +172,7 @@ public class BotSharpDbContext : Database, IBotSharpRepository public void UpdateConversationStatus(string conversationId, string status) => new NotImplementedException(); - public bool TruncateConversation(string conversationId, string messageId, bool cleanLog = false) + public IEnumerable TruncateConversation(string conversationId, string messageId, bool cleanLog = false) => throw new NotImplementedException(); #endregion diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs index 9a386e92..d9400e47 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs @@ -446,24 +446,40 @@ namespace BotSharp.Core.Repository } - public bool TruncateConversation(string conversationId, string messageId, bool cleanLog = false) + public IEnumerable TruncateConversation(string conversationId, string messageId, bool cleanLog = false) { - if (string.IsNullOrEmpty(conversationId) || string.IsNullOrEmpty(messageId)) return false; + var deletedMessageIds = new List(); + if (string.IsNullOrEmpty(conversationId) || string.IsNullOrEmpty(messageId)) + { + return deletedMessageIds; + } var dialogs = new List(); + var convDir = FindConversationDirectory(conversationId); - if (string.IsNullOrEmpty(convDir)) return false; + if (string.IsNullOrEmpty(convDir)) + { + return deletedMessageIds; + } var dialogDir = Path.Combine(convDir, DIALOG_FILE); dialogs = CollectDialogElements(dialogDir); - if (dialogs.IsNullOrEmpty()) return false; + if (dialogs.IsNullOrEmpty()) + { + return deletedMessageIds; + } var foundIdx = dialogs.FindIndex(x => x.MetaData?.MessageId == messageId); - if (foundIdx < 0) return false; + if (foundIdx < 0) + { + return deletedMessageIds; + } + + deletedMessageIds = dialogs.Where((x, idx) => idx >= foundIdx && !string.IsNullOrEmpty(x.MetaData?.MessageId)) + .Select(x => x.MetaData.MessageId).Distinct().ToList(); // Handle truncated dialogs var isSaved = HandleTruncatedDialogs(convDir, dialogDir, dialogs, foundIdx); - if (!isSaved) return false; // Handle truncated states var refTime = dialogs.ElementAt(foundIdx).MetaData.CreateTime; @@ -482,7 +498,7 @@ namespace BotSharp.Core.Repository HandleTruncatedLogs(convDir, refTime); } - return isSaved; + return deletedMessageIds; } diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index af7c44b3..c85b8e43 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -168,15 +168,15 @@ public class ConversationController : ControllerBase [FromBody] NewMessageModel input) { var conv = _services.GetRequiredService(); - if (!string.IsNullOrEmpty(input.TruncateMessageId)) - { - await conv.TruncateConversation(conversationId, input.TruncateMessageId); - } - var inputMsg = new RoleDialogModel(AgentRole.User, input.Text) { Files = input.Files }; + if (!string.IsNullOrEmpty(input.TruncateMessageId)) + { + await conv.TruncateConversation(conversationId, input.TruncateMessageId, inputMsg.MessageId); + } + var routing = _services.GetRequiredService(); routing.Context.SetMessageId(conversationId, inputMsg.MessageId); @@ -212,12 +212,15 @@ public class ConversationController : ControllerBase [FromBody] NewMessageModel input) { var conv = _services.GetRequiredService(); + var inputMsg = new RoleDialogModel(AgentRole.User, input.Text) + { + Files = input.Files + }; if (!string.IsNullOrEmpty(input.TruncateMessageId)) { - await conv.TruncateConversation(conversationId, input.TruncateMessageId); + await conv.TruncateConversation(conversationId, input.TruncateMessageId, inputMsg.MessageId); } - var inputMsg = new RoleDialogModel(AgentRole.User, input.Text); var routing = _services.GetRequiredService(); routing.Context.SetMessageId(conversationId, inputMsg.MessageId); diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/FileController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/FileController.cs index 84bb65b0..a24357a2 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/FileController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/FileController.cs @@ -44,12 +44,11 @@ public class FileController : ControllerBase return fileService.GetConversationFiles(conversationId, messageId); } - [HttpGet("/conversation/{conversationId}/message/{messageId}/file/{fileName}/type/{type}")] - public async Task GetMessageFile([FromRoute] string conversationId, [FromRoute] string messageId, - [FromRoute] string fileName, [FromRoute] string type) + [HttpGet("/conversation/{conversationId}/message/{messageId}/file/{fileName}")] + public async Task GetMessageFile([FromRoute] string conversationId, [FromRoute] string messageId, [FromRoute] string fileName) { var fileService = _services.GetRequiredService(); - var file = fileService.GetMessageFile(conversationId, messageId, fileName, type); + var file = fileService.GetMessageFile(conversationId, messageId, fileName); if (string.IsNullOrEmpty(file)) { return NotFound(); diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/WebSocketsMiddleware.cs b/src/Plugins/BotSharp.Plugin.ChatHub/WebSocketsMiddleware.cs index 4194cc84..e20f8602 100644 --- a/src/Plugins/BotSharp.Plugin.ChatHub/WebSocketsMiddleware.cs +++ b/src/Plugins/BotSharp.Plugin.ChatHub/WebSocketsMiddleware.cs @@ -15,11 +15,12 @@ public class WebSocketsMiddleware public async Task Invoke(HttpContext httpContext) { var request = httpContext.Request;; - var messageFileRegex = new Regex(@"/conversation/[a-z0-9_.-]+/message/[a-z0-9_.-]+/file/[a-z0-9_.-]+/type/[a-z0-9_.-]+", RegexOptions.IgnoreCase); + var messageFileRegex = new Regex(@"/conversation/[a-z0-9-]+/message/[a-z0-9-]+/file/[a-z0-9-]+", RegexOptions.IgnoreCase); // web sockets cannot pass headers so we must take the access token from query param and // add it to the header before authentication middleware runs - if ((request.Path.StartsWithSegments("/chatHub", StringComparison.OrdinalIgnoreCase) || messageFileRegex.IsMatch(request.Path.Value ?? string.Empty)) && + if ((request.Path.StartsWithSegments("/chatHub", StringComparison.OrdinalIgnoreCase) + || messageFileRegex.IsMatch(request.Path.Value ?? string.Empty)) && request.Query.TryGetValue("access_token", out var accessToken)) { request.Headers["Authorization"] = $"Bearer {accessToken}"; diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs index ed15803e..235be196 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs @@ -1,4 +1,5 @@ using BotSharp.Abstraction.Conversations.Models; +using BotSharp.Abstraction.Files; using BotSharp.Abstraction.Repositories.Filters; using BotSharp.Abstraction.Repositories.Models; using BotSharp.Plugin.MongoStorage.Collections; @@ -411,16 +412,29 @@ public partial class MongoRepository return conversationIds.Take(batchSize).ToList(); } - public bool TruncateConversation(string conversationId, string messageId, bool cleanLog = false) + public IEnumerable TruncateConversation(string conversationId, string messageId, bool cleanLog = false) { - if (string.IsNullOrEmpty(conversationId) || string.IsNullOrEmpty(messageId)) return false; + var deletedMessageIds = new List(); + if (string.IsNullOrEmpty(conversationId) || string.IsNullOrEmpty(messageId)) + { + return deletedMessageIds; + } var dialogFilter = Builders.Filter.Eq(x => x.ConversationId, conversationId); var foundDialog = _dc.ConversationDialogs.Find(dialogFilter).FirstOrDefault(); - if (foundDialog == null || foundDialog.Dialogs.IsNullOrEmpty()) return false; + if (foundDialog == null || foundDialog.Dialogs.IsNullOrEmpty()) + { + return deletedMessageIds; + } var foundIdx = foundDialog.Dialogs.FindIndex(x => x.MetaData?.MessageId == messageId); - if (foundIdx < 0) return false; + if (foundIdx < 0) + { + return deletedMessageIds; + } + + deletedMessageIds = foundDialog.Dialogs.Where((x, idx) => idx >= foundIdx && !string.IsNullOrEmpty(x.MetaData?.MessageId)) + .Select(x => x.MetaData.MessageId).Distinct().ToList(); // Handle truncated dialogs var truncatedDialogs = foundDialog.Dialogs.Where((x, idx) => idx < foundIdx).ToList(); @@ -499,6 +513,6 @@ public partial class MongoRepository _dc.StateLogs.DeleteMany(stateLogBuilder.And(stateLogFilters)); } - return true; + return deletedMessageIds; } }