Skip to content

Receiver

Daniel Collingwood edited this page Jul 28, 2023 · 39 revisions

MailKitSimplified.Receiver Usage

Installation NuGet Downloads

Package Manager Console: > Install-Package MailKitSimplified.Receiver # -Version x.x.x

.NET CLI Console: > dotnet add package MailKitSimplified.Receiver # --version x.x.x

Setup

If you're not familiar with dependency injection then you can specify the IMAP host address like this:

using var imapReceiver = ImapReceiver.Create("imap.example.com:993")
    .SetCredential("user@example.com", "App1icati0nP455w0rd")
    .SetProtocolLog("Logs/ImapClient.txt")
    .SetFolder("INBOX/My Subfolder");

An email receiver must have a IMAP host address and a network credential, leaving out the port number will normally choose the right port automatically (e.g. 143 or 993). Use SetProtocolLog("console") to quickly debug with detailed logging to the console.

Receiving Mail

To download full emails with attachments and everything:

var mimeMessages = await imapReceiver.ReadMail
    .Skip(0).Take(250, continuous: true)
    .GetMimeMessagesAsync();

The Skip() and Take() methods are optional, but they allow you to specify a range of message instead of potentially downloading thousands of emails, something that could take a very long time. You should always use a CancellationToken, especially if you don't set Take to a small number.

To just download the email parts you want to use (UniqueId is very fast so all results are returned):

var messageSummaries = await imapReceiver.ReadFrom("INBOX")
    .GetMessageSummariesAsync(MessageSummaryItems.UniqueId);

To query the SMTP server and get unique IDs for the top 250 emails:

var messageSummaries = await imapReceiver.MailFolderReader.Query(SearchQuery.All)
    .GetMessageSummariesAsync(MailFolderReader.CoreMessageItems);

Other useful MessageSummaryItems to know about are Size, InternalDate, Envelope, and BodyStructure.

Further examples (how to set up MailKit IMAP server logs etc.) can be found in the samples and tests folders on GitHub.

Dependency Injection

Dependency Injection is recommended over manual setup as the built-in garbage collector will handle lifetime and disposal.

using MailKitSimplified.Receiver;

IHost host = Host.CreateDefaultBuilder(args)
    .ConfigureServices((context, services) =>
    {
        services.AddHostedService<ExampleNamespace.EmailService>();
        services.AddMailKitSimplifiedEmailReceiver(context.Configuration);
    })
    .Build();

await host.RunAsync();

You could choose to configure the services manually, but I think appsettings.json is better:

services.AddEmailReceiverOptions(o => { o.ImapHost = "localhost"; o.ImapPort = 143; });
services.AddFolderMonitorOptions(o => { o.IdleMinutes = 9; });

Add the following in appsettings.json (check Properties -> Copy to Output Directory -> Copy if newer):

{
  "EmailReceiver": {
    "MailFolderName": "INBOX",
    "ImapHost": "imap.example.com",
    "ImapPort": 993,
    "ImapCredential": {
      "UserName": "user@example.com",
      "Password": "App1icati0nP455w0rd"
    },
    "ProtocolLog": "Logs\\ImapClient.txt"
  },
  "FolderMonitor": {
    "IgnoreExistingMailOnConnect": false,
    "MessageSummaryItems": "None",
    "IdleMinutes": 9,
    "MaxRetries": 3
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "MailKitSimplified.Receiver.Services.MailKitProtocolLogger": "Debug"
    }
  }
}

Now you can use the fully configured IImapReceiver, IMailFolderReader, or IMailReader anywhere you want with no other setup! For example:

public class EmailService
{
    private readonly IMailFolderReader _readMail;

    public EmailService(IMailFolderReader readMail) {
        _readMail = readMail;
    }
}

That's how receiving emails can become as simple as one line of code.

var mimeMessages = await _readMail.Take(10).GetMimeMessagesAsync();

Mail Folder Idle Monitor

