Skip to content

Instantly share code, notes, and snippets.

@thnk2wn
Created November 6, 2015 21:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thnk2wn/4e57bfdcbfb0b91a042e to your computer and use it in GitHub Desktop.
Save thnk2wn/4e57bfdcbfb0b91a042e to your computer and use it in GitHub Desktop.
using System;
using System.Diagnostics;
using System.Linq;
using CyberCoders.Background.Diagnostics;
using CyberCodersEmailProcessor.Bounce.Data;
using CyberCodersEmailProcessor.Bounce.EmailParsing;
using CyberCodersEmailProcessor.Bounce.Exchange;
using Humanizer;
using Microsoft.Exchange.WebServices.Data;
using NLog;
namespace CyberCodersEmailProcessor.Bounce
{
internal class BounceProcessor
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private readonly BounceDataService _bounceDataService;
private readonly BounceMailboxManager _bounceMailboxManager;
public BounceProcessor()
{
_bounceDataService = new BounceDataService();
_bounceMailboxManager = new BounceMailboxManager();
}
public int BounceCount { get; private set; }
public bool IsStopping { get; private set; }
public int AbuseCount { get; private set; }
public int DelayCount { get; private set; }
public int CandidateBounceCount { get; private set; }
public void Process()
{
var sw = Stopwatch.StartNew();
// thought about making this threaded but plan is to be running this all the time now so there
// shouldn't be that many on any one run. also bounces aren't so critical to warrant thrashing resources to get through quicker.
foreach (var email in _bounceMailboxManager.Read())
{
try
{
if (IsStopping)
{
Logger.ConsoleLog(LogLevel.Info, "Stop request detected; aborting processing");
break;
}
Process(email);
}
catch (Exception ex)
{
// keep on trucking so one bad email doesn't stop processing of others
Logger.ConsoleError(ex);
}
}
sw.Stop();
Logger.Info("{0} emails in {1}. Bounce: {2}, Candidate Bounce: {3} Abuse: {4}, Delay: {5} #summary",
_bounceMailboxManager.ReadCount, sw.Elapsed.Humanize(), BounceCount, CandidateBounceCount, AbuseCount, DelayCount);
StackifyLib.Metrics.Time("Bounce", "Process Time", sw.Elapsed);
}
public void Stop()
{
if (IsStopping) return;
IsStopping = true;
}
private void Process(EmailMessage email)
{
var isAbuse = EmailTypeUtility.IsAbuse(email.Subject);
var isDelay = !isAbuse && EmailTypeUtility.IsDelayEmail(email.Body.Text);
var mode = isAbuse ? "ABUSE" : isDelay ? "DELAY" : "BOUNCE";
Logger.ConsoleLog(LogLevel.Info, "{0}% {1:#,###,###} of {2:#,###,###} {3}: {4:G}",
_bounceMailboxManager.PercentDone, _bounceMailboxManager.ReadCount, _bounceMailboxManager.TotalCount,
mode, email.DateTimeReceived);
// considered splitting abuse out into separate classes and jobs but abuse is so little work and both
// types of content are in the same mailbox, more effecient to process both together. very few abuse emails
if (isDelay)
ProcessDelay(email);
else if (isAbuse)
ProcessAbuse(email);
else
ProcessBounce(email);
StackifyLib.Metrics.Count("Bounce", "Processed Count");
}
private void ProcessBounce(EmailMessage email)
{
var emailParser = new BounceEmailParser(email).Parse();
if (!emailParser.CanHandle)
{
StackifyLib.Metrics.Count("Bounce", "Skip Count");
_bounceMailboxManager.Delete(email);
return;
}
var recorder = new BounceRecorder(emailParser, _bounceDataService);
recorder.RecordBounce();
if (recorder.CandidateUpdated)
{
CandidateBounceCount++;
StackifyLib.Metrics.Count("Bounce", "Candidate Bounce Count");
}
_bounceMailboxManager.Delete(email);
BounceCount++;
}
private void ProcessAbuse(EmailMessage email)
{
Logger.Debug("Processing abuse email w/subject {0} from {1}, received {2:G}",
email.Subject, email.From.Address, email.DateTimeReceived);
// existing logic did this; not quite sure why, guessing regex collide
var body = email.Body.Text.Replace('&', ' ');
var emailAddyMatches = EmailAddressExtractUtility.GetMatches(body);
var blockCount = 0;
foreach (var match in emailAddyMatches.Where(match => EmailAddressUtility.IsValidEmailAddress(match.Value)))
{
_bounceDataService.InsertBlock(match.Value);
blockCount++;
StackifyLib.Metrics.Count("Bounce", "Block Count");
}
AbuseCount++;
_bounceMailboxManager.Delete(email);
StackifyLib.Metrics.Count("Bounce", "Abuse Count");
Logger.Info("Processed abuse email w/subject {0} from {1}, received {2:G}. Blocked: {3}",
email.Subject, email.From.Address, email.DateTimeReceived, blockCount);
}
private void ProcessDelay(EmailMessage email)
{
Logger.Debug("Found delay email w/subject {0}, received at {1}", email.Subject, email.DateTimeReceived);
_bounceMailboxManager.Delete(email);
DelayCount++;
StackifyLib.Metrics.Count("Bounce", "Delay Count");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment