Skip to content

Commit 98976a7

Browse files
committed
Changed from MimeMessageSender to SmtpSender.
1 parent b259811 commit 98976a7

File tree

15 files changed

+342
-98
lines changed

15 files changed

+342
-98
lines changed

.github/workflows/_pipeline.yml

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,27 +44,27 @@ jobs:
4444

4545
deploy_core:
4646
# if: ${{ inputs.environment == 'release' }}
47-
needs: build_core
47+
needs: [version,build_core]
4848
uses: ./.github/workflows/_deploy-nuget.yml
4949
with:
50-
environment: ${{ inputs.environment }}
51-
project-name: 'MailKitSimplified.Core'
52-
version: ${{ needs.version.outputs.version }}
50+
environment: ${{ inputs.environment }}
51+
project-name: 'MailKitSimplified.Core'
52+
version: ${{ needs.version.outputs.version }}
5353
secrets: inherit
5454

5555
deploy_sender:
5656
# if: ${{ inputs.environment == 'release' }}
57-
needs: build_sender
57+
needs: [version,build_sender]
5858
uses: ./.github/workflows/_deploy-nuget.yml
5959
with:
60-
environment: ${{ inputs.environment }}
61-
project-name: 'MailKitSimplified.Sender'
62-
version: ${{ needs.version.outputs.version }}
60+
environment: ${{ inputs.environment }}
61+
project-name: 'MailKitSimplified.Sender'
62+
version: ${{ needs.version.outputs.version }}
6363
secrets: inherit
6464

6565
# deploy_receiver:
6666
# if: ${{ inputs.environment == 'release' }} && {{ github.event_name != 'pull_request' }}
67-
# needs: build_receiver
67+
# needs: [version,build_receiver]
6868
# uses: ./.github/workflows/_deploy-nuget.yml
6969
# with:
7070
# environment: ${{ inputs.environment }}

README.md

Lines changed: 42 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,27 @@
11
# MailKitSimplified [![Build and Test results summary](https://github.com/danzuep/MailKitSimplified/actions/workflows/development.yml/badge.svg)](https://github.com/danzuep/MailKitSimplified/actions/workflows/development.yml)
22

