Получение электронных писем с сервера Exchange и закрытие задач по письму

Приведенный ниже сценарий выполняет следующее:

  • получение всех входящих писем из определенного почтового ящика (сервер Exchange, протокол MAPI/RPC);
  • определение по теме письма задачи (в данной представляют интерес задачи о постановке новых задач пользователю. Как добавить Id задачи в тему письма, написано ниже);
  • закрытие задачи по определенному переходу (поиск перехода по названию).

Заранее необходимо объявить следующие пространства имен:

using System;
using System.IO;
using System.Linq;
using Aspose.Email;
using Aspose.Email.Exchange;
using Aspose.Email.Mail;
using Aspose.Email.Outlook;
using EleWise.ELMA.API;
using EleWise.ELMA.Files;
using EleWise.ELMA.Model.Managers;
using EleWise.ELMA.Model.Services;
using EleWise.ELMA.Runtime.Managers;
using EleWise.ELMA.Services;
using EleWise.ELMA.Tasks.Models;
using EleWise.ELMA.Workflow.BPMN.Diagrams.Elements;
using EleWise.ELMA.Workflow.Models;
using EleWise.ELMA.Workflow.Services;

В сценарии используется контекстная переменная:

context.Initiator - переменная типа "Пользователь".

Текст сценария: 

string resubject = "RE: ";// будет добавлять к теме при отправлен ответа
string remessage = "";// текст ответного письма
            
//настройки почты
string sendFrom = "example@example.ru";
string mailboxURI = "https://mail.example.ru/EWS/exchange.asmx";
string username = "User";
string password = "password";
string domain = "exapmle";
            
//Создаем IEWSClient
IEWSClient client = EWSClient.GetEWSClient (mailboxURI, username, password, domain);
ExchangeMailboxInfo mailboxInfo = client.GetMailboxInfo ();
            
