Skip to content

Commit

Permalink
Got outbound texts, generated from email responses, working in suppor…
Browse files Browse the repository at this point in the history
…t of #464.
  • Loading branch information
uncheckederror committed Nov 10, 2024
1 parent 8fe97d5 commit 2076bb0
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 0 deletions.
30 changes: 30 additions & 0 deletions Messaging/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@

using Flurl.Http;

using MailKit;
using MailKit.Net.Imap;
using MailKit.Search;
using MailKit.Security;

using Messaging;
Expand Down Expand Up @@ -35,6 +38,7 @@
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Data;
using System.Linq;
using System.Net.Mail;
using System.Text.Json;
using System.Text.Json.Serialization;
Expand Down Expand Up @@ -186,6 +190,7 @@
builder.Services.AddMemoryCache();
builder.Services.AddDefaultAWSOptions(builder.Configuration.GetAWSOptions());
builder.Services.AddAWSService<IAmazonS3>();
builder.Services.AddHostedService<TimedHostedService>();

// Add Application Insights
builder.Services.AddApplicationInsightsTelemetry();
Expand Down Expand Up @@ -2193,6 +2198,31 @@ public async Task<bool> SendEmailAsync(string username, string password, string
return false;
}
}

public readonly record struct InboundEmail(string Subject, string Content, string To, string From);

public static async Task<ReadOnlyMemory<InboundEmail>> GetEmailsAsync(string username, string password, CancellationToken cls)
{
using var client = new ImapClient();
await client.ConnectAsync("witcher.mxrouting.net", 0, SecureSocketOptions.StartTls, cls);
await client.AuthenticateAsync(username, password, cls);
var inbox = client.Inbox;
var folder = await inbox.OpenAsync(MailKit.FolderAccess.ReadWrite, cls);
var query = SearchQuery.Recent.And(SearchQuery.NotSeen);
var recentAndUnanswered = await inbox.SearchAsync(query, cls);
List<InboundEmail> emails = [];
foreach (var uid in recentAndUnanswered)
{
var message = await inbox.GetMessageAsync(uid);
var fromNumberStart = message.Subject.IndexOf("from 1");
var toNumberStart = message.Subject.IndexOf("to 1");
var justTheText = message.TextBody.Split("\r\n");
emails.Add(new InboundEmail(message.Subject, justTheText.FirstOrDefault() ?? message.TextBody, message.Subject.Substring(toNumberStart + 3, 11), message.Subject.Substring(fromNumberStart + 5, 11)));
_ = await inbox.StoreAsync(uid, new StoreFlagsRequest(StoreAction.Add, MessageFlags.Seen) { Silent = true }, cls);
}
await client.DisconnectAsync(true, cls);
return emails.ToArray().AsMemory();
}
}


Expand Down
77 changes: 77 additions & 0 deletions Messaging/TimedHostedService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using MailKit.Net.Pop3;

using Microsoft.EntityFrameworkCore;

using Models;

using System.Runtime.InteropServices;

namespace Messaging
{
public class TimedHostedService(ILogger<TimedHostedService> logger, AppSettings appSettings) : BackgroundService
{
private readonly ILogger<TimedHostedService> _logger = logger;
private int _executionCount;

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Timed Hosted Service running.");

// When the timer should have no due-time, then do the work once now.
await DoWork(appSettings, stoppingToken);

// Most email clients refresh every 10 seconds per a brief googling.
using PeriodicTimer timer = new(TimeSpan.FromSeconds(10));

try
{
while (await timer.WaitForNextTickAsync(stoppingToken))
{
await DoWork(appSettings, stoppingToken);
}
}
catch (OperationCanceledException)
{
_logger.LogInformation("Timed Hosted Service is stopping.");
}
}

// Could also be a async method, that can be awaited in ExecuteAsync above
private async Task DoWork(AppSettings appSettings, CancellationToken cls)
{
int count = Interlocked.Increment(ref _executionCount);

_logger.LogInformation("Timed Hosted Service is working. Count: {Count}", count);

var contextOptions = new DbContextOptionsBuilder<MessagingContext>()
.UseSqlite()
.Options;
using var dbContext = new MessagingContext(contextOptions);

// Check for emails
var emails = await EmailMessage.GetEmailsAsync(appSettings.ConnectionStrings.EmailUsername, appSettings.ConnectionStrings.EmailPassword, cls);
// Transform each email to a text message
foreach (var email in MemoryMarshal.ToEnumerable(emails))
{
await EmailToForwardedMessageAsync(email, dbContext);
_logger.LogInformation("To {to} From {from}", email.To, email.From);
}
// Send the messages outbound
_logger.LogInformation("Timed Hosted Service has completed. Count: {Count}", count);
}

private async Task EmailToForwardedMessageAsync(EmailMessage.InboundEmail email, MessagingContext messagingContext)
{
var message = new SendMessageRequest()
{
// Reverse from and to here, as we are responding.
To = email.From,
MSISDN = email.To,
MediaURLs = [],
Message = email.Content
};

_ = await Endpoints.SendMessageAsync(message, false, appSettings, messagingContext);
}
}
}

0 comments on commit 2076bb0

Please sign in to comment.