Получение электронных писем с сервера 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);
        }
    }
}
Внимание!
При копировании из браузера может возникнуть проблема, связанная с неправильным отображением апострофов. На рисунке ниже приведен пример того, как должны выглядеть апострофы.
Если использовать некорректные апострофы, сценарий не будет работать. Необходимо заменить их на корректные (это можно сделать при помощи сочетания клавиш Ctrl+F, Заменить).

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

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 задачи к теме письма, необходимо внести изменение в файл шаблона оповещения. Файл шаблона оповещения размещен по пути: .../<Общая папка с файлами системы ELMA>\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>