string rootUri = client.GetMailboxInfo ().RootUri;
//Получаем список всех папок
ExchangeFolderInfoCollection folderInfoCollection = client.ListSubFolders (rootUri);
//Перебираем папки
foreach (ExchangeFolderInfo folderInfo in folderInfoCollection) 
{
    //Создаем фильтр - будем получать только непрочитанные письма
string resubject = "RE: ";// будет добавлять к теме при отправлен ответа
string remessage = "";// текст ответного письма
            
//настройки почты
string sendFrom = "example@example.ru";
string mailboxURI = "https://mail.example.ru/EWS/exchange.asmx";
string username = "User";
string password = "password";
string domain = "exapmle";
            
//Создаем IEWSClient
IEWSClient client = EWSClient.GetEWSClient (mailboxURI, username, password, domain);
ExchangeMailboxInfo mailboxInfo = client.GetMailboxInfo ();
            
string rootUri = client.GetMailboxInfo ().RootUri;
//Получаем список всех папок
ExchangeFolderInfoCollection folderInfoCollection = client.ListSubFolders (rootUri);
//Перебираем папки
foreach (ExchangeFolderInfo folderInfo in folderInfoCollection) 
{
    //Создаем фильтр - будем получать только непрочитанные письма
    MailQuery query = new MailQuery ("’IsRead’ = ’False’");
    ExchangeMessageInfoCollection msgInfoColl = client.ListMessages (folderInfo.Uri, query); // получаем все непрочитанные письма из папки
                
    //Перебираем все письма
    foreach (ExchangeMessageInfo mail in msgInfoColl) 
    {
        long num = 0;

        if (!mail.IsRead)//если письмо не прочитано - берем в обработку
        {
            client.SetReadFlag (mail.UniqueUri, true); //помечаем письмо как прочитанное                        
            resubject = resubject + mail.Subject.ToString (); //формируем тему ответного письма                        
            var message = client.FetchMessage (mail.UniqueUri); //получаем письмо по uri
            remessage = message.Body; //получаем текст ответного письма                        
            string subject = mail.Subject.ToString (); //получаем тему входящего письма
                        
            string taskstat = ""; //сюда будет записано сообщение для получателя
                        
            //получение Id задачи
            if (subject.Contains ("#")) //тема содержит # + номер
            {
                int number;
                var countv = subject.Split (’#’).Count () - 1;
                string splitvar = subject.Split (’#’) [countv]; //получаем символы после #
                bool result = Int32.TryParse (splitvar, out number); //пробуем преобразовать строку в число
                            
                if (result)//если удалось получить Id
                {
                    num = Convert.ToInt32 (splitvar);
                }
                else// не удалось получить Id
                {
                     num = 0;
                }
            }
            else//тема не содержит # + номер
            {
                num = 0;
            }
                        
            if (num > 0)//если получили Id
            {
                var task = EntityManager<TaskBase>.Instance.LoadOrNull (num); //получаем задачу по Id
                            
                if (task != null) //если удалось получить задачу по Id
                {
                    var workfl = task.WorkflowBookmark.Instance; //получаем экземпляр процесса
                                
                    //проверяем статус задачи
                    if (task.Status == TaskBaseStatus.Complete) {
                        taskstat = "Ваш ответ не доставлен до получателя по причине: Исходная задача уже выполнена. Обратитесь к адресату другим способом.";
                     }
                    if (workfl != null)//если процесс найден
                     {
                        //проверяем статус процесса
                        if (workfl.Status != WorkflowInstanceStatus.Running) 
                        {
                            taskstat = "Ваш ответ не доставлен до получателя по причине: Процесс завершен. Обратитесь к адресату другим способом.";
                        }
                        if ((workfl.Status == WorkflowInstanceStatus.Running) && (task.Status!= TaskBaseStatus.Complete))//"процесс запущен, задача не завершена"
                        {
                            //записываем текст письма в контекст экземпляра процесса, контекстная переменная OtvetSEmail типа "Строка"
                            var OtvetSEmail = (string)workfl.Context.GetType ().GetProperty ("OtvetSEmail").GetValue (workfl.Context, null);
                            OtvetSEmail += (string.IsNullOrEmpty (OtvetSEmail) ? "" : "\r\n") + remessage;
                            workfl.Context.GetType ().GetProperty ("OtvetSEmail").SetValue (workfl.Context, OtvetSEmail, null);
                                        
                            //получаем вложения письма
                            if (message.Attachments.Count > 0) {
                            for (int att = 0; att < message.Attachments.Count; att++) 
                                {
                                    //создаем вложение ELMA
                                    var newAtt = InterfaceActivator.Create<EleWise.ELMA.Common.Models.Attachment> ();
                                    //создаем файл из потока
                                    newAtt.File = CreateBinaryFile (message.Attachments [att].ContentStream, "Вложение №" + att.ToString ());
                                    //и заполняем стандартные свойства вложения
                                    newAtt.CreationAuthor = context.Initiator;
                                    newAtt.CreationDate = DateTime.Now;
                                    newAtt.Save ();
                                                                                                
                                    //добавляем вложение в контекст экземпляра процесса VlozheniyaSEmail (Вложение, список)
                                    dynamic procContext = workfl.Context;
                                    var itemType = procContext.VlozheniyaSEmail.GetType ().GetInterface (typeof(Iesi.Collections.Generic.ISet<>).FullName).GetGenericArguments () [0];
                                    var item = InterfaceActivator.Create (itemType);
                                    procContext.VlozheniyaSEmail.Add (newAtt);
                                }
                            }
                                        
                            //Получаем элемент диаграммы процесса, по которому сформирована задача
                            var element = (BPMNFlowElement)task.WorkflowBookmark.Instance.Process.Diagram.Elements.Single (e => e.Uid == task.WorkflowBookmark.ElementUid);
                            //Ищем исходящий переход по его имени. Так как может быть одна из двух задач, ищем соответсвие одному из двух переходов.
                            var connector = element.OutputConnectors.FirstOrDefault (c => (c.Name == "Письмо загружено" || c.Name == "Готово"));
                            if (connector != null)//если переход найден
                            {
                                //Формируем данные для исполнения задачи
                                var executeData = new WorkflowTaskExecuteData (task, connector.Uid);
                                //Исполняем задачу
                                Locator.GetServiceNotNull<IWorkflowRuntimeService> ().Execute (executeData);
                                taskstat = "Ваш ответ был загружен в систему. Задача выполнена.";
                            }
                        }
                    }
                }
                else 
                {
                    taskstat = "Ваш ответ не доставлен до получателя по причине: Исходная задача не найдена, " + "возможно была изменена тема письма. Обратитесь к адресату другим способом.";
                }
            }
            else 
            {
                taskstat = "Ваш ответ не доставлен до получателя по причине: Исходная задача не найдена, " + "возможно была изменена тема письма. Обратитесь к адресату другим способом.";
            }
                        
            // формирование текста ответного письма 
            remessage = taskstat + "\r\n\r\n-----Original Message-----\r\n" + "\r\nОт: " + message.From 
            + "\r\nДата: " + message.Date + "\r\nКому: " + message.To + "\r\nТема: " + message.Subject + "\r\n" + remessage;
                        
            // отправка ответного письма
            var msg = new Aspose.Email.Mail.MailMessage ();
            msg.From = sendFrom;
            msg.To = message.From.Address.ToString ();
            msg.Subject = resubject;
            msg.Body = remessage; 
            client.Send (msg);
        }
    }
}
; ExchangeMessageInfoCollection msgInfoColl = client.ListMessages (folderInfo.Uri, query); // получаем все непрочитанные письма из папки //Перебираем все письма foreach (ExchangeMessageInfo mail in msgInfoColl) { long num = 0; if (!mail.IsRead)//если письмо не прочитано - берем в обработку { client.SetReadFlag (mail.UniqueUri, true); //помечаем письмо как прочитанное resubject = resubject + mail.Subject.ToString (); //формируем тему ответного письма var message = client.FetchMessage (mail.UniqueUri); //получаем письмо по uri remessage = message.Body; //получаем текст ответного письма string subject = mail.Subject.ToString (); //получаем тему входящего письма string taskstat = ""; //сюда будет записано сообщение для получателя //получение Id задачи if (subject.Contains ("#")) //тема содержит # + номер { int number; var countv = subject.Split (’#’).Count () - 1; string splitvar = subject.Split (’#’) [countv]; //получаем символы после # bool result = Int32.TryParse (splitvar, out number); //пробуем преобразовать строку в число if (result)//если удалось получить Id { num = Convert.ToInt32 (splitvar); } else// не удалось получить Id { num = 0; } } else//тема не содержит # + номер { num = 0; } if (num > 0)//если получили Id { var task = EntityManager<TaskBase>.Instance.LoadOrNull (num); //получаем задачу по Id if (task != null) //если удалось получить задачу по Id { var workfl = task.WorkflowBookmark.Instance; //получаем экземпляр процесса //проверяем статус задачи if (task.Status == TaskBaseStatus.Complete) { taskstat = "Ваш ответ не доставлен до получателя по причине: Исходная задача уже выполнена. Обратитесь к адресату другим способом."; } if (workfl != null)//если процесс найден { //проверяем статус процесса if (workfl.Status != WorkflowInstanceStatus.Running) { taskstat = "Ваш ответ не доставлен до получателя по причине: Процесс завершен. Обратитесь к адресату другим способом."; } if ((workfl.Status == WorkflowInstanceStatus.Running) && (task.Status!= TaskBaseStatus.Complete))//"процесс запущен, задача не завершена" { //записываем текст письма в контекст экземпляра процесса, контекстная переменная OtvetSEmail типа "Строка" var OtvetSEmail = (string)workfl.Context.GetType ().GetProperty ("OtvetSEmail").GetValue (workfl.Context, null); OtvetSEmail += (string.IsNullOrEmpty (OtvetSEmail) ? "" : "\r\n") + remessage; workfl.Context.GetType ().GetProperty ("OtvetSEmail").SetValue (workfl.Context, OtvetSEmail, null); //получаем вложения письма if (message.Attachments.Count > 0) { for (int att = 0; att < message.Attachments.Count; att++) { //создаем вложение ELMA var newAtt = InterfaceActivator.Create<EleWise.ELMA.Common.Models.Attachment> (); //создаем файл из потока newAtt.File = CreateBinaryFile (message.Attachments [att].ContentStream, "Вложение №" + att.ToString ()); //и заполняем стандартные свойства вложения newAtt.CreationAuthor = context.Initiator; newAtt.CreationDate = DateTime.Now; newAtt.Save (); //добавляем вложение в контекст экземпляра процесса VlozheniyaSEmail (Вложение, список) dynamic procContext = workfl.Context; var itemType = procContext.VlozheniyaSEmail.GetType ().GetInterface (typeof(Iesi.Collections.Generic.ISet<>).FullName).GetGenericArguments () [0]; var item = InterfaceActivator.Create (itemType); procContext.VlozheniyaSEmail.Add (newAtt); } } //Получаем элемент диаграммы процесса, по которому сформирована задача var element = (BPMNFlowElement)task.WorkflowBookmark.Instance.Process.Diagram.Elements.Single (e => e.Uid == task.WorkflowBookmark.ElementUid); //Ищем исходящий переход по его имени. Так как может быть одна из двух задач, ищем соответсвие одному из двух переходов. var connector = element.OutputConnectors.FirstOrDefault (c => (c.Name == "Письмо загружено" || c.Name == "Готово")); if (connector != null)//если переход найден { //Формируем данные для исполнения задачи var executeData = new WorkflowTaskExecuteData (task, connector.Uid); //Исполняем задачу Locator.GetServiceNotNull<IWorkflowRuntimeService> ().Execute (executeData); taskstat = "Ваш ответ был загружен в систему. Задача выполнена."; } } } } else { taskstat = "Ваш ответ не доставлен до получателя по причине: Исходная задача не найдена, " + "возможно была изменена тема письма. Обратитесь к адресату другим способом."; } } else { taskstat = "Ваш ответ не доставлен до получателя по причине: Исходная задача не найдена, " + "возможно была изменена тема письма. Обратитесь к адресату другим способом."; } // формирование текста ответного письма remessage = taskstat + "\r\n\r\n-----Original Message-----\r\n" + "\r\nОт: " + message.From + "\r\nДата: " + message.Date + "\r\nКому: " + message.To + "\r\nТема: " + message.Subject + "\r\n" + remessage; // отправка ответного письма var msg = new Aspose.Email.Mail.MailMessage (); msg.From = sendFrom; msg.To = message.From.Address.ToString (); msg.Subject = resubject; msg.Body = remessage; client.Send (msg); } } }

Для создания файла из потока используется метод:

private BinaryFile CreateBinaryFile (Stream stream, string fileName)
{
    var temp = BinaryFile.CreateContentFilePath (fileName);
    using (var fs = new FileStream (temp, FileMode.CreateNew, FileAccess.Write)) {
        stream.Seek (0, SeekOrigin.Begin);
        stream.CopyTo (fs);
    }
    var mimeMappingService = Locator.GetServiceNotNull<IMimeMappingService> ();
    var contractTemplate = new BinaryFile {
        ContentType = mimeMappingService.GetTypeByExtension (Path.GetExtension (fileName)),
        Name = Path.GetFileName (fileName),
        ContentFilePath = temp,
        CreateDate = DateTime.Now,
    };
    DataAccessManager.FileManager.SaveFile (contractTemplate);
    return contractTemplate;
}

Для добавления Id задачи к теме письма, необходимо внести изменение в файл шаблона оповещения. Файл шаблона оповещения размещен по пути: ELMA3-Standart\UserConfig\Notifications\ Workflow.Task. Необходимо добавить {SR(’ #’)}{$New.Id} в строку: 

<Subject>
{if {$RecipientSet}=’ExecutorReplaced’}(@{$New.Executor}){end if}({SR(’Новая задача’)}) ’{$New.Subject}’ {SR(’в процессе’)} {$New.WorkflowBookmark.Instance.Name} ({$New.WorkflowBookmark.Instance.Process.Name}){SR(’ #’)}{$New.Id}
</Subject>