Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
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())
if (IsStopping)
Logger.ConsoleLog(LogLevel.Info, "Stop request detected; aborting processing");
catch (Exception ex)
// keep on trucking so one bad email doesn't stop processing of others
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)
else if (isAbuse)
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");
var recorder = new BounceRecorder(emailParser, _bounceDataService);
if (recorder.CandidateUpdated)
StackifyLib.Metrics.Count("Bounce", "Candidate Bounce Count");
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)))
StackifyLib.Metrics.Count("Bounce", "Block Count");
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);
StackifyLib.Metrics.Count("Bounce", "Delay Count");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment