{"id":261441,"date":"2015-07-14T19:47:02","date_gmt":"2015-07-14T15:47:02","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=261441"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=261441","title":{"rendered":"\u0421\u0431\u043e\u0440\u0449\u0438\u043a \u043f\u043e\u0447\u0442\u044b (\u0434\u0435\u043b\u0430\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u044b\u0435 \u0432\u0435\u0449\u0438 \u0441\u043b\u043e\u0436\u043d\u043e)"},"content":{"rendered":"<br \/>\n<h4>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043f\u0440\u0435\u0434\u0438\u0441\u043b\u043e\u0432\u0438\u044f<\/h4>\n<p>  \u041d\u0430\u0432\u0435\u0440\u043d\u043e\u0435, \u043c\u043d\u043e\u0433\u0438\u0435 \u0438\u0437 \u0432\u0430\u0441 \u0432 \u0441\u0432\u043e\u0435\u0439 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435 \u0441\u0442\u0430\u043b\u043a\u0438\u0432\u0430\u043b\u0438\u0441\u044c \u0441 \u0437\u0430\u0434\u0430\u0447\u0435\u0439 \u0441\u0431\u043e\u0440\u0430 \u043f\u043e\u0447\u0442\u044b \u0441 \u0440\u044f\u0434\u0430 \u044f\u0449\u0438\u043a\u043e\u0432. \u0417\u0430\u0447\u0435\u043c \u044d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043d\u0443\u0436\u043d\u043e? \u041d\u0430\u0432\u0435\u0440\u043d\u043e\u0435, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u044d\u0442\u043e \u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u044b\u0439 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u043e\u0431\u043c\u0435\u043d\u0430 \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u043c\u0435\u0436\u0434\u0443 \u0441\u0438\u0441\u0442\u0435\u043c\u0430\u043c\u0438. \u041c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a \u043f\u043e\u0434 \u043b\u044e\u0431\u044b\u0435 \u044f\u0437\u044b\u043a\u0438, \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u044e\u0449\u0438\u0445 SMTP, POP3, IMAP, \u0433\u043e\u0442\u043e\u0432\u044b\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u043f\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0441\u0442\u044d\u043a\u0430 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 (\u043a\u0430\u043a \u044f \u0441\u043b\u043e\u0436\u043d\u043e \u043d\u0430\u0437\u0432\u0430\u043b \u043f\u043e\u0447\u0442\u043e\u0432\u044b\u0439 \u044f\u0449\u0438\u043a&#8230;) \u0438 \u0442.\u0434. <\/p>\n<p>  \u041d\u0435\u0443\u0434\u0438\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e, \u0447\u0442\u043e \u043c\u043d\u043e\u0433\u0438\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u044e\u0442\u0441\u044f \u0438\u043c\u0435\u043d\u043d\u043e \u0447\u0435\u0440\u0435\u0437 \u043f\u043e\u0447\u0442\u0443. \u0422\u0443\u0442 \u0432 \u0434\u0435\u043b\u043e \u0432\u0441\u0442\u0443\u043f\u0430\u0435\u0442 \u043d\u0435\u043a\u0438\u0439 \u0441\u0435\u0440\u0432\u0438\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0443\u043c\u0435\u0435\u0442 \u044d\u0442\u0443 \u043f\u043e\u0447\u0442\u0443 \u0431\u044b\u0441\u0442\u0440\u043e \u0437\u0430\u0431\u0438\u0440\u0430\u0442\u044c, \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f.<\/p>\n<p>  \u041a\u043e\u043c\u0443 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u043d\u043e\u0433\u043e \u043d\u0438\u0436\u0435 \u043a\u043e\u0434\u0430 \u2014 \u0434\u0430\u043b\u044c\u0448\u0435 \u043c\u043e\u0433\u0443\u0442 \u043d\u0435 \u0447\u0438\u0442\u0430\u0442\u044c:<\/p>\n<pre><code>         foreach (var mailbox in mailboxes)                 using (var client = new Pop3Client())                 {                     client.Connect(Hostname, Port, false);                     client.Authenticate(User, Password);                      var count = client.GetMessageCount();                     for (var i = 0; i &lt; count; i++)                     {                         Mail = client.GetMessage(i + 1);                         var cat = SortMail(Mail);                         DoSomething(Mail, cat);                     }                 } <\/code><\/pre>\n<p>  <a name=\"habracut\"><\/a>  <\/p>\n<h2>\u0427\u0442\u043e \u0431\u0443\u0434\u0435\u043c \u0434\u0435\u043b\u0430\u0442\u044c<\/h2>\n<p>  \u0421\u0440\u0430\u0437\u0443 \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u0440\u044f\u0434 \u0434\u043e\u043f\u0443\u0449\u0435\u043d\u0438\u0439:<br \/>  1) \u0421\u043e\u0431\u0438\u0440\u0430\u0442\u044c \u043f\u043e\u0447\u0442\u0443 \u043d\u0443\u0436\u043d\u043e \u0434\u043b\u044f \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0441\u0438\u0441\u0442\u0435\u043c. \u041c\u043e\u0436\u0435\u0442 \u0432 \u0431\u0443\u0434\u0443\u0449\u0435\u043c \u0435\u0449\u0451 \u0434\u043b\u044f \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445. \u0418 \u0435\u0449\u0451\u2026 \u0412 \u043e\u0431\u0449\u0435\u043c \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u043e\u0435;<br \/>  2) \u041f\u043e\u0447\u0442\u044b \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u043c\u043d\u043e\u0433\u043e \u2014 \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0438\u0437 \u043f\u0443\u043d\u043a\u0442\u0430 1 (\u0430 \u0438\u043d\u0430\u0447\u0435 \u044f \u0431\u044b \u043d\u0435 \u043f\u0438\u0441\u0430\u043b \u044d\u0442\u043e\u0442 \u043f\u043e\u0441\u0442);<br \/>  3) \u041f\u043e\u0447\u0442\u0443 \u043f\u0440\u0438\u0434\u0435\u0442\u0441\u044f \u043f\u0430\u0440\u0441\u0438\u0442\u044c;<br \/>  4) \u0412\u0441\u0435 \u044f\u0449\u0438\u043a\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u043d\u044b\u0435 \u2014 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 \u0442\u0443\u0434\u0430 \u043d\u0435 \u043b\u0435\u0437\u0443\u0442.<\/p>\n<h2>\u0427\u0442\u043e \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c<\/h2>\n<p>  \u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u0434\u043e\u043b\u0436\u043d\u0430 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c 24\/7, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0435\u0451 \u0432 \u0432\u0438\u0434\u0435 Windows Service. \u0414\u043b\u044f \u044d\u0442\u0438\u0445 \u0446\u0435\u043b\u0435\u0439 \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u0441\u0440\u0430\u0437\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <a href=\"https:\/\/github.com\/Topshelf\/Topshelf\">TopShelf<\/a>.<\/p>\n<p>  \u0420\u0430\u0437\u0443\u043c\u0435\u0435\u0442\u0441\u044f, \u0432\u0441\u0451 \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u0440\u0430\u0441\u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u0435\u043d\u043e. \u0422\u0443\u0442 \u043d\u0430 \u0441\u0446\u0435\u043d\u0443 \u0432\u044b\u0445\u043e\u0434\u0438\u0442 \u043c\u043e\u044f \u043b\u044e\u0431\u0438\u043c\u0430\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/hh228603(v=vs.110).aspx\">TPL DataFlow<\/a>.<\/p>\n<p>  \u0417\u0430\u0431\u0438\u0440\u0430\u0442\u044c \u043f\u043e\u0447\u0442\u0443 \u0431\u0443\u0434\u0435\u043c \u043f\u043e POP3. \u0412\u0441\u0435 \u00ab\u043c\u043e\u0434\u043d\u044b\u0435 \u0448\u0442\u0443\u0447\u043a\u0438\u00bb IMAP \u0432 \u0434\u0430\u043d\u043d\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0435 \u0438\u0437\u043b\u0438\u0448\u043d\u0438 \u2014 \u043d\u0430\u0434\u043e \u043a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u0441\u0442\u0440\u0435\u0435 \u0438 \u043f\u0440\u043e\u0449\u0435 \u0437\u0430\u0431\u0440\u0430\u0442\u044c \u0438\u0441\u0445\u043e\u0434\u043d\u0438\u043a \u043f\u0438\u0441\u044c\u043c\u0430 \u0438 \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u0435\u0433\u043e \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430. POP3 \u0442\u0443\u0442 \u0445\u0432\u0430\u0442\u0438\u0442 \u0437\u0430 \u0433\u043b\u0430\u0437\u0430. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <a href=\"https:\/\/www.nuget.org\/packages\/OpenPop.NET\/\">OpenPop.NET<\/a>.<\/p>\n<p>  \u041a\u0430\u043a \u0443\u0436\u0435 \u0433\u043e\u0432\u043e\u0440\u0438\u043b\u043e\u0441\u044c, \u043f\u043e\u0447\u0442\u0443 \u0431\u0443\u0434\u0435\u043c \u043f\u0430\u0440\u0441\u0438\u0442\u044c. \u041c\u043e\u0436\u0435\u0442 \u0447\u0435\u0440\u0435\u0437 Regex, \u043c\u043e\u0436\u0435\u0442 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0435 \u043b\u043e\u0433\u0438\u043a\u0438\u2026 \u043c\u0430\u043b\u043e \u043b\u0438 \u0447\u0442\u043e. \u0418\u043c\u0435\u043d\u043d\u043e \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0443\u0436\u043d\u043e \u0433\u0438\u0431\u043a\u043e \u0438 \u0431\u044b\u0441\u0442\u0440\u043e \u043f\u043e\u0434\u043f\u0438\u0445\u0438\u0432\u0430\u0442\u044c \u043d\u043e\u0432\u044b\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043f\u043b\u0430\u0433\u0438\u043d\u043e\u0432. \u0422\u0443\u0442 \u043d\u0430\u043c \u043f\u043e\u043c\u043e\u0436\u0435\u0442 <a href=\"https:\/\/msdn.microsoft.com\/ru-ru\/library\/dd460648(v=vs.110).aspx\">Managed Extensibility Framework<\/a>.<\/p>\n<p>  \u041b\u043e\u0433\u0438 \u043f\u0438\u0448\u0435\u043c \u0447\u0435\u0440\u0435\u0437 <a href=\"http:\/\/nlog-project.org\/\">NLog<\/a>. <\/p>\n<p>  \u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0444\u0430\u043a\u0443\u043b\u044c\u0442\u0430\u0442\u0438\u0432\u0430 \u043f\u0440\u0438\u043a\u0440\u0443\u0442\u0438\u043c \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433 \u0432 <a href=\"http:\/\/www.zabbix.com\/ru\/\">Zabbix<\/a>. (\u041c\u044b \u0436\u0435 \u0441\u043e\u0431\u0440\u0430\u043b\u0438\u0441\u044c \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c 24\/7 \u0438 \u0432\u044b\u0434\u0430\u0432\u0430\u0442\u044c \u0445\u0432\u0430\u043b\u0435\u043d\u0443\u044e \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u2014 \u043d\u0443\u0436\u043d\u043e \u0441\u043b\u0435\u0434\u0438\u0442\u044c \u0437\u0430 \u044d\u0442\u0438\u043c).<\/p>\n<h2>\u041f\u043e\u0435\u0445\u0430\u043b\u0438<\/h2>\n<p>  \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043e\u0431\u044b\u0447\u043d\u043e\u0435 \u043a\u043e\u043d\u0441\u043e\u043b\u044c\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435. \u041e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u043c NuGet \u043a\u043e\u043d\u0441\u043e\u043b\u044c \u0438 \u0441\u0442\u0430\u0432\u0438\u043c \u0432\u0441\u0435 \u043d\u0443\u0436\u043d\u044b\u0435 \u043f\u0430\u043a\u0435\u0442\u044b:<\/p>\n<pre><code class=\"cmake\">Install-Package Nlog Install-Package OpenPop.NET Install-Package TopShelf Install-Package Microsoft.TPL.DataFlow <\/code><\/pre>\n<p>  \u041f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u0432 \u043f\u0430\u043f\u043a\u0443 \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u0441\u043e\u0437\u0434\u0430\u0435\u043c App.Debug.config \u0438 App.Release.config. \u0412\u044b\u0433\u0440\u0443\u0436\u0430\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442 \u0438\u0437 \u0441\u0442\u0443\u0434\u0438\u0438, \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u0435\u0433\u043e \u043a\u043e\u0434 (\u0417\u0434\u0435\u0441\u044c \u0438 \u0434\u0430\u043b\u0435\u0435 TopCrawler.csproj). \u0412 \u0441\u0435\u043a\u0446\u0438\u044e \u0441 \u043a\u043e\u043d\u0444\u0438\u0433\u043e\u043c \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c:<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"xml\">    &lt;None Include=&quot;App.Debug.config&quot;&gt;       &lt;DependentUpon&gt;App.config&lt;\/DependentUpon&gt;     &lt;\/None&gt;     &lt;None Include=&quot;App.Release.config&quot;&gt;       &lt;DependentUpon&gt;App.config&lt;\/DependentUpon&gt;     &lt;\/None&gt; <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  \u0410 \u043d\u0438\u0436\u0435 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0442\u0430\u0440\u0433\u0435\u0442 \u0434\u043b\u044f MSBuild:<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">Transform target<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"xml\">&lt;UsingTask TaskName=&quot;TransformXml&quot; AssemblyFile=&quot;$(MSBuildExtensionsPath32)\\Microsoft\\VisualStudio\\v$(VisualStudioVersion)\\Web\\Microsoft.Web.Publishing.Tasks.dll&quot; \/&gt;   &lt;Target Name=&quot;AfterCompile&quot; Condition=&quot;Exists('App.$(Configuration).config')&quot;&gt;     &lt;!--Generate transformed app config in the intermediate directory--&gt;     &lt;TransformXml Source=&quot;App.config&quot; Destination=&quot;$(IntermediateOutputPath)$(TargetFileName).config&quot; Transform=&quot;App.$(Configuration).config&quot; \/&gt;     &lt;!--Force build process to use the transformed configuration file from now on.--&gt;     &lt;ItemGroup&gt;       &lt;AppConfigWithTargetPath Remove=&quot;App.config&quot; \/&gt;       &lt;AppConfigWithTargetPath Include=&quot;$(IntermediateOutputPath)$(TargetFileName).config&quot;&gt;         &lt;TargetPath&gt;$(TargetFileName).config&lt;\/TargetPath&gt;       &lt;\/AppConfigWithTargetPath&gt;     &lt;\/ItemGroup&gt;   &lt;\/Target&gt; <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  <i>\u041b\u0438\u0447\u043d\u043e \u044f \u043f\u0440\u0438\u0432\u044b\u043a \u0438\u043c\u0435\u043d\u043d\u043e \u0442\u0430\u043a\u0438\u043c \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u043c \u2014 \u043f\u043e \u0441\u0442\u0430\u0440\u0438\u043d\u043a\u0435 \u2014 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u0442\u0440\u0430\u043d\u0441\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043a\u043e\u043d\u0444\u0438\u0433\u043e\u0432 \u0434\u043b\u044f \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u0441\u0440\u0435\u0434.<\/i><br \/>  \u0414\u043b\u044f \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u0430 \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e strongly-type \u043a\u043e\u043d\u0444\u0438\u0433\u0438. \u041e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043a\u043b\u0430\u0441\u0441 \u0431\u0443\u0434\u0435\u0442 \u0447\u0438\u0442\u0430\u0442\u044c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e. (\u041e \u0442\u0435\u043e\u0440\u0435\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0430\u0441\u043f\u0435\u043a\u0442\u0430\u0445 \u0442\u0430\u043a\u043e\u0433\u043e \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043e\u0431\u0449\u0430\u0442\u044c\u0441\u044f \u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0445). \u041a\u043e\u043d\u0444\u0438\u0433\u0438, \u043b\u043e\u0433\u0438, \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433 \u2014 \u043e\u0442\u043b\u0438\u0447\u043d\u044b\u0439 \u043f\u043e\u0432\u043e\u0434 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u0430\u0442\u0442\u0435\u0440\u043d Singleton.<\/p>\n<p>  \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u043e\u0434\u043d\u043e\u0438\u043c\u0435\u043d\u043d\u0443\u044e \u043f\u0430\u043f\u043a\u0443 (\u0434\u043e\u043b\u0436\u0435\u043d \u0436\u0435 \u0431\u044b\u0442\u044c \u043f\u043e\u0440\u044f\u0434\u043e\u043a). \u0412\u043d\u0443\u0442\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u0435\u043c 3 \u043a\u043b\u0430\u0441\u0441\u0430 \u2014 Config, Logger, Zabbix. \u041d\u0430\u0448 \u043b\u043e\u0433\u0433\u0435\u0440:<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">Logger<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code>static class Logger     {         public static NLog.Logger Log { get; private set; }         public static NLog.Logger Archive { get; private set; }          static Logger()         {             Log = LogManager.GetLogger(&quot;Global&quot;);             Archive = LogManager.GetLogger(&quot;Archivator&quot;);         }     } <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  \u041c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Zabbix \u0437\u0430\u0441\u043b\u0443\u0436\u0438\u0432\u0430\u0435\u0442 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u043e\u0441\u0442\u0430, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u044f \u043f\u0440\u043e\u0441\u0442\u043e \u043e\u0441\u0442\u0430\u0432\u043b\u044e \u0442\u0443\u0442 \u043a\u043b\u0430\u0441\u0441, \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u044e\u0449\u0438\u0439 \u0430\u0433\u0435\u043d\u0442\u0430:<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">Zabbix<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code>namespace TopCrawler.Singleton {     \/\/\/ &lt;summary&gt;     \/\/\/ Singleton: zabbix sender class     \/\/\/ &lt;\/summary&gt;     static class Zabbix     {         public static ZabbixSender Sender { get; private set; }          static Zabbix()         {             Sender = new ZabbixSender(Config.ZabbixServer, Config.ZabbixPort);         }     }      struct ZabbixItem     {         public string Host;         public string Key;         public string Value;     }      class ZabbixSender     {         internal struct SendItem         {             \/\/ ReSharper disable InconsistentNaming - Zabbix is case sensitive             public string host;             public string key;             public string value;             public string clock;             \/\/ ReSharper restore InconsistentNaming         }          #pragma warning disable 0649         internal struct ZabbixResponse         {             public string Response;             public string Info;         }         #pragma warning restore 0649          #region --- Constants ---          public const string DefaultHeader = &quot;ZBXD\\x01&quot;;         public const string SendRequest = &quot;sender data&quot;;         public const int DefaultTimeout = 10000;          #endregion          #region --- Fields ---         private readonly DateTime _dtUnixMinTime = DateTime.SpecifyKind(new DateTime(1970, 1, 1), DateTimeKind.Utc);         private readonly int _timeout;         private readonly string _zabbixserver;         private readonly int _zabbixport;         #endregion          #region --- Constructors ---          public ZabbixSender(string zabbixserver, int zabbixport)             : this(zabbixserver, zabbixport, DefaultTimeout)         {         }          public ZabbixSender(string zabbixserver, int zabbixport, int timeout)         {             _zabbixserver = zabbixserver;             _zabbixport = zabbixport;             _timeout = timeout;         }         #endregion          #region --- Methods ---          public string SendData(ZabbixItem itm)         {             return SendData(new List&lt;ZabbixItem&gt;(1) { itm });         }          public string SendData(List&lt;ZabbixItem&gt; lstData)         {             try             {                 var serializer = new JavaScriptSerializer();                 var values = new List&lt;SendItem&gt;(lstData.Count);                  values.AddRange(lstData.Select(itm =&gt; new SendItem                 {                     host = itm.Host,                     key = itm.Key,                     value = itm.Value,                     clock = Math.Floor((DateTime.Now.ToUniversalTime() - _dtUnixMinTime).TotalSeconds).ToString(CultureInfo.InvariantCulture)                 }));                  var json = serializer.Serialize(new                 {                     request = SendRequest,                     data = values.ToArray()                 });                  var header = Encoding.ASCII.GetBytes(DefaultHeader);                 var length = BitConverter.GetBytes((long)json.Length);                 var data = Encoding.ASCII.GetBytes(json);                  var packet = new byte[header.Length + length.Length + data.Length];                 Buffer.BlockCopy(header, 0, packet, 0, header.Length);                 Buffer.BlockCopy(length, 0, packet, header.Length, length.Length);                 Buffer.BlockCopy(data, 0, packet, header.Length + length.Length, data.Length);                  using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))                 {                     socket.Connect(_zabbixserver, _zabbixport);                      socket.Send(packet);                      \/\/Header                     var buffer = new byte[5];                     ReceivData(socket, buffer, 0, buffer.Length, _timeout);                      if (DefaultHeader != Encoding.ASCII.GetString(buffer, 0, buffer.Length))                         throw new Exception(&quot;Invalid header&quot;);                      \/\/Message length                     buffer = new byte[8];                     ReceivData(socket, buffer, 0, buffer.Length, _timeout);                     var dataLength = BitConverter.ToInt32(buffer, 0);                      if (dataLength == 0)                         throw new Exception(&quot;Invalid data length&quot;);                      \/\/Message                     buffer = new byte[dataLength];                     ReceivData(socket, buffer, 0, buffer.Length, _timeout);                      var response = serializer.Deserialize&lt;ZabbixResponse&gt;(Encoding.ASCII.GetString(buffer, 0, buffer.Length));                     return string.Format(&quot;Response: {0}, Info: {1}&quot;, response.Response, response.Info);                 }             }             catch (Exception e)             {                 return string.Format(&quot;Exception: {0}&quot;, e);             }         }          private static void ReceivData(Socket pObjSocket, byte[] buffer, int offset, int size, int timeout)         {             var startTickCount = Environment.TickCount;             var received = 0;             do             {                 if (Environment.TickCount &gt; startTickCount + timeout)                     throw new TimeoutException();                  try                 {                     received += pObjSocket.Receive(buffer, offset + received, size - received, SocketFlags.None);                 }                 catch (SocketException ex)                 {                     if (ex.SocketErrorCode == SocketError.WouldBlock || ex.SocketErrorCode == SocketError.IOPending || ex.SocketErrorCode == SocketError.NoBufferSpaceAvailable)                         Thread.Sleep(30);                     else                         throw;                 }             } while (received &lt; size);         }          #endregion     } } <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  \u041a\u043e\u043d\u0444\u0438\u0433\u0438\u2026 \u041f\u043e\u0440\u0430 \u0443\u0436\u0435 \u0434\u0435\u043b\u0430\u0442\u044c \u0445\u043e\u0442\u044c \u0447\u0442\u043e-\u0442\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0435. \u0412\u043e-\u043f\u0435\u0440\u0432\u044b\u0445, \u0432 \u043a\u043e\u043d\u0444\u0438\u0433\u0430\u0445 \u0431\u0443\u0434\u0435\u043c \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u044f\u0449\u0438\u043a\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u043e\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u043c. \u0412\u043e \u0432\u0442\u043e\u0440\u044b\u0445 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 DataFlow. \u041f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u0442\u0430\u043a:<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u041a\u043e\u043d\u0444\u0438\u0433\u0438<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"xml\">    &lt;CredentialsList&gt;       &lt;credentials hostname=&quot;8.8.8.8&quot; username=&quot;popka@example.com&quot; password=&quot;123&quot; port=&quot;110&quot; type=&quot;fbl&quot; \/&gt;        &lt;credentials hostname=&quot;8.8.8.8&quot; username=&quot;kesha@example.com&quot; password=&quot;123&quot; port=&quot;110&quot; type=&quot;bounce&quot; \/&gt;     &lt;\/CredentialsList&gt;     &lt;DataFlowOptionsList&gt;       &lt;datablockoptions name=&quot;_sortMailDataBlock&quot; maxdop=&quot;4&quot; boundedcapacity=&quot;4&quot; \/&gt;       &lt;datablockoptions name=&quot;_spamFilterDataBlock&quot; maxdop=&quot;4&quot; boundedcapacity=&quot;4&quot; \/&gt;       &lt;datablockoptions name=&quot;_checkBounceDataBlock&quot; maxdop=&quot;16&quot; boundedcapacity=&quot;16&quot; \/&gt;       &lt;datablockoptions name=&quot;_identifyDataBlock&quot; maxdop=&quot;16&quot; boundedcapacity=&quot;16&quot; \/&gt;       &lt;datablockoptions name=&quot;_addToCrmDataBlock&quot; maxdop=&quot;16&quot; boundedcapacity=&quot;16&quot; \/&gt;       &lt;datablockoptions name=&quot;_addToFblDataBlock&quot; maxdop=&quot;16&quot; boundedcapacity=&quot;16&quot; \/&gt;       &lt;datablockoptions name=&quot;_addToBounceDataBlock&quot; maxdop=&quot;16&quot; boundedcapacity=&quot;16&quot; \/&gt;     &lt;\/DataFlowOptionsList&gt; <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  \u0418\u0442\u0430\u043a, \u0445\u043e\u0441\u0442 \u0438 \u043f\u043e\u0440\u0442 \u043a\u0443\u0434\u0430 \u043a\u043e\u043d\u0435\u043a\u0442\u0438\u0442\u0441\u044f, \u044e\u0437\u0435\u0440 \u0438 \u043f\u0430\u0440\u043e\u043b\u044c \u2014 \u0442\u0443\u0442 \u0432\u0441\u0451 \u043f\u043e\u043d\u044f\u0442\u043d\u043e. \u0414\u0430\u043b\u044c\u0448\u0435 \u0442\u0438\u043f \u044f\u0449\u0438\u043a\u0430. \u0414\u043e\u043f\u0443\u0441\u0442\u0438\u043c, \u0441\u043b\u0443\u0436\u0431\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u043c\u0430\u0440\u043a\u0435\u0442\u0438\u043d\u0433\u043e\u043c (\u043a\u0430\u043a \u0438 \u0434\u0440\u0443\u0433\u0438\u043c\u0438 \u043e\u0442\u0434\u0435\u043b\u0430\u043c\u0438). \u0423 \u043d\u0438\u0445 \u0435\u0441\u0442\u044c \u044f\u0449\u0438\u043a\u0438, \u043a\u0443\u0434\u0430 \u0441\u0432\u0430\u043b\u0438\u0432\u0430\u044e\u0442\u0441\u044f \u0430\u0432\u0442\u043e\u043e\u0442\u0432\u0435\u0442\u044b \u043d\u0430 \u0440\u0430\u0441\u0441\u044b\u043b\u043a\u0438, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043e\u0442\u0447\u0435\u0442\u044b \u043e \u0441\u043f\u0430\u043c\u0435 <a href=\"https:\/\/en.wikipedia.org\/wiki\/Feedback_loop_(email)\">FBL<\/a>. \u0421\u0430\u043c \u044f\u0449\u0438\u043a \u0443\u0436\u0435 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0437\u0438\u0440\u0443\u0435\u0442 \u043f\u0438\u0441\u044c\u043c\u043e, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0434\u043b\u044f \u0442\u0430\u043a\u0438\u0445 \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u0439 \u0441\u0440\u0430\u0437\u0443 \u0437\u0430\u0434\u0430\u0435\u043c \u0442\u0438\u043f \u044f\u0449\u0438\u043a\u0430. \u0421 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438 DataFlow \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043d\u044f\u0442\u043d\u043e \u0434\u0430\u043b\u044c\u0448\u0435, \u043a\u043e\u0433\u0434\u0430 \u043d\u0430\u0447\u043d\u0435\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043e\u0431\u044a\u0435\u043a\u0442\u044b. \u0422\u0443\u0442 \u0443 \u043d\u0430\u0441 \u0431\u0443\u0434\u0443\u0442 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0435 \u0441\u0435\u043a\u0446\u0438\u0438 \u0432 \u043a\u043e\u043d\u0444\u0438\u0433\u0435. \u041c\u0430\u043d\u0443\u0430\u043b\u043e\u0432 \u043a\u0443\u0447\u0430 \u043a\u0430\u043a \u044d\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u043e\u043a\u0430\u0436\u0443 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442:<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0442\u0438\u043f\u044b<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code>    #region --- Types ---     static class MailboxType     {         public const string Bo = &quot;bo&quot;;         public const string Crm = &quot;crm&quot;;         public const string Fbl = &quot;fbl&quot;;         public const string Bounce = &quot;bounce&quot;;     }      class MailboxInfo     {         public string Type { get; set; }         public string Hostname { get; set; }         public string User { get; set; }         public string Password { get; set; }         public int Port { get; set; }     }      class DataBlockOptions     {         public int Maxdop { get; set; }         public int BoundedCapacity { get; set; }          public DataBlockOptions()         {             Maxdop = 1;             BoundedCapacity = 1;         }     }     #endregion <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0441\u0435\u043a\u0446\u0438\u0438<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code>    \/\/\/ &lt;summary&gt;     \/\/\/ Custom config section     \/\/\/ &lt;\/summary&gt;     public class CustomSettingsConfigSection : ConfigurationSection     {         [ConfigurationProperty(&quot;CredentialsList&quot;)]         public CredentialsCollection CredentialItems         {             get { return base[&quot;CredentialsList&quot;] as CredentialsCollection; }         }          [ConfigurationProperty(&quot;DataFlowOptionsList&quot;)]         public DataBlockOptionsCollection DataFlowOptionsItems         {             get { return base[&quot;DataFlowOptionsList&quot;] as DataBlockOptionsCollection; }         }     } <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u0423\u0447\u0438\u043c\u0441\u044f \u0447\u0438\u0442\u0430\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0438\u0437 \u044d\u0442\u0438\u0445 \u0441\u0435\u043a\u0446\u0438\u0439<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code>    \/\/\/ &lt;summary&gt;     \/\/\/ Custom collection - credentials list     \/\/\/ &lt;\/summary&gt;     [ConfigurationCollection(typeof(CredentialsElement), AddItemName = &quot;credentials&quot;)]     public class CredentialsCollection : ConfigurationElementCollection, IEnumerable&lt;CredentialsElement&gt;     {         protected override ConfigurationElement CreateNewElement()         {             return new CredentialsElement();         }          protected override object GetElementKey(ConfigurationElement element)         {             return ((CredentialsElement)element).Username;         }          public CredentialsElement this[int index]         {             get { return BaseGet(index) as CredentialsElement; }         }          public new IEnumerator&lt;CredentialsElement&gt; GetEnumerator()         {             for (var i = 0; i &lt; Count; i++)             {                 yield return BaseGet(i) as CredentialsElement;             }         }     }      \/\/\/ &lt;summary&gt;     \/\/\/ Custom credentials item     \/\/\/ &lt;\/summary&gt;     public class CredentialsElement : ConfigurationElement     {         [ConfigurationProperty(&quot;hostname&quot;, DefaultValue = &quot;&quot;)]         public string Hostname         {             get { return base[&quot;hostname&quot;] as string; }         }          [ConfigurationProperty(&quot;username&quot;, DefaultValue = &quot;&quot;, IsKey = true)]         public string Username         {             get { return base[&quot;username&quot;] as string; }         }          [ConfigurationProperty(&quot;password&quot;, DefaultValue = &quot;&quot;)]         public string Password         {             get { return base[&quot;password&quot;] as string; }         }          [ConfigurationProperty(&quot;type&quot;, DefaultValue = &quot;&quot;)]         public string Type         {             get { return base[&quot;type&quot;] as string; }         }          [ConfigurationProperty(&quot;port&quot;, DefaultValue = &quot;&quot;)]         public string Port         {             get { return base[&quot;port&quot;] as string; }         }     }      \/\/\/ &lt;summary&gt;     \/\/\/ Custom collection - DataBlock options list     \/\/\/ &lt;\/summary&gt;     [ConfigurationCollection(typeof(DataBlockOptionsElement), AddItemName = &quot;datablockoptions&quot;)]     public class DataBlockOptionsCollection : ConfigurationElementCollection, IEnumerable&lt;DataBlockOptionsElement&gt;     {         protected override ConfigurationElement CreateNewElement()         {             return new DataBlockOptionsElement();         }          protected override object GetElementKey(ConfigurationElement element)         {             return ((DataBlockOptionsElement)element).Name;         }          public CredentialsElement this[int index]         {             get { return BaseGet(index) as CredentialsElement; }         }          public new IEnumerator&lt;DataBlockOptionsElement&gt; GetEnumerator()         {             for (var i = 0; i &lt; Count; i++)             {                 yield return BaseGet(i) as DataBlockOptionsElement;             }         }     }      \/\/\/ &lt;summary&gt;     \/\/\/ Custom DataBlock options item     \/\/\/ &lt;\/summary&gt;     public class DataBlockOptionsElement : ConfigurationElement     {         [ConfigurationProperty(&quot;name&quot;, DefaultValue = &quot;&quot;, IsKey = true)]         public string Name         {             get { return base[&quot;name&quot;] as string; }         }          [ConfigurationProperty(&quot;maxdop&quot;, DefaultValue = &quot;&quot;)]         public string Maxdop         {             get { return base[&quot;maxdop&quot;] as string; }         }          [ConfigurationProperty(&quot;boundedcapacity&quot;, DefaultValue = &quot;&quot;)]         public string BoundedCapacity         {             get { return base[&quot;boundedcapacity&quot;] as string; }         }     } <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  \u041f\u043e\u043b\u043d\u0443\u044e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u043a\u043e\u043d\u0444\u0438\u0433\u0430 \u043f\u0438\u0441\u0430\u0442\u044c \u043d\u0435 \u0431\u0443\u0434\u0443, \u043f\u043e\u0434\u0440\u0430\u0437\u0443\u043c\u0435\u0432\u0430\u0435\u0442\u0441\u044f, \u0447\u0442\u043e \u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0442\u0443\u0434\u0430 \u0431\u0443\u0434\u0443\u0442 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c\u0441\u044f \u043d\u0443\u0436\u043d\u044b\u0435 \u043d\u0430\u043c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b.<\/p>\n<p>  \u041d\u0430\u0448\u0438 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u0435\u043c \u0442\u0430\u043a:<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u0427\u0438\u0442\u0430\u0435\u043c<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code>        public List&lt;MailboxInfo&gt; CredentialsList { get; private set; }         public Dictionary&lt;string, DataBlockOptions&gt; DataFlowOptionsList { get; private set; }         ... \tstatic Config()         {             try \t\t\t{                 var customConfig = (CustomSettingsConfigSection)ConfigurationManager.GetSection(&quot;CustomSettings&quot;);  \t\t\t\t\/\/Get mailboxes \t\t\t\tforeach (var item in customConfig.CredentialItems) \t\t\t\t\tCredentialsList.Add(new MailboxInfo \t\t\t\t\t{ \t\t\t\t\t\tHostname = item.Hostname, \t\t\t\t\t\tPort = Convert.ToInt32(item.Port), \t\t\t\t\t\tUser = item.Username, \t\t\t\t\t\tType = item.Type, \t\t\t\t\t\tPassword = item.Password \t\t\t\t\t});  \t\t\t\t\/\/Get DataFlow settings \t\t\t\tforeach (var item in customConfig.DataFlowOptionsItems) \t\t\t\t\tDataFlowOptionsList.Add(item.Name, new DataBlockOptions \t\t\t\t\t{ \t\t\t\t\t\tMaxdop = Convert.ToInt32(item.Maxdop), \t\t\t\t\t\tBoundedCapacity = Convert.ToInt32(item.BoundedCapacity) \t\t\t\t\t});             }             catch (Exception ex)             {                 Logger.Log.Fatal(&quot;Error at reading config: {0}&quot;, ex.Message);                 throw;             }         } <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  <i>\u041a\u0430\u043a-\u0442\u043e \u043e\u0447\u0435\u043d\u044c \u0437\u0430\u0442\u044f\u043d\u0443\u0442\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0441\u044f, \u0430 \u043c\u044b \u0434\u0430\u0436\u0435 \u043d\u0435 \u0434\u043e\u0448\u043b\u0438 \u0434\u043e \u0441\u0430\u043c\u043e\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0433\u043e.<\/i><\/p>\n<p>  \u041e\u043f\u0443\u0441\u0442\u0438\u043c \u043f\u043e\u043a\u0430 \u043e\u0431\u0432\u044f\u0437\u043a\u0443 \u0438\u0437 TopShelf, \u0441\u0447\u0435\u0442\u0447\u0438\u043a\u0438 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438, \u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0441 \u0411\u0414 \u0438 \u043f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u043a \u0434\u0435\u043b\u0443! \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043a\u043b\u0430\u0441\u0441 Crawler \u2014 \u044f\u0434\u0440\u043e. \u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0447\u0438\u0442\u0430\u0435\u043c \u043f\u043e\u0447\u0442\u0443:<\/p>\n<pre><code>        private volatile bool _stopPipeline;         ...         public void Start()         {             do             {                 var getMailsTasks = _config.CredentialsList.Select(credentials =&gt; Task.Run(() =&gt; GetMails(credentials))).ToList();                  foreach (var task in getMailsTasks)                     task.Wait();                  Thread.Sleep(2000);             } while (!_stopPipeline);              \/\/Stop pipeline - wait for completion of all endpoints             \/\/\u0422\u0443\u0442 \u0431\u0443\u0434\u0435\u0442 \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 DataFlow \u043a\u043e\u043d\u0432\u0435\u0439\u0435\u0440\u0430             if (_stopPipeline)                 Logger.Log.Warn(&quot;Pipeline has been stopped by user&quot;);         } <\/code><\/pre>\n<p>  <i>\u0412\u043e\u0442 \u0442\u0443\u0442 \u043b\u0435\u043d\u044c \u0432\u0437\u044f\u043b\u0430 \u0441\u0432\u043e\u0435 \u0438 \u044f \u0440\u0435\u0448\u0438\u043b \u043d\u0435 \u0437\u0430\u043c\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u2014 \u0435\u0441\u043b\u0438 \u044f\u0449\u0438\u043a\u043e\u0432 \u043f\u043e\u0440\u044f\u0434\u043a\u0430 20-30 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0434 \u043a\u0430\u0436\u0434\u044b\u0439 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0442\u0430\u0441\u043a \u0438 \u043d\u0435 \u043f\u0430\u0440\u0438\u0442\u044c\u0441\u044f \u043e \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0435 \u043f\u043e\u0442\u043e\u043a\u043e\u0432. (\u0420\u0430\u0437\u0440\u0435\u0448\u0430\u044e \u0437\u0430\u043a\u0438\u0434\u0430\u0442\u044c \u043f\u043e\u043c\u0438\u0434\u043e\u0440\u0430\u043c\u0438.)<\/i><\/p>\n<p>  \u041f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u043a \u0441\u0430\u043c\u043e\u043c\u0443 \u0447\u0442\u0435\u043d\u0438\u044e:<\/p>\n<pre><code>        private void GetMails(MailboxInfo info)         {             try             {                 using (var client = new Pop3Client())                 { <\/code><\/pre>\n<p>  \u0421\u0440\u0430\u0437\u0443 \u043f\u043e\u0441\u0447\u0438\u0442\u0430\u0435\u043c \u0442\u0430\u0439\u043c\u0438\u043d\u0433\u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u044f\u0449\u0438\u043a\u0443 \u2014 \u043f\u0440\u0438\u0433\u043e\u0434\u0438\u0442\u0441\u044f \u0434\u043b\u044f \u0434\u0438\u0430\u0433\u043d\u043e\u0441\u0442\u0438\u043a\u0438 \u0441\u0435\u0442\u0438 \u0438 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u0441\u0435\u0440\u0432\u0435\u0440\u0430.<\/p>\n<pre><code>                    \/\/Get Zabbix metrics                     var stopwatch = new Stopwatch();                     stopwatch.Start();                     \/\/Get mail count                     client.Connect(info.Hostname, info.Port, false);                     client.Authenticate(info.User, info.Password);                     stopwatch.Stop(); <\/code><\/pre>\n<p>  \u041e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 Zabbix. \u0412\u0441\u0451 \u043f\u0440\u043e\u0441\u0442\u043e \u2014 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0438\u043c\u044f \u0445\u043e\u0441\u0442\u0430 (\u043a\u0430\u043a \u043e\u043d\u043e \u0437\u0430\u0432\u0435\u0434\u0435\u043d\u043e \u0432 Zabbix), \u043a\u043b\u044e\u0447 (\u043e\u043f\u044f\u0442\u044c \u0442\u0430\u043a\u0438 \u0441\u0442\u0440\u043e\u0433\u043e, \u043a\u0430\u043a \u0432 Zabbix) \u0438 \u0441\u0442\u0440\u043e\u043a\u043e\u0432\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435.<\/p>\n<pre><code>                    \/\/Send it to Zabbix                     Zabbix.Sender.SendData(new ZabbixItem { Host = Config.HostKey, Key = info.Type + Config.TimingKey, Value = stopwatch.ElapsedMilliseconds.ToString() });                     Logger.Log.Debug(&quot;Send [{0}] timing to Zabbix: connected to '{1}' as '{2}', timing {3}ms&quot;, info.Type, info.Hostname, info.User, stopwatch.ElapsedMilliseconds);                      var count = client.GetMessageCount();                     if (count == 0)                         return;                      Logger.Log.Debug(&quot;We've got new {0} messages in '{1}'&quot;, count, info.User);                     \/\/Send messages to sorting block                     for (var i = 0; i &lt; count; i++)                     {                         try                         {                             var mailInfo = new MessageInfo                             {                                 IsSpam = false,                                 Mail = client.GetMessage(i + 1),                                 Type = MessageType.UNKNOWN,                                 Subtype = null,                                 Recipient = null,                                 Mailbox = info                             };                             Logger.Log.Debug(&quot;Download message from '{0}'. Size: {1}b&quot;, info.User, mailInfo.Mail.RawMessage.Length); <\/code><\/pre>\n<p>  DataFlow pipeline \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0437\u0434\u0430\u043d\u0430 \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u043a\u043b\u0430\u0441\u0441\u0430 Crawler. \u0421\u0447\u0438\u0442\u0430\u0435\u043c, \u0447\u0442\u043e \u043d\u0430\u0448 \u043f\u0435\u0440\u0432\u044b\u0439 \u044d\u0442\u0430\u043f \u2014 \u043e\u0442\u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0438\u0441\u044c\u043c\u043e.<\/p>\n<pre><code>                            while (!_sortMailDataBlock.Post(mailInfo))                                 Thread.Sleep(500); <\/code><\/pre>\n<p>  \u0412\u0438\u0434\u0438\u0442\u0435, \u043a\u0430\u043a \u043f\u0440\u043e\u0441\u0442\u043e \u2014 \u0441\u0430\u043c \u043a\u043e\u043d\u0432\u0435\u0439\u0435\u0440 \u043e\u0434\u0438\u043d. \u0412\u0441\u0435 \u0442\u0430\u0441\u043a\u0438, \u0447\u0438\u0442\u0430\u044e\u0449\u0438\u0435 \u043f\u043e\u0447\u0442\u0443, \u043a\u0438\u0434\u0430\u044e\u0442 \u0442\u0443\u0434\u0430 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043f\u043e \u043e\u0434\u043d\u043e\u043c\u0443. \u0415\u0441\u043b\u0438 \u0431\u043b\u043e\u043a \u0437\u0430\u043d\u044f\u0442, Post \u0432\u0435\u0440\u043d\u0435\u0442 false \u0438 \u043c\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u043e\u0434\u043e\u0436\u0434\u0435\u043c \u043f\u043e\u043a\u0430 \u043e\u043d \u043d\u0435 \u043e\u0441\u0432\u043e\u0431\u043e\u0434\u0438\u0442\u0441\u044f. \u0422\u0435\u043a\u0443\u0449\u0438\u0439 \u043f\u043e\u0442\u043e\u043c \u0432 \u044d\u0442\u043e \u0432\u0440\u0435\u043c\u044f \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c. \u0412\u043e\u0442 \u044d\u0442\u043e \u044f \u043d\u0430\u0437\u044b\u0432\u0430\u044e \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u0438\u0437\u043c \u0431\u0435\u0437 \u0437\u0430\u0431\u043e\u0442. <\/p>\n<p>  \u0421\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0443\u0448\u043b\u043e \u043d\u0430 \u043a\u043e\u043d\u0432\u0435\u0439\u0435\u0440, \u0442\u0435\u043f\u0435\u0440\u044c \u0435\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u0441\u043e \u0441\u043f\u043e\u043a\u043e\u0439\u043d\u043e\u0439 \u0434\u0443\u0448\u043e\u0439 \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0432 RAW \u0430\u0440\u0445\u0438\u0432 (\u0434\u0430-\u0434\u0430! \u0432\u0441\u0451, \u0447\u0442\u043e \u0447\u0438\u0442\u0430\u0435\u043c \u2014 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u0432 \u0444\u0430\u0439\u043b\u043e\u0432\u044b\u0439 \u0430\u0440\u0445\u0438\u0432. \u0421\u043b\u0443\u0436\u0431\u0430 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 \u043d\u0430\u043c \u043f\u043e\u0442\u043e\u043c \u0441\u043a\u0430\u0436\u0435\u0442 \u0441\u043f\u0430\u0441\u0438\u0431\u043e).<\/p>\n<p>  \u041d\u0430\u0441\u0442\u0440\u043e\u0438\u043c, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0440\u043e\u0442\u0430\u0446\u0438\u044e \u0430\u0440\u0445\u0438\u0432\u0430:<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">NLog.config<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"xml\">  &lt;targets&gt;     &lt;!-- add your targets here --&gt;          &lt;target name=&quot;logfile&quot; xsi:type=&quot;File&quot; fileName=&quot;${basedir}\\logs\\${shortdate}-message.log&quot; \/&gt;     &lt;target name=&quot;Archivefile&quot; xsi:type=&quot;File&quot; fileName=&quot;${basedir}\\archive\\${shortdate}-archive.dat&quot; \/&gt;      &lt;\/targets&gt; <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  \u041f\u043e\u0442\u043e\u043c \u043d\u0430 \u043d\u0435\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0442\u0440\u0430\u0432\u0438\u0442\u044c <a href=\"https:\/\/www.elastic.co\/products\/logstash\">logStash<\/a>, \u043d\u043e \u044d\u0442\u043e \u0443\u0436\u0435 \u0434\u0440\u0443\u0433\u0430\u044f \u0438\u0441\u0442\u043e\u0440\u0438\u044f\u2026<\/p>\n<pre><code>                            \/\/Save every mail to archive                             Logger.Log.Debug(&quot;Archive message&quot;);                             Logger.Archive.Info(Functions.MessageToString(mailInfo.Mail));                         }                         catch (Exception ex)                         {                             Logger.Log.Error(&quot;Parse email error: {0}&quot;, ex.Message);                             Functions.ErrorsCounters[info.Type].Increment();                              \/\/Archive mail anyway                             Logger.Log.Debug(&quot;Archive message&quot;);                             Logger.Archive.Info(Encoding.Default.GetString(client.GetMessageAsBytes(i + 1)));                         }                          if (_config.DeleteMail)                             client.DeleteMessage(i + 1);                          if (_stopPipeline)                             break;                     }                     Logger.Log.Debug(&quot;Done with '{0}'&quot;, info.User);                 }             }             catch (Exception ex)             {                 Logger.Log.Error(&quot;General error - type: {0}, message: {1}&quot;, ex, ex.Message);                 Functions.ErrorsCounters[info.Type].Increment();             }         } <\/code><\/pre>\n<p>  \u0417\u0434\u0435\u0441\u044c \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438 \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0441\u0447\u0435\u0442\u0447\u0438\u043a\u0438 \u043e\u0448\u0438\u0431\u043e\u043a (\u0432 \u0440\u0430\u0437\u0440\u0435\u0437\u0435 \u0442\u0438\u043f\u043e\u0432 \u044f\u0449\u0438\u043a\u043e\u0432), \u0433\u0434\u0435 ErrorsCounters \u2014 \u044d\u0442\u043e:<\/p>\n<pre><code>public static Dictionary&lt;string, Counter&gt; ErrorsCounters = new Dictionary&lt;string, Counter&gt;(); <\/code><\/pre>\n<p>  \u0410 \u0441\u0430\u043c\u0438 \u0441\u0447\u0435\u0442\u0447\u0438\u043a\u0438 \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0442\u0430\u043a:<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">Counter.cs<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code>class Counter     {         private long _counter;          public Counter()         {             _counter = 0;         }          public void Increment()         {             Interlocked.Increment(ref _counter);         }          public long Read()         {             return _counter;         }          public long Refresh()         {             return Interlocked.Exchange(ref _counter, 0);         }          public void Add(long value)         {             Interlocked.Add(ref _counter, value);         }          public void Set(long value)         {             Interlocked.Exchange(ref _counter, value);         }     } <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  \u041f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u043a \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044e \u043a\u043e\u043d\u0432\u0435\u0439\u0435\u0440\u0430. \u0414\u043e\u043f\u0443\u0441\u0442\u0438\u043c, \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u044f\u0449\u0438\u043a\u0438, \u043a\u0443\u0434\u0430 \u0441\u044b\u043f\u044f\u0442\u0441\u044f \u0430\u0432\u0442\u043e\u043e\u0442\u0432\u0435\u0442\u044b. \u0422\u0430\u043a\u0438\u0435 \u043f\u0438\u0441\u044c\u043c\u0430 \u043d\u0430\u0434\u043e \u0440\u0430\u0441\u043f\u0430\u0440\u0441\u0438\u0442\u044c (\u0447\u0442\u043e \u0437\u0430 \u0430\u0432\u0442\u043e\u043e\u0442\u0432\u0435\u0442, \u043e\u0442 \u043a\u043e\u0433\u043e, \u043f\u043e \u043a\u0430\u043a\u043e\u0439 \u0440\u0430\u0441\u0441\u044b\u043b\u043a\u0435 \u0438 \u0442.\u0434.) \u0438 \u0441\u043b\u043e\u0436\u0438\u0442\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0432 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 (\u0411\u0414). \u0414\u043e\u043f\u0443\u0441\u0442\u0438\u043c, \u0435\u0441\u0442\u044c \u044f\u0449\u0438\u043a\u0438, \u043a\u0443\u0434\u0430 \u043f\u0430\u0434\u0430\u044e\u0442 FBL \u043e\u0442\u0447\u0435\u0442\u044b. \u0422\u0430\u043a\u0438\u0435 \u043f\u0438\u0441\u044c\u043c\u0430 \u0441\u0440\u0430\u0437\u0443 \u0441\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0435\u043c \u0432 \u0431\u0430\u0437\u0443. \u0412\u0441\u0435 \u043f\u0440\u043e\u0447\u0438\u0435 \u043f\u0438\u0441\u044c\u043c\u0430 \u0441\u0447\u0438\u0442\u0430\u0435\u043c \u00ab\u043f\u043e\u043b\u0435\u0437\u043d\u044b\u043c\u0438\u00bb \u2014 \u0438\u0445 \u043d\u0430\u0434\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u043d\u0430 \u0441\u043f\u0430\u043c \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u0432\u043e \u0432\u043d\u0435\u0448\u043d\u044e\u044e \u0441\u0438\u0441\u0442\u0435\u043c\u0443, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, CRM.<\/p>\n<p>  <i>\u041a\u0430\u043a \u0432\u044b \u0443\u0436\u0435 \u043f\u043e\u043d\u044f\u043b\u0438, \u0434\u0430\u043d\u043d\u044b\u0439 \u043f\u0440\u0438\u043c\u0435\u0440 \u0432 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u043c \u0440\u0430\u0441\u0441\u043c\u0430\u0442\u0440\u0438\u0432\u0430\u0435\u0442 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0441\u0431\u043e\u0440\u0449\u0438\u043a\u0430 \u0434\u043b\u044f \u0437\u0430\u0434\u0430\u0447 \u043c\u0430\u0440\u043a\u0435\u0442\u0438\u043d\u0433\u0430 \u2014 \u0441\u0431\u043e\u0440 \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0438 \u043f\u043e \u0434\u043e\u0441\u0442\u0430\u0432\u043a\u0435 \u043f\u043e\u0447\u0442\u044b, \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u0441\u043f\u0430\u043c\u0435.<\/i><\/p>\n<p>  \u0418\u0442\u0430\u043a, \u043c\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043b\u0438\u0441\u044c \u0441 \u0440\u0430\u0431\u043e\u0447\u0438\u043c \u043f\u043e\u0442\u043e\u043a\u043e\u043c. \u041e\u0431\u044a\u044f\u0432\u043b\u044f\u0435\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0431\u043b\u043e\u043a\u0438 \u0432 \u043a\u043b\u0430\u0441\u0441\u0435 Crawler:<\/p>\n<pre><code>class MessageInfo     {         public bool IsSpam { get; set; }         public Message Mail { get; set; }         public string Subtype { get; set; }         public string Recipient { get; set; }         public MessageType Type { get; set; }         public MailboxInfo Mailbox { get; set; }     }  class Crawler     {         \/\/Pipeline         private TransformBlock&lt;MessageInfo, MessageInfo&gt; _sortMailDataBlock;         private TransformBlock&lt;MessageInfo, MessageInfo&gt; _spamFilterDataBlock;         private TransformBlock&lt;MessageInfo, MessageInfo&gt; _checkBounceDataBlock;         private TransformBlock&lt;MessageInfo, MessageInfo&gt; _identifyDataBlock;         private ActionBlock&lt;MessageInfo&gt; _addToCrmDataBlock;         private ActionBlock&lt;MessageInfo&gt; _addToFblDataBlock;         private ActionBlock&lt;MessageInfo&gt; _addToBounceDataBlock;         ... <\/code><\/pre>\n<p>  \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043c\u0435\u0442\u043e\u0434 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0431\u043b\u043e\u043a\u0438 \u043a\u043e\u043d\u0432\u0435\u0439\u0435\u0440\u0430 (\u0434\u043b\u044f \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0431\u043b\u043e\u043a\u043e\u0432 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u043d\u0430\u0448\u0438 \u0437\u0430\u043c\u0435\u0447\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0441\u0435\u043a\u0446\u0438\u0438 \u0438\u0437 \u043a\u043e\u043d\u0444\u0438\u0433\u043e\u0432):<\/p>\n<pre><code>      public void Init()         {             \/\/*** Create pipeline ***             \/\/Create TransformBlock to get message type             var blockOptions = _config.GetDataBlockOptions(&quot;_sortMailDataBlock&quot;);             _sortMailDataBlock = new TransformBlock&lt;MessageInfo, MessageInfo&gt;(mail =&gt; SortMail(mail),                 new ExecutionDataflowBlockOptions                 {                     MaxDegreeOfParallelism = blockOptions.Maxdop,                     BoundedCapacity = blockOptions.BoundedCapacity                 });             \/\/Create TransformBlock to filter spam             blockOptions = _config.GetDataBlockOptions(&quot;_spamFilterDataBlock&quot;);             _spamFilterDataBlock = new TransformBlock&lt;MessageInfo, MessageInfo&gt;(mail =&gt; FilterSpam(mail),                 new ExecutionDataflowBlockOptions                 {                     MaxDegreeOfParallelism = blockOptions.Maxdop,                     BoundedCapacity = blockOptions.BoundedCapacity                 });             \/\/Create TransformBlock to sort bounces             blockOptions = _config.GetDataBlockOptions(&quot;_checkBounceDataBlock&quot;);             _checkBounceDataBlock = new TransformBlock&lt;MessageInfo, MessageInfo&gt;(mail =&gt; BounceTypeCheck(mail),                 new ExecutionDataflowBlockOptions                 {                     MaxDegreeOfParallelism = blockOptions.Maxdop,                     BoundedCapacity = blockOptions.BoundedCapacity                 });             \/\/Create TransformBlock to identify bounce owner             blockOptions = _config.GetDataBlockOptions(&quot;_identifyDataBlock&quot;);             _identifyDataBlock = new TransformBlock&lt;MessageInfo, MessageInfo&gt;(mail =&gt; GetRecipient(mail),                 new ExecutionDataflowBlockOptions                 {                     MaxDegreeOfParallelism = blockOptions.Maxdop,                     BoundedCapacity = blockOptions.BoundedCapacity                 });             \/\/Create ActionBlock to send mail to CRM             blockOptions = _config.GetDataBlockOptions(&quot;_addToCrmDataBlock&quot;);             _addToCrmDataBlock = new ActionBlock&lt;MessageInfo&gt;(mail =&gt; AddToCrm(mail),                 new ExecutionDataflowBlockOptions                 {                     MaxDegreeOfParallelism = blockOptions.Maxdop,                     BoundedCapacity = blockOptions.BoundedCapacity                 });             \/\/Create ActionBlock to send FBL to MailWH             blockOptions = _config.GetDataBlockOptions(&quot;_addToFblDataBlock&quot;);             _addToFblDataBlock = new ActionBlock&lt;MessageInfo&gt;(mail =&gt; AddToFbl(mail),                 new ExecutionDataflowBlockOptions                 {                     MaxDegreeOfParallelism = blockOptions.Maxdop,                     BoundedCapacity = blockOptions.BoundedCapacity                 });             \/\/Create ActionBlock to send Bounce to MailWH             blockOptions = _config.GetDataBlockOptions(&quot;_addToBounceDataBlock&quot;);             _addToBounceDataBlock = new ActionBlock&lt;MessageInfo&gt;(mail =&gt; AddToBounce(mail),                 new ExecutionDataflowBlockOptions                 {                     MaxDegreeOfParallelism = blockOptions.Maxdop,                     BoundedCapacity = blockOptions.BoundedCapacity                 }); <\/code><\/pre>\n<p>  \u0421\u043e\u0431\u0438\u0440\u0430\u0435\u043c \u043a\u043e\u043d\u0432\u0435\u0439\u0435\u0440 \u0432 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0438 \u0441 \u043d\u0430\u0448\u0435\u0439 \u0441\u0445\u0435\u043c\u043e\u0439:<\/p>\n<pre><code>            \/\/*** Build pipeline ***             _sortMailDataBlock.LinkTo(_spamFilterDataBlock, info =&gt; info.Type == MessageType.GENERAL);             _sortMailDataBlock.LinkTo(_addToFblDataBlock, info =&gt; info.Type == MessageType.FBL);             _sortMailDataBlock.LinkTo(_checkBounceDataBlock, info =&gt; info.Type == MessageType.BOUNCE);             _sortMailDataBlock.LinkTo(DataflowBlock.NullTarget&lt;MessageInfo&gt;(), info =&gt; info.Type == MessageType.UNKNOWN); \/*STUB*\/             _checkBounceDataBlock.LinkTo(_identifyDataBlock);             _identifyDataBlock.LinkTo(_addToBounceDataBlock);             _spamFilterDataBlock.LinkTo(_addToCrmDataBlock, info =&gt; !info.IsSpam);             _spamFilterDataBlock.LinkTo(DataflowBlock.NullTarget&lt;MessageInfo&gt;(), info =&gt; info.IsSpam); \/*STUB*\/ <\/code><\/pre>\n<p>  \u041a\u0430\u043a \u0432\u0438\u0434\u0438\u043c, \u0432\u0441\u0451 \u043f\u0440\u0435\u0434\u0435\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u2014 \u0441\u0432\u044f\u0437\u044b\u0432\u0430\u0435\u043c \u0431\u043b\u043e\u043a \u0441\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c (\u0441 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c\u044e \u0437\u0430\u0434\u0430\u043d\u0438\u044f \u0443\u0441\u043b\u043e\u0432\u0438\u044f \u0441\u0432\u044f\u0437\u0438). \u0412\u0441\u0435 \u0431\u043b\u043e\u043a\u0438 \u0438\u0441\u043f\u043e\u043b\u043d\u044f\u044e\u0442\u0441\u044f \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e. \u041a\u0430\u0436\u0434\u044b\u0439 \u0431\u043b\u043e\u043a \u0438\u043c\u0435\u0435\u0442 \u0441\u0442\u0435\u043f\u0435\u043d\u044c \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u0438\u0437\u043c\u0430 \u0438 \u0435\u043c\u043a\u043e\u0441\u0442\u044c (\u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0435\u043c\u043a\u043e\u0441\u0442\u0438 \u043c\u043e\u0436\u043d\u043e \u0440\u0435\u0433\u0443\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u043f\u0435\u0440\u0435\u0434 \u0431\u043b\u043e\u043a\u043e\u043c, \u0442\u043e \u0435\u0441\u0442\u044c \u0431\u043b\u043e\u043a \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043f\u0440\u0438\u043d\u044f\u043b, \u043d\u043e \u0435\u0449\u0435 \u043d\u0435 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442). \u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u0434\u0430\u0432\u0430\u0442\u044c \u0432\u044b\u0441\u043e\u043a\u0443\u044e \u0441\u0442\u0435\u043f\u0435\u043d\u044c \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u0438\u0437\u043c\u0430 \u0434\u043b\u044f \u00ab\u0441\u043b\u043e\u0436\u043d\u044b\u0445\u00bb \u0438 \u0434\u043e\u043b\u0433\u0438\u0445 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439, \u043a\u0430\u043a, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043f\u0430\u0440\u0441\u0438\u043d\u0433 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0433\u043e \u043f\u0438\u0441\u044c\u043c\u0430.<\/p>\n<p>  <i>\u041d\u0435 \u0431\u0443\u0434\u0443 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u043c\u0430\u0442\u0447\u0430\u0441\u0442\u044c DataFlow, \u043b\u0443\u0447\u0448\u0435 \u0432\u0441\u0451 \u043f\u0440\u043e\u0447\u0435\u0441\u0442\u044c \u0432 \u043f\u0435\u0440\u0432\u043e\u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0435 <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/hh228603(v=vs.110).aspx\">TPL DataFlow<\/a><\/i>.<\/p>\n<p>  \u0414\u0430\u043b\u0435\u0435 \u0437\u0430\u0434\u0430\u0435\u043c \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u0432\u044b\u0445\u043e\u0434\u0430 \u0438\u0437 \u0431\u043b\u043e\u043a\u0430:<\/p>\n<pre><code>            _sortMailDataBlock.Completion.ContinueWith(t =&gt;             {                 if (t.IsFaulted) ((IDataflowBlock)_spamFilterDataBlock).Fault(t.Exception);                 else _spamFilterDataBlock.Complete();             });             _sortMailDataBlock.Completion.ContinueWith(t =&gt;             {                 if (t.IsFaulted) ((IDataflowBlock)_addToFblDataBlock).Fault(t.Exception);                 else _addToFblDataBlock.Complete();             });             _sortMailDataBlock.Completion.ContinueWith(t =&gt;             {                 if (t.IsFaulted) ((IDataflowBlock)_checkBounceDataBlock).Fault(t.Exception);                 else _checkBounceDataBlock.Complete();             });             _spamFilterDataBlock.Completion.ContinueWith(t =&gt;             {                 if (t.IsFaulted) ((IDataflowBlock)_addToCrmDataBlock).Fault(t.Exception);                 else _addToCrmDataBlock.Complete();             });             _checkBounceDataBlock.Completion.ContinueWith(t =&gt;             {                 if (t.IsFaulted) ((IDataflowBlock)_identifyDataBlock).Fault(t.Exception);                 else _identifyDataBlock.Complete();             });             _identifyDataBlock.Completion.ContinueWith(t =&gt;             {                 if (t.IsFaulted) ((IDataflowBlock)_addToBounceDataBlock).Fault(t.Exception);                 else _addToBounceDataBlock.Complete();             });         } <\/code><\/pre>\n<p>  \u0412\u0441\u0451, \u043d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435 \u043a\u043e\u043d\u0432\u0435\u0439\u0435\u0440 \u0443\u0436\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442, \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u0442\u0438\u0442\u044c \u0432 \u043d\u0435\u0433\u043e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f. \u041e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0435\u0433\u043e \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0432 \u043d\u0430\u0448 \u043c\u0435\u0442\u043e\u0434 Start:<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">Start<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code>       public void Start()         {             do             {                 var getMailsTasks = _config.CredentialsList.Select(credentials =&gt; Task.Run(() =&gt; GetMails(credentials))).ToList();                  foreach (var task in getMailsTasks)                     task.Wait();                  Thread.Sleep(2000);             } while (!_stopPipeline);              \/\/Stop pipeline - wait for completion of all endpoints             _sortMailDataBlock.Complete();             _addToCrmDataBlock.Completion.Wait();             _addToFblDataBlock.Completion.Wait();             _addToBounceDataBlock.Completion.Wait();             if (_stopPipeline)                 Logger.Log.Warn(&quot;Pipeline has been stopped by user&quot;);         } <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  \u041f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u043a \u0434\u0435\u043b\u0435\u0433\u0430\u0442\u0430\u043c. <br \/>  \u0421\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0430\u2026 \u041d\u0443, \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c \u0443 \u043d\u0430\u0441 \u0432\u0441\u0451 \u043f\u0440\u043e\u0441\u0442\u043e (\u0443\u0441\u043b\u043e\u0436\u043d\u0438\u0442\u044c \u0442\u043e \u0432\u0441\u0435\u0433\u0434\u0430 \u0443\u0441\u043f\u0435\u0435\u043c):<\/p>\n<pre><code>       private MessageInfo SortMail(MessageInfo mail)         {             switch (mail.Mailbox.Type)             {                 case MailboxType.Crm:                     mail.Type = MessageType.GENERAL;                     break;                 case MailboxType.Bounce:                     mail.Type = MessageType.BOUNCE;                     break;                 case MailboxType.Fbl:                     mail.Type = MessageType.FBL;                     break;             }             return mail;         } <\/code><\/pre>\n<p>  \u0421\u043f\u0430\u043c \u0444\u0438\u043b\u044c\u0442\u0440. \u042d\u0442\u043e \u043d\u0430 \u0434\u043e\u043c\u0430\u0448\u043d\u044e\u044e \u0440\u0430\u0431\u043e\u0442\u0443 \u2014 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 <a href=\"http:\/\/spamassassin.apache.org\/\">SpamAssassin<\/a>.<br \/>  \u0412\u043e\u0442 \u0432\u0430\u043c \u0434\u0435\u043b\u0435\u0433\u0430\u0442:<\/p>\n<pre><code>        private MessageInfo FilterSpam(MessageInfo mail)         {             \/\/TODO: Add SpamAssassin logic             return mail;         } <\/code><\/pre>\n<p>  \u0418 \u043a\u043b\u0430\u0441\u0441\u044b \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 API SpamAssassin (<a href=\"http:\/\/www.codeproject.com\/Articles\/13106\/A-C-Wrapper-for-the-SpamAssassin-Protocol\">\u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043f\u0440\u043e\u0435\u043a\u0442<\/a>).<br \/>  \u0410 \u043c\u044b \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u043a \u043f\u0430\u0440\u0441\u0438\u043d\u0433\u0443 \u043f\u0438\u0441\u0435\u043c. \u041f\u0430\u0440\u0441\u0438\u043c \u043c\u044b \u0430\u0432\u0442\u043e\u043e\u0442\u0432\u0435\u0442\u044b. \u0422\u0443\u0442 \u0432\u0441\u0442\u0443\u043f\u0430\u0435\u0442 \u0432 \u0434\u0435\u043b\u043e MEF. <br \/>  \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442 (dll) \u0441 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430\u043c\u0438 \u0434\u043b\u044f \u043d\u0430\u0448\u0438\u0445 \u043f\u043b\u0430\u0433\u0438\u043d\u043e\u0432 (\u041d\u0430\u0437\u043e\u0432\u0435\u043c Interfaces).<br \/>  \u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441:<\/p>\n<pre><code>    public interface ICondition     {         string Check(Message mimeMessage);     }      public interface IConditionMetadata     {         Type Type { get; }     } <\/code><\/pre>\n<p>  \u0418\u2026 \u0432\u0441\u0451. \u041d\u0430\u0448 TopCrawler \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u044d\u0442\u043e\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0438 \u043f\u0440\u043e\u0435\u043a\u0442 \u0441 \u043f\u043b\u0430\u0433\u0438\u043d\u0430\u043c\u0438 \u0442\u043e\u0436\u0435 \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0435\u0433\u043e.<br \/>  \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043d\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 (\u0442\u043e\u0436\u0435 dll), \u043d\u0430\u0437\u043e\u0432\u0435\u043c Conditions.<br \/>  \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0442\u0438\u043f\u044b \u0430\u0432\u0442\u043e\u043e\u0442\u0432\u0435\u0442\u043e\u0432:<\/p>\n<pre><code>    #region --- Types ---     static class BounceType     {         public const string Full = &quot;BounceTypeFull&quot;;         public const string Timeout = &quot;BounceTypeTimeout&quot;;         public const string Refused = &quot;BounceTypeRefused&quot;;         public const string NotFound = &quot;BounceTypeNotFound&quot;;         public const string Inactive = &quot;BounceTypeInactive&quot;;         public const string OutOfOffice = &quot;BounceTypeOutOfOffice&quot;;         public const string HostNotFound = &quot;BounceTypeHostNotFound&quot;;         public const string NotAuthorized = &quot;BounceTypeNotAuthorized&quot;;         public const string ManyConnections = &quot;BounceTypeManyConnections&quot;;     }     #endregion <\/code><\/pre>\n<p>  \u0418 \u043a\u043b\u0430\u0441\u0441\u044b, \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u044e\u0449\u0438\u0435 \u043d\u0430\u0448 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441:<\/p>\n<pre><code>    [Export(typeof(ICondition))]     [ExportMetadata(&quot;Type&quot;, typeof(ConditionNotFound1))]     public class ConditionNotFound1 : ICondition     {         public string Check(Message mimeMessage)         {             if (!mimeMessage.MessagePart.IsMultiPart)                 return null;              const string pattern = &quot;Diagnostic-Code:.+smtp.+550&quot;;             var regexp = new Regex(pattern, RegexOptions.IgnoreCase);              return mimeMessage.MessagePart.MessageParts.Any(part =&gt; part.ContentType.MediaType == &quot;message\/delivery-status&quot; && regexp.IsMatch(part.GetBodyAsText())) ? BounceType.NotFound : null;         }     }    ...    [Export(typeof(ICondition))]     [ExportMetadata(&quot;Type&quot;, typeof(ConditionTimeout2))]     public class ConditionTimeout2 : ICondition     {             return BounceType.Timeout;             }     ... <\/code><\/pre>\n<p>  \u041a\u0430\u043a \u0432\u044b \u0437\u0430\u043c\u0435\u0442\u0438\u043b\u0438\u0431 \u0432\u0441\u0451 \u0434\u0435\u043b\u043e \u0432 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u0430\u0445. \u0421 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043d\u0438\u0445 \u043f\u043b\u0430\u0433\u0438\u043d\u044b \u0438 \u0431\u0443\u0434\u0443\u0442 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u044b.<br \/>  \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u0441\u044f \u043a \u043d\u0430\u0448\u0435\u043c\u0443 \u043f\u0440\u043e\u0435\u043a\u0442\u0443 \u0438 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u043c \u043f\u043b\u0430\u0433\u0438\u043d\u044b:<\/p>\n<pre><code>    class Crawler     {         ...         \/\/Plugins         [ImportMany]         public IEnumerable&lt;Lazy&lt;ICondition, IConditionMetadata&gt;&gt; BounceTypeConditions { get; set; }          private void LoadPlugins()         {             try             {                 var container = new CompositionContainer(new DirectoryCatalog(_config.PluginDirectory), true);                 container.ComposeParts(this);             }             catch (Exception ex)             {                 Logger.Log.Error(&quot;Unable to load plugins: {0}&quot;, ex.Message);             }         }        ... <\/code><\/pre>\n<p>  LoadPlugins \u0434\u0435\u0440\u0433\u0430\u0435\u043c \u0432 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440\u0435 \u043d\u0430\u0448\u0435\u0433\u043e \u043a\u043b\u0430\u0441\u0441\u0430. \u041e\u0431\u044a\u044f\u0441\u043d\u044f\u0442\u044c \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u043f\u0440\u043e \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u043d\u0435 \u0431\u0443\u0434\u0443 \u2014 \u0433\u0443\u0433\u043b \u0441\u043f\u0440\u0430\u0432\u0438\u0442\u0441\u044f \u043b\u0443\u0447\u0448\u0435.<\/p>\n<p>  \u041f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u043a \u043d\u0430\u0448\u0435\u043c\u0443 \u0434\u0435\u043b\u0435\u0433\u0430\u0442\u0443 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0442\u0438\u043f\u0430 Bounce. \u0423\u0441\u043b\u043e\u0432\u0438\u044f \u0431\u0443\u0434\u0443\u0442 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0442\u044c\u0441\u044f \u043f\u043e \u043e\u0447\u0435\u0440\u0435\u0434\u0438, \u043f\u043e\u043a\u0430 \u043d\u0435 \u0441\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043f\u0435\u0440\u0432\u043e\u0435 \u2014 \u0438\u0441\u043a\u043b\u044e\u0447\u0430\u044e\u0449\u0438\u0439 \u043c\u0435\u0442\u043e\u0434:<\/p>\n<pre><code>       private MessageInfo BounceTypeCheck(MessageInfo mailInfo)         {             try             {                 foreach (var condition in BounceTypeConditions)                 {                     var res = condition.Value.Check(mailInfo.Mail);                     if (res == null)                         continue;                      mailInfo.Subtype = res;                     Logger.Log.Debug(&quot;Bounce type condition [{0}] triggered for message [{1}]&quot;, condition.Metadata.Type, mailInfo.Mail.Headers.MessageId);                     break;                 }             }             catch (Exception ex)             {                 Logger.Log.Error(&quot;Failed to determine bounce type for message '{0}': {1}&quot;, mailInfo.Mail.Headers.MessageId, ex.Message);                 Logger.ErrorsCounters[MailboxType.Bounce].Increment();             }             return mailInfo;         } <\/code><\/pre>\n<p>  \u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c\u0431 \u0435\u0441\u043b\u0438 \u043f\u043e\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043d\u043e\u0432\u0430\u044f \u043b\u043e\u0433\u0438\u043a\u0430\u0431 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432 \u043f\u0440\u043e\u0435\u043a\u0442 \u0441 \u043f\u043b\u0430\u0433\u0438\u043d\u0430\u043c\u0438 \u043d\u043e\u0432\u044b\u0439 \u043a\u043b\u0430\u0441\u0441, \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u044e\u0449\u0438\u0439 \u043d\u0430\u0448 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0438 \u2014 \u0432\u0443\u0430\u043b\u044f! <i>\u041f\u0440\u0438\u043c\u0435\u0440 \u0432\u0442\u043e\u0440\u043e\u0433\u043e \u043f\u043b\u0430\u0433\u0438\u043d\u0430 \u043f\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044e \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u0435\u043b\u044f \u043f\u0438\u0441\u044c\u043c\u0430 \u043f\u0440\u0438\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0442\u044c \u043d\u0435 \u0431\u0443\u0434\u0443 \u2014 \u0438\u0442\u0430\u043a \u0443\u0436\u0435 \u0434\u043b\u0438\u043d\u043d\u044b\u0439 \u043f\u043e\u0441\u0442 (\u0410\u0432\u0442\u043e\u043e\u0442\u0432\u0435\u0442 \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043b \u0441\u0430\u043c \u0441\u0435\u0440\u0432\u0435\u0440, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u0435\u043b\u044f \u0442\u043e\u0436\u0435 \u043d\u0430\u0434\u043e \u0440\u0430\u0441\u043f\u0430\u0440\u0441\u0438\u0442\u044c \u0438\u0437 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u043e\u0432 \u043f\u0438\u0441\u044c\u043c\u0430)<\/i>.<\/p>\n<p>  \u0421 \u0437\u0430\u043f\u0438\u0441\u044c\u044e \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432 \u0432 \u0411\u0414 \u0442\u043e\u0436\u0435 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435\u043e\u0431\u044b\u0447\u043d\u043e\u0433\u043e. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0442\u0430\u043a:<\/p>\n<pre><code>       private void AddToBounce(MessageInfo mail)         {             try             {                 MailWH.BounceAdd(mail);                 Functions.ProcessedCounters[MailboxType.Bounce].Increment();                 Functions.Log.Debug(&quot;Send Bounce to MailWH&quot;);             }             catch (Exception ex)             {                 Functions.Log.Error(&quot;Error saving Bounce message '{0}' to MailWH: {1}&quot;, mail.Mail.Headers.MessageId, ex.Message);                 Functions.ErrorsCounters[MailboxType.Bounce].Increment();             }         } <\/code><\/pre>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">BounceAdd<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code>       public static long BounceAdd(MessageInfo message)         {             using (var conn = new SqlConnection(ConnectionString))             using (var cmd = new SqlDataAdapter(&quot;BounceAdd&quot;, conn))             {                 var body = message.Mail.FindFirstPlainTextVersion() == null                     ? message.Mail.FindFirstHtmlVersion().GetBodyAsText()                     : message.Mail.FindFirstPlainTextVersion().GetBodyAsText();                  var outId = new SqlParameter(&quot;@ID&quot;, SqlDbType.BigInt)                 {                     Direction = ParameterDirection.Output                 };                 cmd.SelectCommand.CommandType = CommandType.StoredProcedure;                 cmd.SelectCommand.Parameters.Add(new SqlParameter(&quot;@RawMessage&quot;, message.Mail.RawMessage));                 cmd.SelectCommand.Parameters.Add(new SqlParameter(&quot;@Message&quot;, body));                 cmd.SelectCommand.Parameters.Add(new SqlParameter(&quot;@Subject&quot;, message.Mail.Headers.Subject ?? &quot;&quot;));                 cmd.SelectCommand.Parameters.Add(new SqlParameter(&quot;@MessageID&quot;, message.Mail.Headers.MessageId ?? &quot;&quot;));                 cmd.SelectCommand.Parameters.Add(new SqlParameter(&quot;@AddressTo&quot;, message.Mail.Headers.To[0].Address ?? &quot;&quot;));                 cmd.SelectCommand.Parameters.Add(new SqlParameter(&quot;@AddressFrom&quot;, message.Mail.Headers.From.Address ?? &quot;&quot;));                 cmd.SelectCommand.Parameters.Add(new SqlParameter(&quot;@DateRecieved&quot;, DateTime.Now));                 cmd.SelectCommand.Parameters.Add(new SqlParameter(&quot;@BounceTypeSysName&quot;, (object)message.Subtype ?? DBNull.Value));                 cmd.SelectCommand.Parameters.Add(new SqlParameter(&quot;@SourceFrom&quot;, (object)message.Recipient ?? DBNull.Value));                 \/\/ TODO: Add ListId support                 cmd.SelectCommand.Parameters.Add(new SqlParameter(&quot;@ListId&quot;, DBNull.Value));                 cmd.SelectCommand.Parameters.Add(outId);                  conn.Open();                 cmd.SelectCommand.ExecuteNonQuery();                  return outId.Value as long? ?? 0;             }         } <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  \u041f\u0440\u043e\u0441\u0442\u0438\u0442\u0435, \u0447\u0442\u043e \u043d\u0435 \u0443\u0441\u043f\u0435\u043b \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c TopShelf \u2014 \u043f\u043e\u0441\u0442 \u0438 \u0442\u0430\u043a \u0443\u0436\u0435 \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u0440\u0430\u0437\u0434\u0443\u043b\u0441\u044f.<\/p>\n<h2>\u0412\u044b\u0432\u043e\u0434\u044b<\/h2>\n<p>  \u0412 \u044d\u0442\u043e\u043c \u0443\u0440\u043e\u043a\u0435 \u043c\u044b \u0443\u0437\u043d\u0430\u043b\u0438, \u0447\u0442\u043e \u0437\u0430\u0434\u0430\u0447\u0430 \u0441\u0431\u043e\u0440\u0430 \u043f\u043e\u0447\u0442\u044b \u043c\u043e\u0436\u0435\u0442 \u043e\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f \u043d\u0435 \u0442\u0430\u043a\u043e\u0439 \u043f\u0440\u043e\u0441\u0442\u043e\u0439. \u0420\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u043d\u043e\u0435 \u044f\u0434\u0440\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0431\u044b\u0441\u0442\u0440\u043e \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u043d\u043e\u0432\u044b\u0435 \u0448\u0430\u0433\u0438 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430 \u2014 DataFlow-\u0431\u043b\u043e\u043a\u0438, \u043d\u0435 \u0437\u0430\u0442\u0440\u0430\u0433\u0438\u0432\u0430\u044f \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0443\u044e \u043b\u043e\u0433\u0438\u043a\u0443. \u041f\u043e\u0434\u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u043f\u043b\u0430\u0433\u0438\u043d\u043e\u0432 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0431\u044b\u0441\u0442\u0440\u043e \u043d\u0430\u0440\u0430\u0449\u0438\u0432\u0430\u0442\u044c \u0441\u043a\u0440\u0438\u043f\u0442\u043e\u043f\u043e\u0434\u043e\u0431\u043d\u0443\u044e \u043b\u043e\u0433\u0438\u043a\u0443 \u043f\u0430\u0440\u0441\u0438\u043d\u0433\u0430, \u0430 \u0441\u0430\u043c DataFlow \u0440\u0430\u0441\u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u0438\u0432\u0430\u0435\u0442 \u0432\u0441\u0435 \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u044f (\u043f\u0440\u0438\u0447\u0435\u043c \u043c\u044b \u0438\u043c\u0435\u0435\u043c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0433\u0438\u0431\u043a\u043e \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0442\u044c \u043c\u043d\u043e\u0433\u043e\u043f\u043e\u0442\u043e\u0447\u043d\u043e\u0441\u0442\u044c \u043f\u043e\u0434 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u0443\u044e \u043c\u0430\u0448\u0438\u043d\u0443). TopShelf \u0434\u0430\u0435\u0442 \u043d\u0430\u043c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u0441\u0435\u0440\u0432\u0438\u0441 \u043a\u0430\u043a \u0432 \u0440\u0435\u0436\u0438\u043c\u0435 \u0441\u043b\u0443\u0436\u0431\u044b, \u0442\u0430\u043a \u0438 \u0432 \u043a\u043e\u043d\u0441\u043e\u043b\u044c\u043d\u043e\u043c \u0440\u0435\u0436\u0438\u043c\u0435 \u0434\u043b\u044f \u043e\u0431\u043b\u0435\u0433\u0447\u0435\u043d\u0438\u044f \u043e\u0442\u043b\u0430\u0434\u043a\u0438.<\/p>\n<p>  \u0424\u0443\u0445\u2026 \u0415\u0441\u043b\u0438 \u0431\u0443\u0434\u0435\u0442 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e, \u0434\u0430\u043b\u044c\u0448\u0435 \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u044d\u0442\u043e \u043d\u0430 \u0440\u0435\u043b\u044c\u0441\u044b Continious Integration, \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0430\u0432\u0442\u043e\u0431\u0438\u043b\u0434\u044b \u0438 \u0432\u044b\u043f\u0443\u0441\u043a \u0440\u0435\u043b\u0438\u0437\u0430 \u0447\u0435\u0440\u0435\u0437 <a href=\"https:\/\/www.visualstudio.com\/en-us\/features\/release-management-vs.aspx\">VS Release Management<\/a>.             <\/p>\n<div class=\"clear\"><\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"http:\/\/habrahabr.ru\/post\/262613\/\"> http:\/\/habrahabr.ru\/post\/262613\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<br \/>\n<h4>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043f\u0440\u0435\u0434\u0438\u0441\u043b\u043e\u0432\u0438\u044f<\/h4>\n<p>  \u041d\u0430\u0432\u0435\u0440\u043d\u043e\u0435, \u043c\u043d\u043e\u0433\u0438\u0435 \u0438\u0437 \u0432\u0430\u0441 \u0432 \u0441\u0432\u043e\u0435\u0439 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435 \u0441\u0442\u0430\u043b\u043a\u0438\u0432\u0430\u043b\u0438\u0441\u044c \u0441 \u0437\u0430\u0434\u0430\u0447\u0435\u0439 \u0441\u0431\u043e\u0440\u0430 \u043f\u043e\u0447\u0442\u044b \u0441 \u0440\u044f\u0434\u0430 \u044f\u0449\u0438\u043a\u043e\u0432. \u0417\u0430\u0447\u0435\u043c \u044d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043d\u0443\u0436\u043d\u043e? \u041d\u0430\u0432\u0435\u0440\u043d\u043e\u0435, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u044d\u0442\u043e \u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u044b\u0439 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u043e\u0431\u043c\u0435\u043d\u0430 \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u043c\u0435\u0436\u0434\u0443 \u0441\u0438\u0441\u0442\u0435\u043c\u0430\u043c\u0438. \u041c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a \u043f\u043e\u0434 \u043b\u044e\u0431\u044b\u0435 \u044f\u0437\u044b\u043a\u0438, \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u044e\u0449\u0438\u0445 SMTP, POP3, IMAP, \u0433\u043e\u0442\u043e\u0432\u044b\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u043f\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0441\u0442\u044d\u043a\u0430 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 (\u043a\u0430\u043a \u044f \u0441\u043b\u043e\u0436\u043d\u043e \u043d\u0430\u0437\u0432\u0430\u043b \u043f\u043e\u0447\u0442\u043e\u0432\u044b\u0439 \u044f\u0449\u0438\u043a&#8230;) \u0438 \u0442.\u0434. <\/p>\n<p>  \u041d\u0435\u0443\u0434\u0438\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e, \u0447\u0442\u043e \u043c\u043d\u043e\u0433\u0438\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u044e\u0442\u0441\u044f \u0438\u043c\u0435\u043d\u043d\u043e \u0447\u0435\u0440\u0435\u0437 \u043f\u043e\u0447\u0442\u0443. \u0422\u0443\u0442 \u0432 \u0434\u0435\u043b\u043e \u0432\u0441\u0442\u0443\u043f\u0430\u0435\u0442 \u043d\u0435\u043a\u0438\u0439 \u0441\u0435\u0440\u0432\u0438\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0443\u043c\u0435\u0435\u0442 \u044d\u0442\u0443 \u043f\u043e\u0447\u0442\u0443 \u0431\u044b\u0441\u0442\u0440\u043e \u0437\u0430\u0431\u0438\u0440\u0430\u0442\u044c, \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f.<\/p>\n<p>  \u041a\u043e\u043c\u0443 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u043d\u043e\u0433\u043e \u043d\u0438\u0436\u0435 \u043a\u043e\u0434\u0430 \u2014 \u0434\u0430\u043b\u044c\u0448\u0435 \u043c\u043e\u0433\u0443\u0442 \u043d\u0435 \u0447\u0438\u0442\u0430\u0442\u044c:<\/p>\n<pre><code>         foreach (var mailbox in mailboxes)                 using (var client = new Pop3Client())                 {                     client.Connect(Hostname, Port, false);                     client.Authenticate(User, Password);                      var count = client.GetMessageCount();                     for (var i = 0; i &lt; count; i++)                     {                         Mail = client.GetMessage(i + 1);                         var cat = SortMail(Mail);                         DoSomething(Mail, cat);                     }                 } <\/code><\/pre>\n<p>  <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-261441","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/261441","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=261441"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/261441\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=261441"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=261441"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=261441"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}