To asynchronously monitor the mail folder for incoming messages:

await new MailFolderMonitor(imapReceiver).SetMessageSummaryItems(MessageSummaryItems.Envelope)
    .SetIgnoreExistingMailOnConnect(true).SetIdleMinutes(29).SetMaxRetries(1)
    .OnMessageArrival((messageSummary) => OnArrivalAsync(messageSummary))
    .OnMessageDeparture((messageSummary) => OnDepartureAsync(messageSummary))
    .IdleAsync();

The values shown above are the default method override values, the de-factro FolderMonitorOptions are UniqueId, false, 9, and 3 respectively. If you're happy with the latter then using MailFolderMonitor is even easier:

await imapReceiver.MonitorFolder.OnMessageArrival(ForwardMessageAsync).IdleAsync();

One line of code instead of 251! It never used to be that easy.

Message Flags

Here's how to mark all incoming messages as Seen:

var cancellationTokenSource = new CancellationTokenSource();
using var imapReceiver = ImapReceiver.Create(ServerHost, ServerPort)
    .SetCredential(Username, Password);
await imapReceiver.MonitorFolder
    .SetMessageSummaryItems(MessageSummaryItems.Envelope | MessageSummaryItems.Flags)
    .OnMessageArrival(m =>
        Console.WriteLine($"[{m.UniqueId}] - {m.Envelope.From} {m.NormalizedSubject}");
        if (m.Folder.Access != FolderAccess.ReadWrite)
            await m.Folder.OpenAsync(FolderAccess.ReadWrite);
        if (!m.Flags.Value.HasFlag(MessageFlags.Seen))
            await m.Folder.AddFlagsAsync(m.UniqueId, MessageFlags.Seen, true);
    }).IdleAsync(cancellationTokenSource.Token);

Here's how to delete the first 250 Seen messages in the Inbox:

var mailFolder = await imapReceiver.ConnectMailFolderAsync();
_ = await mailFolder.OpenAsync(FolderAccess.ReadWrite, cancellationToken).ConfigureAwait(false);
var messageSummaries = await imapReceiver.MailFolderReader.Query(SearchQuery.Seen)
    .GetMessageSummariesAsync(MessageSummaryItems.Flags);
var uniqueIds = messageSummaries.Select(m => m.UniqueId).OrderBy(u => u.Id).ToList();
await mailFolder.AddFlagsAsync(uniqueIds, MessageFlags.Deleted, silent: true);
await mailFolder.ExpungeAsync();
await mailFolder.CloseAsync(false, CancellationToken.None).ConfigureAwait(false);

Forwarding Emails

Here's how to forward an email:

async Task ForwardMessageAsync(IMessageSummary messageSummary)
{
    var mimeForward = await messageSummary.GetForwardMessageAsync(
        "<p>FYI.</p>", includeMessageId: true);
    mimeForward.From.Add("from@example.com");
    mimeForward.To.Add("to@example.com");
    _logger.LogInformation($"Reply: \r\n{mimeForward.HtmlBody}");
    await _smtpSender.SendAsync(mimeForward); //cancellationToken
}

Replying To Emails

Here's how to download an email and reply to it:

var mimeMessages = await _imapReceiver.ReadMail.Take(1).GetMimeMessagesAsync();
var mimeReply = mimeMessages.Single().GetReplyMessage("<p>Reply here.</p>", addRecipients: true);

To only download the email parts you want to use:

var messageSummaries = await imapReceiver.ReadMail.Take(1)
    .GetMessageSummariesAsync(MessageSummaryItems.All);
var mimeReply = await messageSummaries.Single()
    .GetReplyMessageAsync("<p>Reply here.</p>");
mimeReply.From.Add(new MailboxAddress("", "from@localhost"));
mimeReply.To.Add(new MailboxAddress("", "to@localhost"));

One line of code instead of 238! It never used to be that easy.

See Also

Clone this wiki locally