-
-
Notifications
You must be signed in to change notification settings - Fork 371
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Suppressing Automatic Replies #938
Comments
It is almost impossible to suppress auto replies on e-mails that you sent. You can set the ReplyTo field to something like noreply@yourcompany.com to let the receiving mailserver sent the reply to another e-mail address that just eats the reply. But even that is not 100% fool proof. You can also make something that just tries to detect if an e-mail is an auto reply and then just deletes the e-mail but that is also not 100% fool proof because there is no real standard that says if an e-mail is an auto reply. |
Indeed I feared that may be the case, though my initial research did lead me to RFC 3834, which notes the following:
As these emails being sent out by MailKit are generated by an automated process, adding the Auto-Submitted header field with a value of I cannot change the ReplyTo field, since I still want users to be able to reply directly to these emails if they need assistance. So it is only the automatic replies which I would like suppressed. Even if 100% reliable suppression of auto-replies on emails I've sent is not feasible, suppressing the vast majority of them would still be a great help. |
I made this to try to detect auto replies. If you want you can use it because all the code in it comes mostly from the internet You probably need to translate my Dutch (Netherlands) comments. internal class AutoReplyDetector
{
#region Consts
private const string AutoSubmittedHeader = "Auto-submitted";
private const string XAutoreplyHeader = "X-Autoreply";
private const string XAutoRespond = "X-Autorespond";
private const string PrecedenceHeader = "Precedence";
// private const string XMailerHeader = "X-Mailer";
private const string XAutoResponseSuppressHeader = "X-Auto-Response-Suppress";
private const string ListIdHeader = "List-Id";
private const string ListUnsubscribeHeader = "List-Unsubscribe";
private const string FeedbackIdHeader = "Feedback-ID";
private const string XmsFBLHeader = "X-MSFBL";
private const string XLoopHeader = "X-Loop";
#endregion
#region Private enum XAutoResponseSuppressHeaderValue
/// <summary>
/// Mogelijke waarden voor <see cref="XAutoResponseSuppressHeader"/>
/// </summary>
/// <remarks>
/// Specifies whether a client or server application will fore go
/// sending automated replies in response to this message.
/// </remarks>
private enum XAutoResponseSuppressHeaderValue
{
/// <summary>
/// Suppress delivery reports from transport.
/// </summary>
DR = 0x00000001,
/// <summary>
/// Suppress non-delivery reports from transport.
/// </summary>
NDR = 0x00000002,
/// <summary>
/// Suppress read notifications from receiving client.
/// </summary>
RN = 0x00000004,
/// <summary>
/// Suppress non-read notifications from receiving client.
/// </summary>
NRN = 0x00000008,
/// <summary>
/// Suppress Out of Office (OOF) notifications.
/// </summary>
OOF = 0x00000010,
/// <summary>
/// Suppress auto-reply messages other than OOF notifications.
/// </summary>
AutoReply = 0x00000020
}
#endregion
#region Detect
/// <summary>
/// Detecteert of een e-mail een automatisch antwoord is
/// </summary>
internal static AutoReplyDetectorResult Detect(MimeMessage message)
{
var headers = message.Headers;
if (headers.Contains(AutoSubmittedHeader))
{
var value = headers[AutoSubmittedHeader];
if (value.ToLowerInvariant() != "no")
return new AutoReplyDetectorResult(true, $"Found header '{AutoSubmittedHeader}' with value '{value}'");
}
else if (headers.Contains(XAutoreplyHeader))
{
var value = headers[XAutoreplyHeader];
return new AutoReplyDetectorResult(true, $"Found header '{XAutoreplyHeader}' with value '{value}'");
}
else if (headers.Contains(XAutoRespond))
{
var value = headers[XAutoRespond];
return new AutoReplyDetectorResult(true, $"Found header '{XAutoRespond}' with value '{value}'");
}
else if (headers.Contains(PrecedenceHeader))
{
var value = headers[PrecedenceHeader];
return new AutoReplyDetectorResult(true, $"Found header '{PrecedenceHeader}' with value '{value}'");
}
//else if (headers.Contains(XMailerHeader))
//{
// var value = headers[XMailerHeader];
// return new AutoReplyDetectorResult(true, $"Found header '{XMailerHeader}' with value '{value}'");
//}
else if (headers.Contains(XAutoResponseSuppressHeader))
{
var value = headers[XAutoResponseSuppressHeader];
return new AutoReplyDetectorResult(true, $"Found header '{XAutoResponseSuppressHeader}' with value '{value}'");
}
else if (headers.Contains(ListIdHeader))
{
var value = headers[ListIdHeader];
return new AutoReplyDetectorResult(true, $"Found header '{ListIdHeader}' with value '{value}', this is probably an e-mail from a mailing list");
}
else if (headers.Contains(ListUnsubscribeHeader))
{
var value = headers[ListUnsubscribeHeader];
return new AutoReplyDetectorResult(true, $"Found header '{ListUnsubscribeHeader}' with value '{value}', this is probably an e-mail from a mailing list");
}
else if (headers.Contains(FeedbackIdHeader))
{
var value = headers[FeedbackIdHeader];
return new AutoReplyDetectorResult(true, $"Found header '{FeedbackIdHeader}' with value '{value}', this is probably an e-mail from a mailing list");
}
else if (headers.Contains(XmsFBLHeader))
{
var value = headers[XmsFBLHeader];
return new AutoReplyDetectorResult(true, $"Found header '{XmsFBLHeader}' with value '{value}', this is probably an e-mail from a mailing list");
}
else if (headers.Contains(XLoopHeader))
{
var value = headers[XLoopHeader];
return new AutoReplyDetectorResult(true, $"Found header '{XLoopHeader}' with value '{value}', this is probably an e-mail from a mailing list");
}
return new AutoReplyDetectorResult(false, string.Empty);
}
#endregion
} /// <summary>
/// Wordt gebruikt om de status van <see cref="AutoReplyDetector.Detect"/> te retourneren
/// </summary>
[DataContract(Name = "autoreplydetectorresult", Namespace = "")]
public class AutoReplyDetectorResult
{
#region Properties
/// <summary>
/// Retourneert <c>true</c> wanneer de <see cref="Email"/> waarschijnlijk
/// een automatisch antwoord is
/// </summary>
/// <remarks>
/// Gebruik <see cref="Reason"/> om te achterhalen waarom we denken dat het
/// een automatisch antwoord is
/// </remarks>
[DataMember(Name = "primarystatus", EmitDefaultValue = false)]
public bool IsAutoReply { get; private set; }
/// <summary>
/// De reden waarom het een automatisch antwoord is
/// </summary>
public string Reason { get; private set; }
#endregion
#region Constructor
/// <summary>
/// Maakt dit object en zet alle benodigde properties
/// </summary>
/// <param name="isAutoReply"></param>
/// <param name="reason"></param>
internal AutoReplyDetectorResult(bool isAutoReply, string reason)
{
IsAutoReply = isAutoReply;
Reason = reason;
}
#endregion
} And the rest that slips through you need to detect with just looking for specific words in the subject of the e-mail and then in the body. |
Unfortunately you are venturing into uncharted territory (at least as far as I've explored). I just fired off your question to some folks on the Office365 team that have helped answer questions for me before (seems like a few are OOF until 2023-07-07 so it may take a few days to get a response). Hopefully they'll have an answer, but if not, your best bet might be a try-and-see approach to see if setting those headers work or not. Failing that, @Sicos1977 's suggestion on trying to detect auto-response emails and filtering them out (or auto-deleting them) might be the next best option. I'll keep you updated if I get any responses from the Office365 team. |
I'm working with e-mail traffic for the last 20 years and detecting 100% of all auto replies is just impossible. The best solution I found is what I posted. Detecting something like a soft or hard bouncer is much much easier. Wish it would also be that easy for auto replies :-) |
I am being asked which types of auto-replies specifically you are trying to suppress. Can you clarify? Thanks. |
I'm Ideally trying to suppress all auto-replies except for non-delivery reports. But if it's all-or-nothing, it'd be okay to suppress non-delivery reports as well. Auto-Replies To Suppress:
|
If you send messages via SMTP, delivery reports shouldn't happen unless you opt-in to receive them via the SMTP DSN feature (assuming we are talking about the same thing?). Or, you might be able to use DeliveryStatusNotification.Never if the SMTP server supports DSN. See this code snippet for how to do this: http://mimekit.net/docs/html/M_MailKit_Net_Smtp_SmtpClient_GetDeliveryStatusNotifications.htm (just use Never instead of Failure). Likewise, for Read notifications, they should be opt-in only if you set a If you are getting them anyway, let me know, and I can pass that along as well. |
Well, in theory. But come to think of it, I'm pretty sure I've gotten them sometimes and I never enable this. Only in the "Failed to deliver" cases, though. |
Still trying to get answers from Exchange/Outlook folks... |
I appreciate all your help on this. No worries if you're not able to get a response. An official answer would be nice, but I can definitely play around with the X-Auto-Response-Suppress header and see if it suppresses most of what I need it to. |
My email kept getting passed around and no one seemed to have an answer. |
I'm just going to close this but I wish I could have gotten you a conclusive answer :( |
Having the same issue (detecting auto replies), #938 (comment) @Sicos1977 your little detector looks great, maybe that could be made a little lib - just like the MailBounceDetector? BTW my research also yield'ed the following sources for more information:
It seems to be an endless issue... |
…uto-Submitted This was totally confusing for me why there are two header IDs in MimeKit with the quite very same header. I even suspected a bug or that it is just a legacy API-compatibility thing (one being deprecated, though not stated?). After some research it turns out, the header is actually defined. At least for the modern RFC3834 header the keywords are actually also defined by IANA: https://www.iana.org/assignments/auto-submitted-keywords/auto-submitted-keywords.xml (But you don't do mapping to an enum, so introducing this would change the API drastically and be a different/bigger task, so I thought for the code this may not be relevant.) So to clarify this and ease searching around the web and decrease potential confusion for other developers, IMHO, it is a good idea to just directly point out the basic difference in the (code) doc, directly where you are using it. For background, I am at jstedfast#938 (comment) here detecting auto-reply mails for which this header is very important.
If one wanted to suppress receiving automatic replies in response to a message sent via MailKit, would adding the X-Auto-Response-Suppress header to the MimeMessage be the correct way to go about this? Our organization uses Outlook 365, so the vast majority of our auto-replies will be coming from an Exchange server.
There is of course the more standard "Auto-Submitted: auto-generated" header, but I was unsure if this would also tell the mail server not to respond with another auto-generated email or auto-reply.
The text was updated successfully, but these errors were encountered: