Skip to content

Commit 2c4cece

Browse files
author
Ruben Bisharyan
committed
Refactoring with SMS/Email responses and more
1 parent 45043f1 commit 2c4cece

File tree

16 files changed

+388
-122
lines changed

16 files changed

+388
-122
lines changed

Readme.md

Lines changed: 106 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@
77
- [1.3.1. Program.cs Example](#131-programcs-example)
88
- [1.3.2. Appsettings.json Example](#132-appsettingsjson-example)
99
- [1.4. Configuration Options Explained](#14-configuration-options-explained)
10-
- [1.5. Limitations](#15-limitations)
11-
- [1.6. License](#16-license)
10+
- [1.5. Usage](#15-usage)
11+
- [1.5.1. Send SMS message](#151-send-sms-message)
12+
- [1.5.2. Send Email message](#152-send-email-message)
13+
- [1.6. Limitations](#16-limitations)
14+
- [1.7. License](#17-license)
1215

1316
## 1.1. Introduction
1417
**PandaTech.Communicator** is aimed to send Email and SMS messages to the clients of your service where you use this library.
@@ -191,10 +194,109 @@ The configuration options in `appsettings.json` and `program.cs` (priority) are
191194
- **EmailFake:** This setup is for fake Email service to be used which doesn't send real SMS messages and just return `TTask.CompletedTask`.
192195
- **EmailConfigurations:** Email configurations given by `appsettings.json` or via `builder.AddCommunicator()` options for Email.
193196

194-
## 1.5. Limitations
197+
## 1.5. Usage
198+
199+
In order to use the library, you need to generate `SmsMessage` or `EmailMessage` and use one of the interfaces mentioned above for the service you need to use.
200+
201+
Both of them support multiple recipients.
202+
203+
The structure of the messages are shown below.
204+
205+
```csharp
206+
public class SmsMessage
207+
{
208+
public List<string> Recipients { get; set; } = null!;
209+
public string Message { get; set; } = null!;
210+
public string Channel { get; set; } = null!;
211+
}
212+
213+
public class EmailMessage
214+
{
215+
public List<string> Recipients { get; set; } = null!;
216+
public string Subject { get; set; } = null!;
217+
public string Body { get; set; } = null!;
218+
public List<string> Cc { get; set; } = [];
219+
public List<string> Bcc { get; set; } = [];
220+
public List<EmailAttachment> Attachments { get; set; } = [];
221+
public bool IsBodyHtml { get; set; } = false;
222+
public string Channel { get; set; } = null!;
223+
}
224+
```
225+
226+
Channel is set by `EmailChannels` or by `SmsChannels` classes with constant values.
227+
228+
There are 2 interfaces `ISmsService` and `IEmailService` responsible for individual cases SMS or Email.
229+
Methods for sending SMS/Email messages are:
230+
- **SendAsync:**
231+
- **SendBulkAsync:**
232+
233+
The structure of the service interfaces are shown below.
234+
235+
```csharp
236+
public interface ISmsService
237+
{
238+
Task<List<GeneralSmsResponse>> SendAsync(SmsMessage smsMessage, CancellationToken cancellationToken = default);
239+
Task<List<GeneralSmsResponse>> SendBulkAsync(List<SmsMessage> smsMessageList, CancellationToken cancellationToken = default);
240+
}
241+
242+
public interface IEmailService
243+
{
244+
Task SendAsync(EmailMessage emailMessage, CancellationToken cancellationToken = default);
245+
Task SendBulkAsync(List<EmailMessage> emailMessages, CancellationToken cancellationToken = default);
246+
}
247+
```
248+
249+
## 1.5.1. Send SMS message
250+
251+
```csharp
252+
var sms = new SmsMessage
253+
{
254+
Recipients = ["+37493910593"],
255+
Message = "123456",
256+
Channel = SmsChannels.GeneralSender
257+
};
258+
await smsService.SendAsync(sms);
259+
```
260+
261+
Sms service return as well response for each service individually (Not generic), which can be used in the code if you need to store related information into the Database.
262+
263+
The responses are:
264+
- **DexatelSmsSendResponse**
265+
- **TwilioSmsSendResponse**
266+
267+
Both responses return with list (ex. List<GeneralSmsResponse>) when you use any of the methods to send sms.
268+
269+
```csharp
270+
public class GeneralSmsResponse
271+
{
272+
public string From { get; set; } = null!;
273+
public string To { get; set; } = null!;
274+
public string OuterSmsId { get; set; } = null!;
275+
public string Status { get; set; } = null!;
276+
public DateTime CreateDate { get; set; }
277+
public DateTime UpdateDate { get; set; }
278+
public string Body { get; set; } = null!;
279+
}
280+
```
281+
282+
## 1.5.2. Send Email message
283+
284+
```csharp
285+
var email = new EmailMessage
286+
{
287+
Recipients = ["[email protected]"],
288+
Subject = "Some subject",
289+
Body = "Some body",
290+
IsBodyHtml = false,
291+
Channel = EmailChannels.GeneralSender
292+
};
293+
await emailService.SendAsync(email);
294+
```
295+
296+
## 1.6. Limitations
195297

196298
PandaTech.Communicator works with all Email providers, but with only with existing integrations for SMS listed above.
197299

198-
## 1.6. License
300+
## 1.7. License
199301

200302
PandaTech.Communicator is licensed under the MIT License.

src/Communicator.Demo/Program.cs

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Communicator.Demo.MessageTemplates;
2+
using Communicator.Enums;
23
using Communicator.Extensions;
34
using Communicator.Models;
45
using Communicator.Options;
@@ -83,15 +84,15 @@
8384
Subject = "Some subject",
8485
Body = "Some body",
8586
IsBodyHtml = false,
86-
Channel = "GeneralSender"
87+
Channel = EmailChannels.GeneralSender
8788
};
8889
await emailService.SendAsync(email);
8990

9091
var sms = new SmsMessage
9192
{
9293
Recipients = ["+37493910593"],
93-
Message = SmsTemplates.OtpCodeVerificationRequestMessage("123456"),
94-
Channel = "GeneralSender"
94+
Message = "123456",
95+
Channel = SmsChannels.GeneralSender
9596
};
9697
await smsService.SendAsync(sms);
9798
return Results.Ok("Email and SMS sent successfully.");
@@ -105,15 +106,15 @@
105106
Subject = "Some subject",
106107
Body = "Some body",
107108
IsBodyHtml = false,
108-
Channel = "GeneralSender"
109+
Channel = EmailChannels.GeneralSender
109110
};
110111
await emailService.SendAsync(email);
111112

112113
var sms = new SmsMessage
113114
{
114115
Recipients = ["+37493910593", "+37493910593", "+37493910594", "+37493910595"],
115116
Message = "Barev erjankutyun",
116-
Channel = "GeneralSender"
117+
Channel = SmsChannels.GeneralSender
117118
};
118119
await smsService.SendAsync(sms);
119120
return Results.Ok("Email and SMS sent successfully.");
@@ -127,12 +128,26 @@
127128
Subject = "Some subject",
128129
Body = EmailTemplates.AddEmailAddressTemplate("Test", "Test", "https://www.google.com/"),
129130
IsBodyHtml = true,
130-
Channel = "GeneralSender"
131+
Channel = EmailChannels.GeneralSender
131132
};
132133
await emailService.SendAsync(email);
133134
return Results.Ok("Email sent successfully.");
134135
});
135136

137+
app.MapGet("/send/email/html-body/with-response", async (IEmailService emailService) =>
138+
{
139+
var email = new EmailMessage
140+
{
141+
Recipients = ["[email protected]"],
142+
Subject = "Some subject",
143+
Body = EmailTemplates.AddEmailAddressTemplate("Test", "Test", "https://www.google.com/"),
144+
IsBodyHtml = true,
145+
Channel = EmailChannels.GeneralSender
146+
};
147+
var response = await emailService.SendAsync(email);
148+
return Results.Ok(response);
149+
});
150+
136151
app.MapGet("/send/email/multiple-recipients/html-body", async (IEmailService emailService) =>
137152
{
138153
var email = new EmailMessage
@@ -141,7 +156,7 @@
141156
Subject = "Some subject",
142157
Body = EmailTemplates.AddEmailAddressTemplate("Test", "Test", "https://www.google.com/"),
143158
IsBodyHtml = true,
144-
Channel = "GeneralSender"
159+
Channel = EmailChannels.GeneralSender
145160
};
146161
await emailService.SendAsync(email);
147162
return Results.Ok("Email sent successfully.");
@@ -153,7 +168,7 @@
153168
{
154169
Recipients = ["+37493910593", "+37493910593", "+37493910594", "+37493910595"],
155170
Message = "Barev erjankutyun",
156-
Channel = "GeneralSender"
171+
Channel = SmsChannels.GeneralSender
157172
};
158173
await smsService.SendAsync(sms);
159174
return Results.Ok("SMS sent successfully.");
@@ -165,10 +180,47 @@
165180
{
166181
Recipients = ["+37495988247"],
167182
Message = "Barev erjankutyun",
168-
Channel = "TransactionalSender"
183+
Channel = SmsChannels.TransactionalSender
169184
};
170185
await smsService.SendAsync(sms);
171186
return Results.Ok("SMS sent successfully.");
172187
});
173188

189+
app.MapGet("/send/bulk/email/html-body", async (IEmailService emailService) =>
190+
{
191+
var emailMessageList = new List<EmailMessage>();
192+
for (var i = 0; i < 5; i++)
193+
{
194+
var random = new Random();
195+
emailMessageList.Add(new EmailMessage
196+
{
197+
Recipients = ["[email protected]", "[email protected]", "[email protected]", $"a{random.Next(1, 5)}@a.com"],
198+
Subject = "Some subject",
199+
Body = EmailTemplates.AddEmailAddressTemplate("Test", "Test", "https://www.google.com/"),
200+
IsBodyHtml = true,
201+
Channel = EmailChannels.GeneralSender
202+
});
203+
}
204+
205+
await emailService.SendBulkAsync(emailMessageList);
206+
return Results.Ok("Emails sent successfully.");
207+
});
208+
209+
app.MapGet("/send/bulk/sms/multiple-recipients", async (ISmsService smsService) =>
210+
{
211+
var smsMessageList = new List<SmsMessage>();
212+
for (var i = 1; i <= 5; i++)
213+
{
214+
smsMessageList.Add(new SmsMessage
215+
{
216+
Recipients = ["+37493910593", $"+3749391059{i}"],
217+
Message = "Barev erjankutyun",
218+
Channel = SmsChannels.GeneralSender
219+
});
220+
}
221+
222+
await smsService.SendBulkAsync(smsMessageList);
223+
return Results.Ok("SMS messages sent successfully.");
224+
});
225+
174226
app.Run();

src/Communicator/Enums/Channels.cs

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,28 @@
1+
using System.Reflection;
2+
13
namespace Communicator.Enums;
24

35
internal static class Channels
46
{
5-
internal static readonly List<string> EmailChannels = new()
7+
internal static List<string> EmailChannels => GetEmailChannels();
8+
9+
internal static List<string> SmsChannels => GetSmsChannels();
10+
11+
private static List<string> GetEmailChannels()
612
{
7-
"GeneralSender",
8-
"TransactionalSender",
9-
"NotificationSender",
10-
"MarketingSender",
11-
"SupportSender"
12-
};
13+
var fields = typeof(EmailChannels).GetFields(BindingFlags.Public | BindingFlags.Static);
14+
15+
return fields
16+
.Where(field => field.FieldType == typeof(string))
17+
.Select(field => (string)field.GetValue(null)!).ToList();
18+
}
1319

14-
internal static readonly List<string> SmsChannels = new()
20+
private static List<string> GetSmsChannels()
1521
{
16-
"GeneralSender",
17-
"TransactionalSender",
18-
"NotificationSender",
19-
"MarketingSender",
20-
"SupportSender"
21-
};
22+
var fields = typeof(SmsChannels).GetFields(BindingFlags.Public | BindingFlags.Static);
23+
24+
return fields
25+
.Where(field => field.FieldType == typeof(string))
26+
.Select(field => (string)field.GetValue(null)!).ToList();
27+
}
2228
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace Communicator.Enums;
2+
3+
public static class EmailChannels
4+
{
5+
public const string GeneralSender = "GeneralSender";
6+
public const string TransactionalSender = "TransactionalSender";
7+
public const string NotificationSender = "NotificationSender";
8+
public const string MarketingSender = "MarketingSender";
9+
public const string SupportSender = "SupportSender";
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace Communicator.Enums;
2+
3+
public static class SmsChannels
4+
{
5+
public const string GeneralSender = "GeneralSender";
6+
public const string TransactionalSender = "TransactionalSender";
7+
public const string NotificationSender = "NotificationSender";
8+
public const string MarketingSender = "MarketingSender";
9+
public const string SupportSender = "SupportSender";
10+
}

src/Communicator/Helpers/SmsMessageValidator.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,23 @@ internal static void Validate(SmsMessage smsMessage)
1010
{
1111
throw new ArgumentException("At least one recipient is required", nameof(smsMessage.Recipients));
1212
}
13-
13+
1414
char[] charsToCheck = ['(', ')', ' '];
1515
if (smsMessage.Recipients.Any(number => number.Any(c => charsToCheck.Contains(c))))
1616
{
1717
throw new ArgumentException("Invalid phone number", nameof(smsMessage.Recipients));
1818
}
1919
}
20+
21+
internal static SmsMessage ValidateAndTransform(SmsMessage smsMessage)
22+
{
23+
if (smsMessage.Recipients.Count == 0)
24+
{
25+
throw new ArgumentException("At least one recipient is required", nameof(smsMessage.Recipients));
26+
}
27+
28+
smsMessage.Recipients = smsMessage.Recipients.Transform();
29+
30+
return smsMessage;
31+
}
2032
}

src/Communicator/Helpers/SmsRecipients.cs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,27 @@ namespace Communicator.Helpers;
55

66
internal static class SmsRecipients
77
{
8-
internal static string ValidateAndTransform(this string recipient)
9-
{
10-
return PandaValidator.IsPandaFormattedPhoneNumber(recipient)
11-
? recipient.RemovePhoneFormatParenthesesAndAdditionSign()
12-
: recipient;
13-
}
8+
private static readonly char[] CharsToCheck = ['+', '(', ')', ' '];
149

15-
internal static List<string> ValidateAndTransform(this List<string> recipients)
10+
internal static string Transform(this string recipient)
1611
{
17-
if (recipients.Count == 0)
12+
if (PandaValidator.IsPandaFormattedPhoneNumber(recipient)
13+
|| recipient.Any(c => CharsToCheck.Contains(c)))
1814
{
19-
throw new ArgumentException("At least one recipient is required", nameof(recipients));
15+
recipient = recipient.RemovePhoneFormatParenthesesAndAdditionSign();
2016
}
2117

18+
return recipient;
19+
}
20+
21+
internal static List<string> Transform(this List<string> recipients)
22+
{
2223
foreach (var recipient in recipients.Where(PandaValidator.IsPandaFormattedPhoneNumber))
2324
{
2425
recipient.RemovePhoneFormatParenthesesAndAdditionSign();
2526
}
2627

27-
char[] charsToCheck = ['+', '(', ')', ' '];
28-
if (recipients.Any(x => x.Any(n => charsToCheck.Contains(n))))
28+
if (recipients.Any(x => x.Any(n => CharsToCheck.Contains(n))))
2929
{
3030
recipients = recipients.RemovePhoneFormatParenthesesAndAdditionSign();
3131
}

0 commit comments

Comments
 (0)