3-
Sending and receiving emails sounds simple, after all, electronic mail existed [decades](https://en.wikipedia.org/wiki/History_of_email) before the [Internet](https://en.wikipedia.org/wiki/History_of_the_Internet). If you're looking for a an all-in-one .NET solution for email, you'll quickly discover [MailKit](https://github.com/jstedfast/MailKit) is recommended by even the likes of [Microsoft](https://learn.microsoft.com/en-us/dotnet/api/system.net.mail.smtpclient?view=net-6.0#remarks). Unfortunately for new users though, MailKit can do too much, so when I first started using it I was surprised at how many configuration steps were involved in getting it set up, and on the receiving end how poorly some real-world SMTP servers out there implement [the standard](https://www.rfc-editor.org/rfc/rfc2822). The aim of this package is to make sending an email as simple as possible.
3+
Sending and receiving emails sounds simple, after all, electronic mail existed [decades](https://en.wikipedia.org/wiki/History_of_email) before the [Internet](https://en.wikipedia.org/wiki/History_of_the_Internet). If you're looking for a an all-in-one .NET solution for email, you'll quickly discover [MailKit](https://github.com/jstedfast/MailKit) is recommended by even the likes of [Microsoft](https://learn.microsoft.com/en-us/dotnet/api/system.net.mail.smtpclient?view=net-6.0#remarks) due to how it implements the [RFC standard](https://www.rfc-editor.org/rfc/rfc2822). Unfortunately the downside of doing it all is that MailKit can be difficult to [set up](https://github.com/jstedfast/MailKit#using-mailkit) [and use](https://github.com/jstedfast/MimeKit/blob/master/FAQ.md#messages-1), especially the first time you go to try something like [checking attachments](https://github.com/jstedfast/MimeKit/blob/master/FAQ.md#q-how-do-i-tell-if-a-message-has-attachments) or [writing a reply](https://github.com/jstedfast/MimeKit/blob/master/FAQ.md#q-how-do-i-reply-to-a-message). The aim of this package is to make sending (and receiving) emails as simple as possible!
44

5-
## Sender Usage
5+
Sending an email is now as easy as:
6+
```csharp
7+
await writeEmail.To("test@localhost").SendAsync();
8+
```
9+
10+
## MailKitSimplified.Sender Usage
611

712
### Setup
813

9-
If you're not familiar with dependency injection then just use this:
10-
```
11-
using var smtpSender = MimeMessageSender.Create("smtp.example.com");
14+
If you're not familiar with dependency injection then you can specify the SMTP host address like this:
15+
16+
```csharp
17+
using var smtpSender = SmtpSender.Create("smtp.example.com");
1218
```
1319

20+
An email sender must have a SMTP host address, and sometimes a port number, but leaving the port as the default value of 0 will normally choose the right port automatically (e.g. 25). Most companies use LDAP or something similar for behind-the-scenes authentication, but if not you can specify a network credential too.
21+
1422
### Sending Mail
1523

16-
```
24+
```csharp
1725
var email = smtpSender.WriteEmail
1826
.From("me@example.com")
1927
.To("you@example.com")
@@ -23,17 +31,16 @@ var email = smtpSender.WriteEmail
2331
await email.SendAsync();
2432
```
2533

26-
An email must have a SMTP host address, a `From` and at least one `To` address; that's all. You can use each method as many times as you want, but setting a subject or body multiple times will overwrite previous subject or body values. The order of anything after WriteEmail does not matter.
27-
Any issues will throw an exception, but you can also opt to just log them and continue with a `false` output:
34+
Any configuration issues will throw an exception, but you can also opt to just log any exceptions and continue with a `false` output:
2835

29-
```
36+
```csharp
3037
bool isSent = await smtpSender.WriteEmail
3138
.From("me@example.com", "My Name")
3239
.To("you@example.com", "Your Name")
33-
.To("friend1@example.com")
34-
.To("friend2@example.com")
40+
.Cc("friend1@example.com")
41+
.Bcc("friend2@example.com")
3542
.Subject("Hey You")
36-
.Body($"Hello! {DateTime.Now}")
43+
.Body($"Hello at {DateTime.Now}.")
3744
.Attach("C:/Temp/attachment1.txt", "C:/Temp/attachment2.pdf")
3845
.Attach("./attachment3.docx")
3946
.TrySendAsync();
@@ -46,7 +53,8 @@ Further examples (detailed MailKit SMPT server logs etc.) can be found in MailKi
4653
### Dependency Injection
4754

4855
This is recommended over manual setup as the built-in garbage collector will handle lifetime and disposal.
49-
```
56+
57+
```csharp
5058
using MailKitSimplified.Sender.Extensions;
5159

5260
IHost host = Host.CreateDefaultBuilder(args)
@@ -61,75 +69,61 @@ await host.RunAsync();
6169
```
6270

6371
You'll also need the following in appsettings.json:
64-
```
72+
73+
```json
6574
{
6675
"EmailSender:SmtpHost": "smtp.example.com"
6776
}
6877
```
6978

7079
Other optional settings include SmtpPort, ProtocolLog, SmtpCredential:UserName and SmtpCredential:Password.
7180

72-
Now you can use the fully configured IEmailSender anywhere you want with no other setup! For example:
73-
74-
```
75-
public class EmailService {
76-
77-
private readonly IEmailSender _smtpSender;
78-
79-
public EmailService(IEmailSender smtpSender) {
80-
_smtpSender = smtpSender;
81-
}
82-
}
83-
```
84-
85-
Or, even easier, just start with IEmailWriter:
86-
87-
```
88-
public class EmailService {
81+
Now you can use the fully configured IEmailSender or IEmailWriter anywhere you want with no other setup! For example:
8982

83+
```csharp
84+
public class EmailService
85+
{
9086
private readonly IEmailWriter _writeEmail;
9187

9288
public EmailService(IEmailWriter emailWriter) {
9389
_writeEmail = emailWriter;
9490
}
95-
96-
public async Task SendTestEmailAsync(string bodyText = "") {
97-
await _writeEmail
98-
.From("me@example.com")
99-
.To("you@example.com")
100-
.Subject("Test email")
101-
.Body(bodyText)
102-
.SendAsync();
103-
}
10491
}
10592
```
10693

107-
## Receiver Usage
94+
That's how sending an email can become as simple as one line of code.
95+
96+
```csharp
97+
await _writeEmail.To("test@localhost").SendAsync();
98+
```
99+
100+
## MailKitSimplified.Receiver Usage
108101

109102
### Setup
110103

111104
If you're not familiar with dependency injection then just use this:
112-
```
113-
using var imapReceiver = MimeMessageReceiver.Create("imap.example.com", 0, "U5ern@me", "P@55w0rd");
105+
106+
```csharp
107+
using var imapReceiver = ImapReceiver.Create("imap.example.com", 0, "U5ern@me", "P@55w0rd");
114108
```
115109

116-
An email receiver must have a IMAP host address, a network credential, and a valid mail folder to read from.
110+
An email receiver must have a IMAP host address, a network credential (unless you're using something like `smtp4dev`), and sometimes a port number, but leaving the port as the default value of 0 will normally choose the right port automatically.
117111

118112
### Receiving Mail
119113

120-
```
114+
```csharp
121115
var mailboxReceiver = imapReceiver
122116
.ReadFrom("INBOX")
123117
.Skip(0)
124118
.Take(10);
125119

126-
await mailboxReceiver.GetMimeMessagesAsync();
120+
var mimeMessages = await mailboxReceiver.GetMimeMessagesAsync();
127121
```
128122

129-
```
123+
```csharp
130124
var mimeMessageQueue = await imapReceiver
131125
.ReadFrom("INBOX")
132126
.GetMimeMessagesAsync();
133127
```
134128

135-
Further examples (detailed MailKit IMAP server logs etc.) can be found in MailKitSimplifiedReceiverUnitTests and the example solution file.
129+
Further examples (detailed MailKit IMAP server logs etc.) can be found in the 'samples' and 'tests' folders.

samples/ConsoleServiceExample/Program.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@
88
using var loggerFactory = LoggerFactory.Create(_ => _.SetMinimumLevel(LogLevel.Trace).AddDebug().AddConsole());
99
var logger = loggerFactory.CreateLogger<Program>();
1010

11-
using var smtpSender = MimeMessageSender.Create("localhost", 25);
12-
bool isSent = await Email.Create(smtpSender)
11+
using var smtpSender = SmtpSender.Create("localhost", 25);
12+
bool isSent = await smtpSender.WriteEmail
1313
.Bcc($"{Guid.NewGuid():N}@localhost")
1414
.TrySendAsync();
1515

1616
logger.LogInformation("Email {result}.", isSent ? "sent" : "failed to send");
1717

18-
using var imapReceiver = ImapClientService.Create("localhost", null, 143);
19-
var messages = await MailFolderReader.Create("INBOX", imapReceiver)
18+
using var imapReceiver = ImapReceiver.Create("localhost", null, 143);
19+
var messageSummaries = await imapReceiver.ReadFrom("INBOX")
2020
.GetMessageSummariesAsync(MessageSummaryItems.UniqueId);
2121

22-
logger.LogDebug($"Email received: {messages.Select(m => m.UniqueId).ToEnumeratedString()}");
22+
logger.LogDebug("Email(s) received: {ids}", messageSummaries.Select(m => m.UniqueId).ToEnumeratedString());
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
1-
using System.Threading;
1+
using System;
2+
using System.Threading;
23
using System.Threading.Tasks;
34

45
namespace MailKitSimplified.Core.Abstractions
56
{
6-
public interface IEmailSender
7+
public interface IEmailSender : IDisposable
78
{
89
IEmailWriter WriteEmail { get; }
10+
911
Task SendAsync(ISendableEmail email, CancellationToken cancellationToken = default);
12+
1013
Task<bool> TrySendAsync(ISendableEmail email, CancellationToken cancellationToken = default);
1114
}
1215
}

source/MailKitSimplified.Core/Models/EmailSenderOptions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Net;
33
using System.ComponentModel.DataAnnotations;
4+
using MailKitSimplified.Core.Abstractions;
45

56
namespace MailKitSimplified.Core.Models
67
{
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using MailKit;
2+
using System.Threading;
3+
using System.Threading.Tasks;
4+
5+
namespace MailKitSimplified.Receiver.Abstractions
6+
{
7+
public interface IEmailReader
8+
{
9+
IEmailReader MailFolderName(string mailFolderName);
10+
11+
IEmailReader Skip(int count);
12+
13+
IEmailReader Take(int count);
14+
15+
ValueTask<IMailFolder> ConnectAsync(CancellationToken cancellationToken = default);
16+
}
17+
}

source/MailKitSimplified.Receiver/Abstractions/IImapClientService.cs renamed to source/MailKitSimplified.Receiver/Abstractions/IImapReceiver.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
namespace MailKitSimplified.Receiver.Abstractions
77
{
8-
public interface IImapClientService : IDisposable
8+
public interface IImapReceiver : IDisposable
99
{
1010
ValueTask<IMailFolder> ConnectAsync(CancellationToken cancellationToken = default);
1111
ValueTask<IMailFolder> GetFolderAsync(string mailFolderName, CancellationToken ct = default);

source/MailKitSimplified.Receiver/Extensions/DependencyInjectionExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public static IServiceCollection AddMailKitSimplifiedEmailReceiver(this IService
1414
// This adds IOptions<EmailReceiverOptions> from appsettings.json
1515
services.Configure<EmailReceiverOptions>(configSection);
1616
services.AddTransient<IMailFolderReader, MailFolderReader>();
17-
services.AddTransient<IImapClientService, ImapClientService>();
17+
services.AddTransient<IImapReceiver, ImapReceiver>();
1818
return services;
1919
}
2020
}

source/MailKitSimplified.Receiver/Services/ImapClientService.cs renamed to source/MailKitSimplified.Receiver/Services/ImapReceiver.cs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,15 @@
1818

1919
namespace MailKitSimplified.Receiver.Services
2020
{
21-
public class ImapClientService : IImapClientService
21+
public class ImapReceiver : IImapReceiver
2222
{
2323
private readonly ILogger _logger;
2424
private readonly IImapClient _imapClient;
2525
private readonly EmailReceiverOptions _receiverOptions;
2626

27-
public ImapClientService(IOptions<EmailReceiverOptions> receiverOptions, IProtocolLogger protocolLogger = null, ILogger<ImapClientService> logger = null)
27+
public ImapReceiver(IOptions<EmailReceiverOptions> receiverOptions, IProtocolLogger protocolLogger = null, ILogger<ImapReceiver> logger = null)
2828
{
29-
_logger = logger ?? NullLogger<ImapClientService>.Instance;
29+
_logger = logger ?? NullLogger<ImapReceiver>.Instance;
3030
_receiverOptions = receiverOptions.Value;
3131
if (string.IsNullOrWhiteSpace(_receiverOptions.ImapHost))
3232
throw new NullReferenceException(nameof(EmailReceiverOptions.ImapHost));
@@ -36,27 +36,33 @@ public ImapClientService(IOptions<EmailReceiverOptions> receiverOptions, IProtoc
3636
_imapClient = imapLogger != null ? new ImapClient(imapLogger) : new ImapClient();
3737
}
3838

39-
public static ImapClientService Create(string imapHost, ushort imapPort = 0, string username = null, string password = null, string protocolLog = null)
39+
public static ImapReceiver Create(string imapHost, ushort imapPort = 0, string username = null, string password = null, string protocolLog = null)
4040
{
4141
var imapCredential = username == null && password == null ? null : new NetworkCredential(username ?? "", password ?? "");
4242
var receiver = Create(imapHost, imapCredential, imapPort, protocolLog);
4343
return receiver;
4444
}
4545

46-
public static ImapClientService Create(string imapHost, NetworkCredential imapCredential, ushort imapPort = 0, string protocolLog = null)
46+
public static ImapReceiver Create(string imapHost, NetworkCredential imapCredential, ushort imapPort = 0, string protocolLog = null)
4747
{
4848
var receiverOptions = new EmailReceiverOptions(imapHost, imapCredential, imapPort, protocolLog);
4949
var receiver = Create(receiverOptions);
5050
return receiver;
5151
}
5252

53-
public static ImapClientService Create(EmailReceiverOptions emailReceiverOptions)
53+
public static ImapReceiver Create(EmailReceiverOptions emailReceiverOptions)
5454
{
5555
var options = Options.Create(emailReceiverOptions);
56-
var receiver = new ImapClientService(options);
56+
var receiver = new ImapReceiver(options);
5757
return receiver;
5858
}
5959

60+
public MailFolderReader ReadFrom(string mailFolderName)
61+
{
62+
var mailboxReceiver = MailFolderReader.Create(mailFolderName, this);
63+
return mailboxReceiver;
64+
}
65+
6066
/// <exception cref="AuthenticationException">Failed to authenticate</exception>
6167
public virtual async ValueTask AuthenticateAsync(CancellationToken cancellationToken = default)
6268
{

source/MailKitSimplified.Receiver/Services/MailFolderReader.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,23 +23,23 @@ public class MailFolderReader : IMailFolderReader
2323
public int FolderCount => _mailFolder?.Count ?? 0;
2424

2525
private readonly ILogger _logger;
26-
private readonly IImapClientService _imapClientService;
26+
private readonly IImapReceiver _imapClientService;
2727
private IMailFolder _mailFolder;
2828

29-
public MailFolderReader(IImapClientService imapClientService, ILogger<MailFolderReader> logger = null)
29+
public MailFolderReader(IImapReceiver imapClientService, ILogger<MailFolderReader> logger = null)
3030
{
3131
_logger = logger ?? NullLogger<MailFolderReader>.Instance;
3232
_imapClientService = imapClientService ?? throw new ArgumentNullException(nameof(imapClientService));
3333
}
3434

3535
public static MailFolderReader Create(string folderName, EmailReceiverOptions emailReceiverOptions)
3636
{
37-
var imapClientService = ImapClientService.Create(emailReceiverOptions);
37+
var imapClientService = ImapReceiver.Create(emailReceiverOptions);
3838
var mailFolderReader = Create(folderName, imapClientService);
3939
return mailFolderReader;
4040
}
4141

42-
public static MailFolderReader Create(string folderName, IImapClientService imapClientService)
42+
public static MailFolderReader Create(string folderName, IImapReceiver imapClientService)
4343
{
4444
var mailFolderReader = new MailFolderReader(imapClientService);
4545
mailFolderReader._mailFolder = imapClientService.GetFolderAsync(folderName).GetAwaiter().GetResult();

0 commit comments

Comments
 (0)