Как быстро и правильно отбирать письма в почтовом ящике средствами 1С без полного перебора

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Работа с электронной почтой — одна из частых задач в автоматизации бизнес-процессов на платформе 1С. Будь то регулярная загрузка заказов покупателей, обработка отчетов от контрагентов или разбор системных уведомлений, разработчики регулярно сталкиваются с необходимостью отбирать конкретные письма. Если нужно обработать заказы клиентов в 1С:Бухгалтерии 3.0, можно применить автоматическое создание счетов и договоров по письмам с заказами.

Типичная ошибка начинающих разработчиков — скачивание всех заголовков или даже писем целиком из почтового ящика с последующим перебором в цикле средствами встроенного языка. Если в почтовом ящике накопились тысячи писем, такое регламентное задание начинает выполняться часами, перегружая оперативную память сервера и перегружая сетевой трафик. В этой статье мы подробно разберем, как оптимизировать этот процесс, научимся использовать встроенные механизмы фильтрации протокола IMAP и обойдем типичные ограничения кодировок кириллицы.

В чем главная проблема медленного отбора писем?

Давайте проанализируем фрагмент кода, с которого часто начинают разработку интеграции с почтой:


Профиль = Новый ИнтернетПочтовыйПрофиль;
// Заполнение настроек профиля...
Почта = Новый ИнтернетПочта;
Почта.Подключиться(Профиль);

ТемаПисьма = "Тема нужного письма";
Заголовки = Почта.ПолучитьЗаголовки();

Индексы = Новый СписокЗначений;
Для каждого Письмо из Заголовки Цикл
    Если Письмо.Тема <> ТемаПисьма Тогда
         Индексы.Добавить(Заголовки.Найти(Письмо));
    КонецЕсли;
КонецЦикла;

Для каждого Инд ИЗ Индексы Цикл
    Заголовки.Удалить(Индексы.Значение);
КонецЦикла;

НужныеПисьма = Почта.Выбрать(Истина, Заголовки);

Выясним причину, почему данный алгоритм работает медленно. Метод ПолучитьЗаголовки() без параметров заставляет платформу 1С обратиться к почтовому серверу и запросить информацию обо всех без исключения письмах, хранящихся в текущей папке (обычно это "Входящие"). Если в ящике находится 10 000 писем, система будет вынуждена прокачать через сеть метаданные каждого объекта. Локальный перебор и удаление элементов из коллекции в памяти 1С лишь усугубляют ситуацию.

Более того, в приведенном коде содержится логическая ошибка при удалении элементов. Обход коллекции Индексы и удаление элементов по значению без учета изменения индексов оставшейся коллекции приведет к непредсказуемым результатам или ошибкам времени выполнения.

Чтобы кардинально ускорить работу, нам необходимо перенести задачу фильтрации писем на сторону почтового сервера. Для этого используется современный протокол IMAP и специальные структуры отбора.

Ограничения протоколов: POP3 против IMAP

Прежде чем переходить к написанию кода, важно сделать принципиальный выбор протокола подключения. В 1С поддерживаются два основных протокола получения почты: POP3 и IMAP.

  1. POP3 (Post Office Protocol v3) — устаревший протокол. Он не поддерживает полноценную работу со структурой папок на сервере и, что критично для нашей задачи, не умеет фильтровать заголовки писем на стороне сервера. Метод ПолучитьЗаголовки() при работе через POP3 проигнорирует любые переданные параметры отбора и всегда вернет полный список писем.
  2. IMAP (Internet Message Access Protocol) — современный и многофункциональный протокол. Он позволяет выполнять сложный поиск и фильтрацию писем прямо на сервере. В 1С при работе по IMAP мы можем передать в метод ПолучитьЗаголовки() специальную структуру параметров отбора. Сервер сам найдет подходящие письма и вернет нам только их заголовки. Именно этот протокол мы и будем использовать.

Важное примечание: Если при попытке подключиться по протоколу IMAP вы получаете системную ошибку вида "Произошла ошибка соединения с сервером при работе с IMAP. Код ошибки: 4", обратите внимание на настройки вашего почтового провайдера. В современных почтовых сервисах (Яндекс.Почта, Mail.ru, Gmail) по умолчанию отключен доступ сторонних приложений по протоколу IMAP. Вам необходимо зайти в веб-интерфейс почтового ящика, включить эту опцию в настройках безопасности, а также сгенерировать специальный "Пароль для внешних приложений" вместо использования основного пароля от учетной записи.

Использование параметров отбора IMAP в 1С

Для фильтрации писем на сервере платформа 1С предлагает использовать объект Структура, ключами которой выступают предопределенные параметры поиска. Ниже приведен полный список доступных параметров отбора для IMAP, поддерживаемых платформой (их можно писать как на русском, так и на английском языке):

Рассмотрим простой пример использования серверного отбора по дате:


ПараметрыОтбораIMAP = Новый Структура;
ПараметрыОтбораIMAP.Вставить("ПослеДатыОтправления", ТекущаяДата() - 86400); // Письма за последние сутки
Заголовки = Почта.ПолучитьЗаголовки(ПараметрыОтбораIMAP);

Проблема поиска по теме на русском языке (кириллица в UTF-8)

На практике многие разработчики сталкиваются с тем, что отбор по теме (например, ПараметрыОтбораIMAP.Вставить("Тема", "Заказ покупателя")) не возвращает результаты или приводит к ошибкам. Разберем по шагам, почему так происходит.

Первая причина — ошибки синтаксиса встроенного языка. Начинающие программисты иногда путают типы данных и передают строку туда, где требуется другой тип, либо наоборот. Посмотрите на ошибочную конструкцию:


// ОШИБКА! Метод Дата() не сможет преобразовать произвольный текст в дату
ПараметрыОтбораIMAP.Вставить("Тема", Дата("Тест темы письма"));

Такой код вызовет аварийное завершение работы программы. Корректная передача строкового параметра выглядит следующим образом:

ПараметрыОтбораIMAP.Вставить("Тема", "Тест темы письма");

Вторая причина гораздо глубже и кроется в технологических особенностях протокола IMAP и кодировок. Когда мы передаем в структуру отбора параметры, содержащие исключительно латинские символы, платформа 1С общается с сервером в стандартной кодировке US-ASCII. Если же в строке поиска присутствует кириллица, 1С отправляет поисковый запрос серверу в формате UTF-8.

Почтовые серверы ведут себя в этой ситуации по-разному. Крупные публичные сервисы (например, Яндекс или Gmail) без проблем обрабатывают UTF-8 запросы поиска. Однако многие корпоративные почтовые серверы (особенно старых версий или со специфическими настройками безопасности) не поддерживают расширение IMAP для поиска в некодированном UTF-8 (стандарт IMAP SEARCH CHARSET UTF-8). Получив такой запрос, сервер либо возвращает пустой результат (игнорируя фильтр), либо обрывает соединение с ошибкой.

Оптимальное решение: комбинированный подход

Чтобы ваше регламентное задание работало максимально быстро, стабильно и не зависело от капризов кодировок почтовых серверов, рекомендуется применять комбинированный подход.

Суть подхода заключается в разделении фильтрации на два этапа:

  1. Серверный отбор по надежным ASCII-параметрам. Мы отсекаем основной массив старых и ненужных писем еще на стороне сервера, используя параметры, которые гарантированно не вызовут проблем с кодировкой (даты, латинские адреса отправителей, статус прочтения). Это позволяет моментально сократить объем скачиваемых заголовков с десятков тысяч до единиц.
  2. Клиентский отбор в коде 1С. Получив микроскопический массив заголовков, мы быстро и безопасно проверяем точное совпадение кириллической темы письма на стороне 1С при помощи функции СтрНайти().

Давайте подробно разберем готовый шаблон кода, реализующий эту лучшую практику:


// Шаг 1. Создание и настройка профиля подключения по IMAP
Профиль = Новый ИнтернетПочтовыйПрофиль;
Профиль.АдресСервераIMAP = "imap.yandex.ru";
Профиль.ПортIMAP = 993;
Профиль.ИспользоватьSSLIMAP = Истина;
Профиль.Пользователь = "robot@mycompany.ru";
Профиль.Пароль = "secure_app_password_123";
Профиль.ВремяОжидания = 30; // Тайм-аут соединения в секундах

Почта = Новый ИнтернетПочта;

Попытка
    Почта.Подключиться(Профиль);
Исключение
    ЗаписьЖурналаРегистрации("Почта.Подключение",
        УровеньЖурналаРегистрации.Ошибка, , ,
        "Ошибка подключения к почте: " + ОписаниеОшибки());
    Возврат;
КонецПопытки;

// Шаг 2. Формирование безопасного серверного отбора
ПараметрыОтбора = Новый Структура;
// Исключаем старые письма (например, берем только за последние 2 дня)
ПараметрыОтбора.Вставить("ПослеДатыОтправления", НачалоДня(ТекущаяДата() - 86400 * 2));
// Если мы знаем точный адрес отправителя (только латиница) - обязательно указываем его
ПараметрыОтбора.Вставить("Отправитель", "supplier@partner.com"); 
// Скачиваем только новые, непрочитанные сообщения
ПараметрыОтбора.Вставить("Прочитанные", Ложь);

// Получаем с сервера облегченные заголовки отфильтрованных писем
Заголовки = Почта.ПолучитьЗаголовки(ПараметрыОтбора);

// Массив для хранения заголовков, прошедших финальную проверку темы
ОтобранныеЗаголовки = Новый Массив;
ШаблонТемы = "Реестр платежей"; // Русскоязычная тема для поиска

// Шаг 3. Локальная фильтрация по кириллической теме
Для каждого Заголовок Из Заголовки Цикл
    // Используем регистронезависимый поиск подстроки
    Если СтрНайти(НРег(Заголовок.Тема), НРег(ШаблонТемы)) > 0 Тогда
        ОтобранныеЗаголовки.Добавить(Заголовок);
    КонецЕсли;
КонецЦикла;

// Шаг 4. Скачивание полных писем (включая тело и вложения) только для нужных заголовков
Если ОтобранныеЗаголовки.Количество() > 0 Тогда
    
    // Передаем Ложь в первый параметр, чтобы не помечать письма прочитанными на сервере раньше времени
    Письма = Почта.Выбрать(Ложь, ОтобранныеЗаголовки);
    
    Для каждого Письмо Из Письма Цикл
        // Здесь выполняется логика обработки конкретного письма
        Сообщить("Успешно получено письмо: " + Письмо.Тема + " от " + Письмо.Отправитель.Адрес);
        
        // Помечаем обработанное письмо прочитанным на сервере (опционально)
        Почта.УстановитьИдентификаторПапки("INBOX");
        Почта.УстановитьФлагиПисьма(Письмо.Идентификатор[0], "Seen");
    КонецЦикла;
    
КонецЕсли;

// Шаг 5. Обязательное закрытие соединения
Почта.Отключиться();

Резюме

Используя предложенный алгоритм, мы избавляемся от необходимости перебирать терабайты ненужной почты на сервере 1С. Основную работу по фильтрации берет на себя база данных почтового сервера, а встроенный язык 1С используется локально для точечной проверки строковых реквизитов. Это гарантирует бесперебойную и быструю работу ваших фоновых процессов в любых конфигурациях на платформе 1С:Предприятие.

← На главную