Created
November 6, 2015 21:01
-
-
Save thnk2wn/4e57bfdcbfb0b91a042e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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