From 2aa0607e8599ec481f6ff0674200fa45b8e0e167 Mon Sep 17 00:00:00 2001 From: Hadi Khazaee Asl Date: Mon, 28 Sep 2026 16:34:36 +0330 Subject: [PATCH] last ... --- Extensions/XAiModelsExtensions.cs | 59 ++- Interfaces/IXAiDataProvider.cs | 147 ++++++- Models/XAiResonseMessage.cs | 16 + Models/XAiResponseRequest.cs | 25 ++ Providers/XAiDataProvider.cs | 699 ++++++++++++++++++++++-------- 5 files changed, 758 insertions(+), 188 deletions(-) create mode 100644 Models/XAiResonseMessage.cs create mode 100644 Models/XAiResponseRequest.cs diff --git a/Extensions/XAiModelsExtensions.cs b/Extensions/XAiModelsExtensions.cs index 2d19367..2dfef6b 100644 --- a/Extensions/XAiModelsExtensions.cs +++ b/Extensions/XAiModelsExtensions.cs @@ -1,10 +1,12 @@ using System; using System.Linq; using xAiModels.Models; +using xModels.Extensions; using xCommons.Extensions; using xAiModels.Constants; using xAiModels.Models.Dtos; using xExceptions.Constants; +using Microsoft.Extensions.AI; using xAiModels.Models.Entities; using System.Collections.Generic; @@ -177,6 +179,22 @@ namespace xAiModels.Extensions return result; } + /// + /// Validate a Request for Retrieve Response in Controllers ... + /// + /// + /// + public static bool IsValid(this XAiResponseRequest source) + { + // + var result = + !source.IsNullOrDefault() && + !source.Prompt.IsNullOrEmpty(); + + // + return result; + } + /// /// Check a Collection Contains Default Project for Specified Owner or not ... /// @@ -211,7 +229,7 @@ namespace xAiModels.Extensions var result = !item.IsNullOrDefault(); // - return result; + return result; } /// @@ -253,7 +271,7 @@ namespace xAiModels.Extensions var result = !item.IsNullOrDefault(); // - return result; + return result; } /// @@ -293,7 +311,7 @@ namespace xAiModels.Extensions // return item.Id; } - + /// /// Get Default Conversation Id from a Collection Contains ... /// @@ -333,7 +351,38 @@ namespace xAiModels.Extensions var result = item.Id; // - return result; - } + return result; + } + + public static ChatMessage ToChatMessages( + this XAiMessageDto source + ) + { + // + // TODO: Handle Files Attached here ... + + // + ChatMessage result = null; + + // + if (!source.IsNullOrDefault()) + { + // + result = new ChatMessage + { + AuthorName = + source.Role == XAiChatRole.User && + !source.Owner.IsNullOrDefault() + ? source.Owner.GetFullname() + : string.Empty, + Role = source.Role.ToChatRole(), + MessageId = source.Id.ToString(), + Contents = [new TextContent(source.Content)] + }; + } + + // + return result; + } } } \ No newline at end of file diff --git a/Interfaces/IXAiDataProvider.cs b/Interfaces/IXAiDataProvider.cs index 5f6f337..f8aa942 100644 --- a/Interfaces/IXAiDataProvider.cs +++ b/Interfaces/IXAiDataProvider.cs @@ -11,18 +11,51 @@ namespace xAiModels.Interfaces /// public interface IXAiDataProvider { + // + #region Getters ... + /// + /// Retrieve Specified Project ... + /// + /// + /// + /// + /// + /// + Task GetProject( + Guid id, + bool fillConversations = false, + bool fillConversationMessages = false, + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve Specified Conversation ... + /// + /// + /// + /// + /// + Task GetConversation( + Guid id, + bool fillMessages = false, + CancellationToken cancellationToken = default + ); + #endregion + // #region Loaders ... /// /// Load Projects List of Specified Owner ... /// /// + /// /// /// /// /// Task> LoadProjects( string ownerId, + string connectionId = null, bool fillConversations = false, bool fillConversationMessages = false, CancellationToken cancellationToken = default @@ -33,11 +66,13 @@ namespace xAiModels.Interfaces /// /// /// + /// /// /// Task> LoadConversations( Guid projectId, bool fillMessages = false, + string connectionId = null, CancellationToken cancellationToken = default ); @@ -54,7 +89,117 @@ namespace xAiModels.Interfaces #endregion // - #region Default Identifiers ... + #region Enrichers ... + /// + /// Enrich Specified Item ... + /// + /// + /// + /// + /// + /// + Task Enrich( + XAiProjectDto source, + bool fillConversations = false, + bool fillConversationMessages = false, + CancellationToken cancellationToken = default + ); + + /// + /// Enrich Specified Items ... + /// + /// + /// + /// + /// + /// + Task> EnrichRange( + IEnumerable sources, + bool fillConversations = false, + bool fillConversationMessages = false, + CancellationToken cancellationToken = default + ); + + /// + /// Enrich Specified Item ... + /// + /// + /// + /// + /// + Task Enrich( + XAiConversationDto source, + bool fillMessages = false, + CancellationToken cancellationToken = default + ); + + /// + /// Enrich Specified Items ... + /// + /// + /// + /// + /// + Task> EnrichRange( + IEnumerable sources, + bool fillMessages = false, + CancellationToken cancellationToken = default + ); + + /// + /// Enrich Specified Item ... + /// + /// + /// + /// + Task Enrich( + XAiMessageDto source, + CancellationToken cancellationToken = default + ); + + /// + /// Enrich Specified Items ... + /// + /// + /// + /// + Task> EnrichRange( + IEnumerable sources, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Defaults ... + /// + /// Retrieve Default Project of Specified Owner ... + /// Create it if not Handled ... + /// + /// + /// + /// + /// + Task GetDefaultProject( + string ownerId, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve Default Conversation of Specified Owner ... + /// Create it if not Handled ... + /// + /// + /// + /// + /// + /// + Task GetDefaultConversation( + string ownerId, + Guid projectId, + string connectionId = null, + CancellationToken cancellationToken = default + ); #endregion // diff --git a/Models/XAiResonseMessage.cs b/Models/XAiResonseMessage.cs new file mode 100644 index 0000000..c9450f3 --- /dev/null +++ b/Models/XAiResonseMessage.cs @@ -0,0 +1,16 @@ +using System; +using xModels.Base; +using xAiModels.Constants; + +namespace xAiModels.Models +{ + public class XAiResonseMessage : XBaseDto + { + public long Sequence { get; set; } + public Guid RespondTo { get; set; } + public string OwnerId { get; set; } + public string Content { get; set; } + public XAiChatRole Role { get; set; } + public DateTime RespondedOn { get; set; } + } +} \ No newline at end of file diff --git a/Models/XAiResponseRequest.cs b/Models/XAiResponseRequest.cs new file mode 100644 index 0000000..f700bd1 --- /dev/null +++ b/Models/XAiResponseRequest.cs @@ -0,0 +1,25 @@ +using System; + +namespace xAiModels.Models +{ + /// + /// /Request for a Response in Controllers ... + /// + public class XAiResponseRequest + { + /// + /// Prompt Text ... + /// + public string Prompt { get; set; } + + /// + /// Specified Project Id ... + /// + public Guid ProjectId { get; set; } + + /// + /// Specified Conversation Id ... + /// + public Guid ConversationId { get; set; } + } +} \ No newline at end of file diff --git a/Providers/XAiDataProvider.cs b/Providers/XAiDataProvider.cs index 637208a..1376d1f 100644 --- a/Providers/XAiDataProvider.cs +++ b/Providers/XAiDataProvider.cs @@ -15,6 +15,7 @@ using xAiModels.Interfaces.Dtos; using xStringService.Interfaces; using System.Collections.Generic; using Microsoft.AspNetCore.SignalR; +using xAiModels.Interfaces.Enrichers; namespace xAiModels.Providers { @@ -28,6 +29,9 @@ namespace xAiModels.Providers private readonly IXStringProvider stringProvider; private readonly XAppConfiguration appConfiguration; private readonly IHubContext hub; + private readonly IXAiMessageEnricher messageEnricher; + private readonly IXAiProjectEnricher projectEnricher; + private readonly IXAiConversationEnricher conversationEnricher; private readonly IXAiMessageRepositoryService messageRepositoryService; private readonly IXAiProjectRepositoryService projectRepositoryService; private readonly IXAiProjectTitleResourceProvider projectTitleResourceProvider; @@ -40,6 +44,9 @@ namespace xAiModels.Providers IXStringProvider stringProvider, XAppConfiguration appConfiguration, IHubContext hub, + IXAiProjectEnricher projectEnricher, + IXAiMessageEnricher messageEnricher, + IXAiConversationEnricher conversationEnricher, IXAiMessageRepositoryService messageRepositoryService, IXAiProjectRepositoryService projectRepositoryService, IXAiProjectTitleResourceProvider projectTitleResourceProvider, @@ -50,7 +57,10 @@ namespace xAiModels.Providers { this.hub = hub; this.stringProvider = stringProvider; + this.projectEnricher = projectEnricher; + this.messageEnricher = messageEnricher; this.appConfiguration = appConfiguration; + this.conversationEnricher = conversationEnricher; this.messageRepositoryService = messageRepositoryService; this.projectRepositoryService = projectRepositoryService; this.projectTitleResourceProvider = projectTitleResourceProvider; @@ -65,12 +75,14 @@ namespace xAiModels.Providers /// Load Projects List of Specified Owner ... /// /// + /// /// /// /// /// public async Task> LoadProjects( string ownerId, + string connectionId = null, bool fillConversations = false, bool fillConversationMessages = false, CancellationToken cancellationToken = default @@ -85,6 +97,9 @@ namespace xAiModels.Providers // // Handle Default Project and Conversation ... + + // + // Default Project ... bool hasDefault = await HasDefaultProject( ownerId: ownerId, cancellationToken: cancellationToken @@ -94,7 +109,30 @@ namespace xAiModels.Providers // await CreateDefaultProject( ownerId: ownerId, - connectionId: null, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + var defaultProject = await GetDefaultProject( + ownerId: ownerId, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + // Default Project's Default Conversation ... + hasDefault = await HasDefaultConversation( + ownerId: ownerId, + projectId: defaultProject.Id, + cancellationToken: cancellationToken + ); + if (!hasDefault) + { + // + await CreateDefaultConversation( + ownerId: ownerId, + connectionId: connectionId, + projectId: defaultProject.Id, cancellationToken: cancellationToken ); } @@ -109,6 +147,18 @@ namespace xAiModels.Providers orderBuilder: x => x.OrderBy(y => y.CreatedOn) ); + // + if (result.HasChild()) + { + // + result = await projectEnricher.EnrichRange( + sources: result, + cancellationToken: cancellationToken, + fillConversations: fillConversations, + fillConversationMessages: fillConversationMessages + ); + } + // return result; } @@ -117,12 +167,14 @@ namespace xAiModels.Providers /// Load List of Conversations for Specified Project ... /// /// + /// /// /// /// public async Task> LoadConversations( Guid projectId, bool fillMessages = false, + string connectionId = null, CancellationToken cancellationToken = default ) { @@ -135,6 +187,38 @@ namespace xAiModels.Providers XException.InvalidArgs.Throw(); } + // + // Retrieve Project ... + var project = await projectRepositoryService.GetAsync( + id: projectId, + includeBuilder: null, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken + ); + if (project.IsNullOrDefault()) + { + XException.NotFound.Throw(); + } + + // + // Default Conversation ... + var hasDefault = await HasDefaultConversation( + projectId: projectId, + ownerId: project.OwnerId, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + if (!hasDefault) + { + // + await CreateDefaultConversation( + projectId: projectId, + ownerId: project.OwnerId, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + // // Retrieve Conversation's List ... var result = await conversationRepositoryService.FindManyAsync( @@ -145,6 +229,17 @@ namespace xAiModels.Providers orderBuilder: x => x.OrderBy(y => y.CreatedOn) ); + // + if (result.HasChild()) + { + // + result = await conversationEnricher.EnrichRange( + sources: result, + fillMessages: fillMessages, + cancellationToken: cancellationToken + ); + } + // return result; } @@ -169,6 +264,18 @@ namespace xAiModels.Providers XException.InvalidArgs.Throw(); } + // + var conversation = await conversationRepositoryService.GetAsync( + id: conversationId, + includeBuilder: null, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken + ); + if (conversation.IsNullOrDefault()) + { + XException.NotFound.Throw(); + } + // // Retrieve Message Lists of Specified Conversation ... var result = await messageRepositoryService.FindManyAsync( @@ -179,6 +286,110 @@ namespace xAiModels.Providers predicate: x => x.ConversationId == conversationId ); + // + if (result.HasChild()) + { + // + result = await messageEnricher.EnrichRange( + sources: result, + cancellationToken: cancellationToken + ); + } + + // + return result; + } + #endregion + + // + #region Getters ... + /// + /// Retrieve Specified Project ... + /// + /// + /// + /// + /// + /// + public async Task GetProject( + Guid id, + bool fillConversations = false, + bool fillConversationMessages = false, + CancellationToken cancellationToken = default + ) + { + // + // Validate Args ... + if (id.IsDefaultGuid()) + { + XException.InvalidArgs.Throw(); + } + + // + var result = await projectRepositoryService.GetAsync( + id: id, + includeBuilder: null, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken + ); + if (result.IsNullOrDefault()) + { + XException.NotFound.Throw(); + } + + // + // Handle Enrichment ... + result = await projectEnricher.Enrich( + source: result, + fillConversations: fillConversations, + cancellationToken: cancellationToken, + fillConversationMessages: fillConversationMessages + ); + + // + return result; + } + + /// + /// Retrieve Specified Conversation ... + /// + /// + /// + /// + /// + public async Task GetConversation( + Guid id, + bool fillMessages = false, + CancellationToken cancellationToken = default + ) + { + // + // Validate Args ... + if (id.IsDefaultGuid()) + { + XException.InvalidArgs.Throw(); + } + + // + var result = await conversationRepositoryService.GetAsync( + id: id, + includeBuilder: null, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken + ); + if (result.IsNullOrDefault()) + { + XException.NotFound.Throw(); + } + + // + // Handle Enrichment ... + result = await conversationEnricher.Enrich( + source: result, + fillMessages: fillMessages, + cancellationToken: cancellationToken + ); + // return result; } @@ -467,48 +678,7 @@ namespace xAiModels.Providers #endregion // - #region Private ... - /// - /// Check Default Project Exists or not ... - /// - /// - /// - /// - /// - private async Task HasDefaultProject( - string ownerId, - string connectionId = null, - CancellationToken cancellationToken = default - ) - { - // - // Validate Args ... - var result = !ownerId.IsNullOrEmpty(); - if (!result) - { - return result; - } - - // - // Check Default Owner Exists or not ... - result = !(await stringProvider.FindOne( - cancellationToken: cancellationToken, - predicate: x => - x.ResourceTitle - .ToNormalString() - .Contains(ownerId.ToNormalString()) && - x.ResourceTitle - .ToNormalString() - .Contains(XAiModelsConstants.XAiProjectTitleIdentifier - .ToNormalString()) && - x.TranslatedValue - .ToNormalString() - .Contains(XAiDataProviderConstants.XDefaultAiProjectSuffix) - )) - .IsNullOrDefault(); - return result; - } - + #region Defaults ... /// /// Retrieve Default Project of Specified Owner ... /// Create it if not Handled ... @@ -517,7 +687,7 @@ namespace xAiModels.Providers /// /// /// - private async Task GetDefaultProject( + public async Task GetDefaultProject( string ownerId, string connectionId = null, CancellationToken cancellationToken = default @@ -527,7 +697,6 @@ namespace xAiModels.Providers // Check Default Project Exists or noit ... var isExists = await HasDefaultProject( ownerId: ownerId, - connectionId: connectionId, cancellationToken: cancellationToken ); if (!isExists) @@ -578,6 +747,309 @@ namespace xAiModels.Providers return result; } + /// + /// Retrieve Default Conversation of Specified Owner ... + /// Create it if not Handled ... + /// + /// + /// + /// + /// + /// + public async Task GetDefaultConversation( + string ownerId, + Guid projectId, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate Args ... + if (ownerId.IsNullOrEmpty()) + { + XException.InvalidArgs.Throw(); + } + + // + // Prepare Project ... + XAiProjectDto project = null; + if (projectId.IsNull() || + projectId.IsDefaultGuid() + ) + { + // + project = await GetDefaultProject( + ownerId: ownerId, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + else + { + // + project = await projectRepositoryService.GetAsync( + id: project.Id, + includeBuilder: null, + ignoreSoftDeleteds: true, + // fillConversations: false, + // fillConversationMessages: false, + cancellationToken: cancellationToken + ); + } + if (project.IsNullOrDefault()) + { + XException.NotFound.Throw(); + } + + // + // Check Default Conversation Exists or noit ... + var isExists = await HasDefaultConversation( + ownerId: ownerId, + projectId: project.Id, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + if (!isExists) + { + // + await CreateDefaultConversation( + ownerId: ownerId, + projectId: project.Id, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + + // + // Detect Conversation Resource ... + var resource = await stringProvider.FindOne( + cancellationToken: cancellationToken, + predicate: x => + x.ResourceTitle + .ToNormalString() + .Contains(ownerId.ToNormalString()) && + x.ResourceTitle + .ToNormalString() + .Contains(XAiModelsConstants.XAiProjectTitleIdentifier + .ToNormalString()) && + x.TranslatedValue + .ToNormalString() + .Contains(XAiDataProviderConstants.XDefaultAiProjectSuffix) + ); + if (resource.IsNullOrDefault()) + { + XException.NotFound.Throw(); + } + + // + // Find Default Conversation ... + var result = await conversationRepositoryService.FindOneAsync( + cancellationToken: cancellationToken, + predicate: x => x.Title + .Equals( + resource.ResourceTitle, + StringComparison.InvariantCultureIgnoreCase + ) && + x.ProjectId == project.Id + ); + if (result.IsNullOrDefault()) + { + XException.NotFound.Throw(); + } + + // + return result; + } + #endregion + + // + #region Enrichers ... + /// + /// Enrich Specified Item ... + /// + /// + /// + /// + /// + /// + public async Task Enrich( + XAiProjectDto source, + bool fillConversations = false, + bool fillConversationMessages = false, + CancellationToken cancellationToken = default + ) + { + // + var result = await projectEnricher.Enrich( + source: source, + fillConversations: fillConversations, + cancellationToken: cancellationToken, + fillConversationMessages: fillConversationMessages + ); + + // + return result; + } + + /// + /// Enrich Specified Items ... + /// + /// + /// + /// + /// + /// + public async Task> EnrichRange( + IEnumerable sources, + bool fillConversations = false, + bool fillConversationMessages = false, + CancellationToken cancellationToken = default + ) + { + // + var result = await projectEnricher.EnrichRange( + sources: sources, + fillConversations: fillConversations, + cancellationToken: cancellationToken, + fillConversationMessages: fillConversationMessages + ); + + // + return result; + } + + /// + /// Enrich Specified Item ... + /// + /// + /// + /// + /// + public async Task Enrich( + XAiConversationDto source, + bool fillMessages = false, + CancellationToken cancellationToken = default + ) + { + // + var result = await conversationEnricher.Enrich( + source: source, + fillMessages: fillMessages, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Enrich Specified Items ... + /// + /// + /// + /// + /// + public async Task> EnrichRange( + IEnumerable sources, + bool fillMessages = false, + CancellationToken cancellationToken = default + ) + { + // + var result = await conversationEnricher.EnrichRange( + sources: sources, + fillMessages: fillMessages, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Enrich Specified Item ... + /// + /// + /// + /// + public async Task Enrich( + XAiMessageDto source, + CancellationToken cancellationToken = default + ) + { + // + var result = await messageEnricher.Enrich( + source: source, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Enrich Specified Items ... + /// + /// + /// + /// + public async Task> EnrichRange( + IEnumerable sources, + CancellationToken cancellationToken = default + ) + { + // + var result = await messageEnricher.EnrichRange( + sources: sources, + cancellationToken: cancellationToken + ); + + // + return result; + } + #endregion + + // + #region Private ... + /// + /// Check Default Project Exists or not ... + /// + /// + /// + /// + private async Task HasDefaultProject( + string ownerId, + CancellationToken cancellationToken = default + ) + { + // + // Validate Args ... + var result = !ownerId.IsNullOrEmpty(); + if (!result) + { + return result; + } + + // + // Check Default Owner Exists or not ... + result = !(await stringProvider.FindOne( + cancellationToken: cancellationToken, + predicate: x => + x.ResourceTitle + .ToNormalString() + .Contains(ownerId.ToNormalString()) && + x.ResourceTitle + .ToNormalString() + .Contains(XAiModelsConstants.XAiProjectTitleIdentifier + .ToNormalString()) && + x.TranslatedValue + .ToNormalString() + .Contains(XAiDataProviderConstants.XDefaultAiProjectSuffix) + )) + .IsNullOrDefault(); + return result; + } + /// /// Create Default Project for Specified Owner ... /// @@ -890,143 +1362,6 @@ namespace xAiModels.Providers } } - /// - /// Retrieve Default Conversation of Specified Owner ... - /// Create it if not Handled ... - /// - /// - /// - /// - /// - /// - private async Task GetDefaultConversation( - string ownerId, - Guid projectId, - string connectionId = null, - CancellationToken cancellationToken = default - ) - { - // - // Validate Args ... - if (ownerId.IsNullOrEmpty()) - { - XException.InvalidArgs.Throw(); - } - - // - // Prepare Project ... - XAiProjectDto project = null; - if (projectId.IsNull() || - projectId.IsDefaultGuid() - ) - { - // - project = await GetDefaultProject( - ownerId: ownerId, - connectionId: connectionId, - cancellationToken: cancellationToken - ); - } - else - { - // - project = await projectRepositoryService.GetAsync( - id: project.Id, - includeBuilder: null, - ignoreSoftDeleteds: true, - // fillConversations: false, - // fillConversationMessages: false, - cancellationToken: cancellationToken - ); - } - if (project.IsNullOrDefault()) - { - XException.NotFound.Throw(); - } - - // - // Check Default Conversation Exists or noit ... - var isExists = await HasDefaultConversation( - ownerId: ownerId, - projectId: project.Id, - connectionId: connectionId, - cancellationToken: cancellationToken - ); - if (!isExists) - { - // - await CreateDefaultConversation( - ownerId: ownerId, - projectId: project.Id, - connectionId: connectionId, - cancellationToken: cancellationToken - ); - } - - // - // Detect Conversation Resource ... - var resource = await stringProvider.FindOne( - cancellationToken: cancellationToken, - predicate: x => - x.ResourceTitle - .ToNormalString() - .Contains(ownerId.ToNormalString()) && - x.ResourceTitle - .ToNormalString() - .Contains(XAiModelsConstants.XAiProjectTitleIdentifier - .ToNormalString()) && - x.TranslatedValue - .ToNormalString() - .Contains(XAiDataProviderConstants.XDefaultAiProjectSuffix) - ); - if (resource.IsNullOrDefault()) - { - XException.NotFound.Throw(); - } - - // - // Find Default Conversation ... - var result = await conversationRepositoryService.FindOneAsync( - cancellationToken: cancellationToken, - predicate: x => x.Title - .Equals( - resource.ResourceTitle, - StringComparison.InvariantCultureIgnoreCase - ) && - x.ProjectId == project.Id - ); - if (result.IsNullOrDefault()) - { - XException.NotFound.Throw(); - } - - // - return result; - } - - /// - /// Normalize and Validate Language ... - /// - /// - /// - private string NormalizeAndValidateLanguage(string language) - { - // - if (language.IsNullOrEmpty()) - { - language = appConfiguration.DefaultLanguage; - } - - // - if (language.IsNullOrEmpty()) - { - XException.InvalidArgs.Throw(); - } - - // - return language; - } - /// /// Send Custom Push Message ... ///