commit 2b054507468a449f5105c0b158008a65830c1ee9 Author: Hadi Khazaee Asl Date: Thu Jan 25 04:51:13 2024 +0330 Initial Commit ... diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1bd9d0b --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +# +# DotNet ... +bin +obj + +# +# Natural Docs ... +Documentation/* diff --git a/Configurations/.gitkeep b/Configurations/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/Constants/.gitkeep b/Constants/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/Constants/ConfigurationNodeNames.cs b/Constants/ConfigurationNodeNames.cs new file mode 100644 index 0000000..05f52da --- /dev/null +++ b/Constants/ConfigurationNodeNames.cs @@ -0,0 +1,5 @@ +namespace xMessageService.Constants { + public partial struct ConfigurationNodeNames { + public const string MESSAGE_NODE_NAME = "MessageConfiguration"; + } +} \ No newline at end of file diff --git a/Constants/xMessageServiceConstants.cs b/Constants/xMessageServiceConstants.cs new file mode 100644 index 0000000..fdb8877 --- /dev/null +++ b/Constants/xMessageServiceConstants.cs @@ -0,0 +1,5 @@ +namespace xMessageService.Constants { + public struct xMessageServiceConstants { + public const string DEFAULT_EMPTY_PROVIDER = "EMPTY"; + } +} \ No newline at end of file diff --git a/DI/XDIHelperExtension.cs b/DI/XDIHelperExtension.cs new file mode 100644 index 0000000..b912698 --- /dev/null +++ b/DI/XDIHelperExtension.cs @@ -0,0 +1,142 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using xCommons.Extensions; +using xExceptions.Constants; +using xMessageService.Constants; +using xMessageService.Interfaces; +using xMessageService.Models; +using xMessageService.Providers; + +namespace xMessageService.DI { + public static partial class XDIHelperExtension { + /// + /// Retrieve XMessage Configuration from App Settings + /// + /// + /// + public static XMessageConfiguration GetXMessageConfiguration (this IConfiguration config) { + // + var xMessageConfigSection = config.GetSection (ConfigurationNodeNames.MESSAGE_NODE_NAME); + var xMessageConfiguration = xMessageConfigSection.Get (); + + // + var defaultMailConfiguration = new Dictionary { + ["EMPTY"] = + new XMailConfiguration { + Title = xMessageServiceConstants.DEFAULT_EMPTY_PROVIDER, + Host = "", + Port = 25, + EnableSsl = false, + Username = "", + Password = "" + } + }; + + // + var defaultSmsConfiguration = new Dictionary { + ["EMPTY"] = + new XSmsConfiguration { + ServiceUrl = "", + Username = "", + Password = "", + LineNumber = "", + UserApiKey = "", + SecretKey = "", + } + }; + + // + if (xMessageConfiguration.IsNull ()) { + // + xMessageConfiguration = new XMessageConfiguration { + DefaultSMSProvider = xMessageServiceConstants.DEFAULT_EMPTY_PROVIDER, + DefaultMailProvider = xMessageServiceConstants.DEFAULT_EMPTY_PROVIDER, + MailConfigurations = defaultMailConfiguration, + SmsConfigurations = defaultSmsConfiguration + }; + + // + // Warn Message is Default Configuration ... + Console.WriteLine ($"IMPORTANT: xMessageService Configuration is Default, so the Service can't works properly ..."); + } + + // + // Handle Default Mail Provider ... + if (xMessageConfiguration.DefaultMailProvider.IsNullOrEmpty ()) { + // + if (xMessageConfiguration.IsNull () || + // + xMessageConfiguration.MailConfigurations.IsNull () || + xMessageConfiguration.MailConfigurations.Count == 0) { + // + Console.WriteLine ("There is no defined Mail Provider Configurations ..."); + xMessageConfiguration.DefaultMailProvider = xMessageServiceConstants.DEFAULT_EMPTY_PROVIDER; + } + + // + if (xMessageConfiguration.DefaultMailProvider == xMessageServiceConstants.DEFAULT_EMPTY_PROVIDER) { + xMessageConfiguration.MailConfigurations = defaultMailConfiguration; + } else { + xMessageConfiguration.DefaultMailProvider = xMessageConfiguration.MailConfigurations.Keys.First (); + } + } + + // + // Handle Default SMS Provider ... + if (xMessageConfiguration.DefaultSMSProvider.IsNullOrEmpty ()) { + // + if (xMessageConfiguration.IsNull () || + xMessageConfiguration.SmsConfigurations.IsNull () || + xMessageConfiguration.SmsConfigurations.Count == 0) { + // + Console.WriteLine ("There is no defined SMS Provider Configurations ..."); + xMessageConfiguration.DefaultSMSProvider = xMessageServiceConstants.DEFAULT_EMPTY_PROVIDER; + } + + // + if (xMessageConfiguration.DefaultSMSProvider == xMessageServiceConstants.DEFAULT_EMPTY_PROVIDER) { + xMessageConfiguration.SmsConfigurations = defaultSmsConfiguration; + } else { + xMessageConfiguration.DefaultSMSProvider = xMessageConfiguration.SmsConfigurations.Keys.First (); + } + } + + // + return xMessageConfiguration; + } + + /// + /// Register XMessage Service + /// + /// + /// + public static void AddXMessageService (this IServiceCollection services, IConfiguration config) { + // + var xMessageConfiguration = config.GetXMessageConfiguration (); + services.AddSingleton (xMessageConfiguration); + + // + // Register SMS Service ... + var xSmsService = new XSmsService ( + xMessageConfiguration.DefaultSMSProvider, + xMessageConfiguration.SmsConfigurations + ); + services.AddSingleton (xSmsService); + + // + // Register Mail Service ... + var xMailService = new XMailService ( + xMessageConfiguration.DefaultMailProvider, + xMessageConfiguration.MailConfigurations + ); + services.AddSingleton (xMailService); + + // + // Register MEssage Provider ... + services.AddSingleton (); + } + } +} \ No newline at end of file diff --git a/Extensions/.gitkeep b/Extensions/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/Helpers/.gitkeep b/Helpers/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/Interfaces/IXMailService.cs b/Interfaces/IXMailService.cs new file mode 100644 index 0000000..992005a --- /dev/null +++ b/Interfaces/IXMailService.cs @@ -0,0 +1,11 @@ +using System.Threading.Tasks; +using xMessageService.Models; + +namespace xMessageService.Interfaces { + public partial interface IXMailService { + Task SendMailAsync ( + XMessage message, + string provider = null + ); + } +} \ No newline at end of file diff --git a/Interfaces/IXMessageProvider.cs b/Interfaces/IXMessageProvider.cs new file mode 100644 index 0000000..ad0d7de --- /dev/null +++ b/Interfaces/IXMessageProvider.cs @@ -0,0 +1,26 @@ +using System; +using System.Threading.Tasks; +using xMessageService.Models; + +namespace xMessageService.Interfaces { + public partial interface IXMessageProvider { + bool IsReady ( + bool? checkMailService = false, + bool? checkSmsService = false + ); + + Task SendMailAsync ( + XMessage message, + string provider = null, + bool throwException = true, + Exception exception = null + ); + + Task SendSmsAsync ( + XMessage message, + string provider = null, + bool throwException = true, + Exception exception = null + ); + } +} \ No newline at end of file diff --git a/Interfaces/IXSmsService.cs b/Interfaces/IXSmsService.cs new file mode 100644 index 0000000..570341a --- /dev/null +++ b/Interfaces/IXSmsService.cs @@ -0,0 +1,11 @@ +using System.Threading.Tasks; +using xMessageService.Models; + +namespace xMessageService.Interfaces { + public partial interface IXSmsService { + Task SendSmsAsync ( + XMessage message, + string provider = null + ); + } +} \ No newline at end of file diff --git a/Models/XMailConfiguration.cs b/Models/XMailConfiguration.cs new file mode 100644 index 0000000..ac6ee94 --- /dev/null +++ b/Models/XMailConfiguration.cs @@ -0,0 +1,10 @@ +namespace xMessageService.Models { + public partial class XMailConfiguration { + public string Title { get; set; } + public string Host { get; set; } + public int Port { get; set; } + public bool EnableSsl { get; set; } + public string Username { get; set; } + public string Password { get; set; } + } +} \ No newline at end of file diff --git a/Models/XMessage.cs b/Models/XMessage.cs new file mode 100644 index 0000000..4f44124 --- /dev/null +++ b/Models/XMessage.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; +using xModels.Base; + +namespace xMessageService.Models { + public partial class XMessage : XBaseDto { + public HashSet Recievers { get; set; } = new HashSet (); + public string Message { get; set; } + } +} \ No newline at end of file diff --git a/Models/XMessageConfiguration.cs b/Models/XMessageConfiguration.cs new file mode 100644 index 0000000..da3a723 --- /dev/null +++ b/Models/XMessageConfiguration.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace xMessageService.Models { + public partial class XMessageConfiguration { + public string DefaultSMSProvider { get; set; } + public string DefaultMailProvider { get; set; } + public IDictionary MailConfigurations { get; set; } + public IDictionary SmsConfigurations { get; set; } + } +} \ No newline at end of file diff --git a/Models/XSmsConfiguration.cs b/Models/XSmsConfiguration.cs new file mode 100644 index 0000000..3eeaf55 --- /dev/null +++ b/Models/XSmsConfiguration.cs @@ -0,0 +1,10 @@ +namespace xMessageService.Models { + public partial class XSmsConfiguration { + public string ServiceUrl { get; set; } + public string Username { get; set; } + public string Password { get; set; } + public string LineNumber { get; set; } + public string UserApiKey { get; set; } + public string SecretKey { get; set; } + } +} \ No newline at end of file diff --git a/Providers/XMailService.cs b/Providers/XMailService.cs new file mode 100644 index 0000000..8942dc3 --- /dev/null +++ b/Providers/XMailService.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Mail; +using System.Threading.Tasks; +using xCommons.Extensions; +using xExceptions.Constants; +using xMessageService.Interfaces; +using xMessageService.Models; + +namespace xMessageService.Providers { + public partial class XMailService : IXMailService { + private SmtpClient smtpClient; + private readonly string defaultProvider; + private readonly IDictionary configurations; + + public XMailService ( + string defaultProvider, + IDictionary configurations + ) { + this.defaultProvider = defaultProvider; + this.configurations = configurations; + + // + // Get instance of SMTP Client ... + PrepareSmtpClient (); + } + + public Task SendMailAsync ( + XMessage message, + string provider = null + ) { + // + // Run Send Mail Asynchronous ... + return Task.Run (() => { + SendMail (message, provider); + }); + } + + #region Private ... + /// + /// Validate and Retrieve Provider Configuration for Service + /// + /// + /// + private XMailConfiguration GetConfiguration (string provider = null) { + // + if (provider.IsNullOrEmpty ()) { + provider = defaultProvider; + } + + // + if (!configurations.Keys.Contains (provider)) { + XException.MessageServiceInitialFailed.Throw (); + } + + // + return configurations[provider]; + } + + /// + /// Prepare SMTP Client by Using Specified/Default Provider + /// + /// + private void PrepareSmtpClient (string provider = null) { + // + var config = GetConfiguration (provider); + if (config.IsNull()) { + return; + } + + // + smtpClient = new SmtpClient () { + Host = config.Host, + Port = config.Port, + EnableSsl = config.EnableSsl, + UseDefaultCredentials = false, + Credentials = new NetworkCredential ( + config.Username, + config.Password) + }; + } + + /// + /// Prepare Mail Message With Specified/Default Provider + /// + /// + /// + /// + private MailMessage PrepareMailMessage ( + XMessage message, + string provider = null + ) { + // + if (message == null || + message.Message.IsNullOrEmpty () || + message.Recievers.Count == 0) { + XException.InvalidMessage.Throw (); + } + + // + var config = GetConfiguration (provider); + var mailMessage = new MailMessage () { + From = new MailAddress (config.Username), + Subject = config.Title, + Body = message.Message + }; + + // + foreach (var reciever in message.Recievers) { + mailMessage.To.Add (reciever); + } + + // + return mailMessage; + } + + /// + /// Send Mail Using Specified/Default Provider + /// + /// + /// + private void SendMail ( + XMessage message, + string provider = null + ) { + // + if (smtpClient.IsNull()) { + // + Console.WriteLine ($"SMTP Error: MessageServiceInitialFailed ..."); + XException.MessageServiceInitialFailed.Throw (); + } + + // + var mailMessage = PrepareMailMessage (message, provider); + + // + try { + smtpClient.Send (mailMessage); + } catch (Exception ex) { + // + Console.WriteLine ($"SMTP Error: {ex.Message}"); + XException.MessageServiceInitialFailed.Throw (); + } + } + #endregion + } +} \ No newline at end of file diff --git a/Providers/XMessageProvider.cs b/Providers/XMessageProvider.cs new file mode 100644 index 0000000..4612e81 --- /dev/null +++ b/Providers/XMessageProvider.cs @@ -0,0 +1,118 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using xCommons.Extensions; +using xExceptions.Constants; +using xMessageService.Interfaces; +using xMessageService.Models; + +namespace xMessageService.Providers { + public partial class XMessageProvider : IXMessageProvider { + private readonly XMessageConfiguration configuration; + private readonly ILogger logger; + private readonly IXSmsService smsService; + private readonly IXMailService mailService; + + public XMessageProvider ( + XMessageConfiguration configuration, + ILogger logger, + IXSmsService smsService, + IXMailService mailService + ) { + this.configuration = configuration; + this.logger = logger; + this.smsService = smsService; + this.mailService = mailService; + } + + /// + /// Send a Mail Message + /// + /// + /// + /// + /// + /// + public async Task SendMailAsync ( + XMessage message, + string provider = null, + bool throwException = true, + Exception exception = null + ) { + // + if (exception.IsNull ()) { + exception = XException.ActionFailed.ToException (); + } + + // + try { + await mailService.SendMailAsync (message, provider); + } catch (Exception ex) { + // + logger.LogError ($"Exeption: {ex.Message}"); + + // + if (throwException) { + throw exception; + } + } + } + + /// + /// Send SMS Message + /// + /// + /// + public async Task SendSmsAsync ( + XMessage message, + string provider = null, + bool throwException = true, + Exception exception = null + ) { + // + if (exception.IsNull ()) { + exception = XException.ActionFailed.ToException (); + } + + // + try { + await smsService.SendSmsAsync (message, provider); + } catch { + if (throwException) { + throw exception; + } + } + } + + /// + /// Check Message Provider is Ready or not + /// + /// + /// + /// + public bool IsReady ( + bool? checkMailService = false, + bool? checkSmsService = false + ) { + // + var result = true; + + // + // Check Mail Service ... + if (checkMailService.HasValue && + checkMailService.GetValueOrDefault ()) { + result = result && mailService != null; + } + + // + // Check Sms Service ... + if (checkSmsService.HasValue && + checkSmsService.GetValueOrDefault ()) { + result = result && smsService != null; + } + + // + return result; + } + } +} \ No newline at end of file diff --git a/Providers/XSmsService.cs b/Providers/XSmsService.cs new file mode 100644 index 0000000..0ce08c2 --- /dev/null +++ b/Providers/XSmsService.cs @@ -0,0 +1,181 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using PhoneNumbers; +using RestSharp; +using xCommons.Extensions; +using xExceptions.Constants; +using xMessageService.Interfaces; +using xMessageService.Models; + +namespace xMessageService.Providers { + public partial class XSmsService : IXSmsService { + private string serviceUrl; + private readonly string defaultProvider; + private readonly IDictionary configurations; + + public XSmsService ( + string defaultProvider, + IDictionary configurations + ) { + this.defaultProvider = defaultProvider; + this.configurations = configurations; + + // + PrepareServiceUrl (); + } + + public Task SendSmsAsync ( + XMessage message, + string provider = null + ) { + // + // Run Send SMS Asynchronous ... + return Task.Run (() => { + SendSMS (message, provider); + }); + } + + #region Private ... + /// + /// Prepare Service Url for Specified/Default Provider + /// + /// + private void PrepareServiceUrl (string provider = null) { + // + if (provider.IsNullOrEmpty ()) { + provider = defaultProvider; + } + + // + var config = GetConfiguration (provider); + if (config.IsNull ()) { + return; + } + + // + serviceUrl = config.ServiceUrl; + serviceUrl += "&username=" + config.Username + "&password=" + config.Password + "&"; + } + + /// + /// Prepare Service url for Integrating XMessage by + /// using Specified/Default Provider Configurations + /// + /// + /// + private void PrepareSmsMessage ( + XMessage message, + string provider = null + ) { + // + if (message == null || + message.Message.IsNullOrEmpty () || + message.Recievers.Count == 0) { + XException.InvalidMessage.Throw (); + } + + // + // Validating Mobile Numbers ... + var phoneNumberUtil = PhoneNumbers.PhoneNumberUtil.GetInstance (); + var recieversList = ""; + + // + PrepareServiceUrl (provider); + + // + foreach (var reciever in message.Recievers) { + try { + // + var mPhoneNumber = phoneNumberUtil.Parse (reciever, null); + if (phoneNumberUtil.IsValidNumber (mPhoneNumber)) { + // + // Format Phone Numbers to the '+989120000000' pattern ... + var recieverPhone = phoneNumberUtil.Format (mPhoneNumber, PhoneNumberFormat.E164); + if (recieversList.Length > 0) { + recieversList += ","; + } + recieversList += reciever; + } + } catch { } + } + + // + var config = GetConfiguration (); + + // + // Preparing All Content and Configurations for Sending Messages ... + serviceUrl += "to=" + + recieversList + + "&message=" + + message.Message + + "&from=" + + config.LineNumber + + "&flash=" + + false; + } + + /// + /// Send a Message as SMS by Specified/Default Provider + /// + /// + /// + private void SendSMS ( + XMessage message, + string provider = null + ) { + // + if (serviceUrl.IsNullOrEmpty ()) { + // + Console.WriteLine ($"SMS Error: MessageServiceInitialFailed ..."); + XException.MessageServiceInitialFailed.Throw (); + } + + // + try { + // + // preparing Service Url based on message data ... + PrepareSmsMessage (message, provider); + + // + // Implementing Service Calling Using RestSharp Library ... + // We Select RestSharp because it supports Sync/Async Calls ... + var client = new RestClient (serviceUrl); + var request = new RestRequest (Method.POST); + + // + // Send Request ... + var response = client.Execute (request); + } catch (Exception ex) { + Console.WriteLine ($"SMS Error: {ex.Message}"); + XException.MessageServiceInitialFailed.Throw (); + } + } + + /// + /// Validate and Retrieve Provider Configuration for Service + /// + /// + /// + private XSmsConfiguration GetConfiguration (string provider = null) { + // + if ( + provider.IsNullOrEmpty () || + provider.ToNormalString () == "empty" + ) { + // + provider = defaultProvider; + return null; + } + + // + if (!configurations.Keys.Contains (provider)) { + XException.MessageServiceInitialFailed.Throw (); + } + + // + return configurations[provider]; + } + #endregion + } +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..9863c8f --- /dev/null +++ b/README.md @@ -0,0 +1,23 @@ +# xMessageService + +it is a Part of xDashboard on SaherElm IT Center which provides: + +- SMTP Mailing Service. +- SMS Sending Service. +- Message Models. +- etc. + +this module has following dependencies : + +- xCommons +- xModels + +for configure and use this Module refer to DI.XDIHelperExtension.cs file. + +## Maintainer + +Hadi Khazaee asl + +[https://www.saherelm.ir](https://www.saherelm.ir) + +[hadi_khazaee_asl@yahoo.com](mailto:hadi_khazaee_asl@yahoo.com) diff --git a/nuget.config b/nuget.config new file mode 100644 index 0000000..87b6eb0 --- /dev/null +++ b/nuget.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/xMessageService.csproj b/xMessageService.csproj new file mode 100644 index 0000000..3c2cd98 --- /dev/null +++ b/xMessageService.csproj @@ -0,0 +1,34 @@ + + + + + netstandard2.0 + xDashboard.xMessageService + 1.0.0 + Hadi Khazaee Asl + SaherElm IT Center + + provide all required tools for sending mail and sms in xDashboard project. + + + + icon.png + + + + + + + + + + + + + + + + + + + \ No newline at end of file