Старший брат 1С:Элемент еще не вышел для широкой публики, попробуем обойтись скриптами. Не то чтобы данная статья была обучающей, но отдельные моменты расскажу подробней, так как язык новый, информации по нему мало, а примеров использования и того меньше.
Установим Исполнитель версии (U), VSC с плагином и начнем немного нестандартно - объявим две структуры. Как и перечисления, это теперь нестандартная коллекция с более широкими возможностями, но в нашем случае сильно мудрить не будем, просто вложим одну в другую.
Для тех, кто совсем не знаком с исполнителем - для улучшение читаемости пару замечаний.
Строка=Строка+"новенькое" это Строка +="Новенькое"
в строке \ экранируем как \\
Структуры пригодятся нам сильно позже - чтобы мы не гадали, как они в коде появились.
Объявляем метод Скрипт() и стучим на наш IMAP сервер с почтой
метод Скрипт()
пер Параметры = новый ПараметрыПодключенияImap(Сервер(), Порт(), новый АутентификацияПочтыПоПаролю(Логин(),Пароль()),ПараметрыЗащищенногоСоединения()) //Отступление первое
исп Соединение = новый СоединениеImap(Параметры)
пер Входящие = Соединение.ПолучитьКаталог(ВидКаталогаПочты.Входящие)
Входящие.Открыть(РежимОткрытияКаталогаImap.Чтение)
пер Письма = ПолучитьВсеПисьма(Входящие) // зависит от потребности, ниже есть метод ПолучитьНепрочитанныеПисьма()
Логин() и т.д. можно, конечно, было бы передать в параметрах к методу Скрипт, но мы для удобства пока вынесем в отдельные методы
Обещанное в комментариях к коду отступление первое - насколько я понимаю, подключается Исполнитель сейчас только к IMAPS (порт 993/TCP/TLS) - а вот в STARTTLS на порту imap (порт 143/TCP) пока не умеет - прошу учитывать.
И второй момент - если вы подключаетесь к своему внутреннему IMAP серверу, то вполне можете столкнуться с ошибкой "The server selected protocol version TLS10 is not accepted by client preferences [TLS13, TLS12]" - В таком случае надо обратиться к файлу java.security и в параметре jdk.tls.disabledAlgorithms удалить TLS10 или TLS11 (Прокатывает только в исполнителе U версии).
При подключении в зависимости от бизнес-логики нам могут понадобиться как непрочитанные письма, так и весь ящик, привожу оба метода
Теперь продолжим наш "главный скрипт"
для текПисьмо из Письма // Начинаем обход полученной коллекции. Будет немного масла масляного - у объекта ПисьмоВСоединенииImap надо будет обратиться к унаследуемому свойству Письмо
пер ОтправительЗначение = НайтиЕмейл(текПисьмо.Письмо.Отправитель.Адрес.ВСтроку()) // здесь у нас MIME BASE64 но мне ФИО отправителя не нужно, поэтому безжалостно выкусываю
пер ДатаИВремяОтправленияЗначение = текПисьмо.Письмо.ДатаОтправления.ВСтроку()
пер ИндексОтсечкиДаты = ДатаИВремяОтправленияЗначение.Найти("T")
пер ДатаОтправленияЗначение = ДатаИВремяОтправленияЗначение.ПодстрокаСНачала(ИндексОтсечкиДаты) // От даты и время оставляем дату - по желанию
пер ИДСообщенияЗначение = текПисьмо.Письмо.ИдСообщения.ВСтроку()
пер ИндексОтсечки = ИДСообщенияЗначение.Найти("@")
пер ИДСообщенияКраткоеЗначение = ИДСообщенияЗначение.ПодстрокаСНачала(ИндексОтсечки) // оставили от идентификатора сообщения кусок до @ - потом идет адрес IMAP сервера
ИДСообщенияКраткоеЗначение = ИДСообщенияКраткоеЗначение.Заменить("<","") // и убрали артефакты
пер УИДЗначение = текПисьмо.Uid.ВСтроку() //Уникальный идентификатор письма, не глобальный - именно этому получателю
пер ЕмейлыТекстаЗначение = НайтиЕмейл(текПисьмо.Письмо.ПолучитьТекстКакСтроку()) //Если письмо переслали для "молотилки" - нужные адреса электронной почты будут в тексте письма - выбираем
пер ИмяФайлаJSON = ДатаОтправленияЗначение +"_"+ ОтправительЗначение +"_"+ УИДЗначение + ".txt"
Если посмотреть на всю эту красоту в отладке, то в некоторых переменных (например, ОтправительЗначение) может иметь вид
=?UTF-8?B?лалалаBase64код ==? normalniy@email.ru. Это так называемый MIME BASE64 и пришла пора для Отступление 2
Исполнитель сейчас умеет немного в Кодировку и даже синглтон для этого есть подходящий - но в данном случае он сумеет только разобрать из BASE64 зашитый там UTF. Если же там WIN1251 или 1252 - ну не повезло. В случае с отправителем нам для этой задачи неважно раскодировать ФИО отправителя, email-то там "красивый" незакодированный, но дальше все-таки придется.
Поэтому в случае с отправителем и телом письма - просто выкусываем оттуда регулярками все email, для идентификации клиента этого вполне достаточно. Сложного тут ничего нет - все понятно из комментариев
метод НайтиЕмейл(ТекстДляПоиска:Строка): Строка
знч ОбразецДляПоискаСочетаний = '([A-Za-z0-9]+[.\-_])*[A-Za-z0-9]+@[A-Za-z0-9-]+(\.[A-Z|a-z]{2,})+' //Это не строка - тип "Образец" для регулярки, объявляется литералом
знч РезультатПоиска = ОбразецДляПоискаСочетаний.НайтиСовпадения(ТекстДляПоиска) //получаем массив email и обходим слегка допилив напильником
пер НайденныйЕмейл = ""
для ОчереднойРезультат из РезультатПоиска
НайденныйЕмейл += ОчереднойРезультат.Значение()+ ","
;
если НайденныйЕмейл.ЗаканчиваетсяНа(",")
НайденныйЕмейл = НайденныйЕмейл.ПодстрокаСНачала(НайденныйЕмейл.Длина()-1) //Уберем последнюю запятую для красоты
;
НайденныйЕмейл = НайденныйЕмейл.Заменить("mailto:","") //если текст писльма HTML то возможны подобные артефакты, убираем, не стал добавлять в регулярку для читаемости
возврат НайденныйЕмейл
;
Дальше начинаем работу с вложениями почтового сообщения
пер НомерВложенияЗначение = 1
пер МассивВложенийЗначение:Массив<ОписаниеВложения>
для текВложение из текПисьмо.Письмо.Вложения
пер ИмяВложенияНекодированноеЗначение = текВложение.ВСтроку()
пер ПрограммноеИмяВложенияЗначение = ДатаОтправленияЗначение +"_"+ ОтправительЗначение +"_"+ УИДЗначение+"_attachment_"+ НомерВложенияЗначение
пер ИмяВложенияраскодированноеЗначение = ПытаемсяРаскодироватьMIME_BASE64(ИмяВложенияНекодированноеЗначение) //Отступление второе
пер ТипСодержимогоЗначение = текВложение.ТипСодержимого // MIME-тип вложения
пер СохраняемФайл = Истина
// Дальше немного жести - всеми правдами и неправдами пытаемся понять, нужный ли это файл - Отступление второе
Естественно, имя вложения у нас тоже закодировано MIME BASE64, да еще и кусками - тут закодировано, - тут не закодировано, тут рыбу заворачиваем и все это в одной строке. Придется постараться - имя файла-то нам важно, мы ж xls ждем, а тут чего только нет вплоть до картинок. Метод по раскодированию привожу ниже - но он с нюансом под наши требования, можете его легко доработать под универсальную функциональность, подробности в комментариях
метод ПытаемсяРаскодироватьMIME_BASE64(ТекстДляДекодирования:Строка): Строка // магия из второго отступления
пер ИмяФайлаДекодированное = ""
знч ОбразецДляПоискаСочетаний = "=?utf-8?B?"
ТекстДляДекодирования = ТекстДляДекодирования.Заменить(Строка ="=?UTF-8?B?",Замена = ОбразецДляПоискаСочетаний) // Так как мне лень использовать в Разделить() регулярку - делаю замену, приходит и так и так
знч ПраваяЧасть ="?="
знч ЕстьКодировкаUTF = ТекстДляДекодирования.Содержит(Строка = ОбразецДляПоискаСочетаний, ИгнорироватьРегистр = Ложь)
если ЕстьКодировкаUTF // из остальных кодировок исполнитель пока Base64 преобразовывать не умеет
знч РезультатПоиска = ТекстДляДекодирования.Разделить(Разделитель = ОбразецДляПоискаСочетаний, ВключаяПустые = Ложь)
для текСтрока из РезультатПоиска
пер ПоискПоПравойЧасти = текСтрока.Разделить(Разделитель = ПраваяЧасть, ВключаяПустые = Ложь ) //код несовершенен - уничтожает значимую незакодированную правую часть
если ПоискПоПравойЧасти.Размер() > 1 // Никакого теперь "Количество()" - привыкаем
для ТекМалаяСтрока из ПоискПоПравойЧасти //если нужна значимая правая часть - раскодируйте только первую строку массива а вторую просто конкатенируйте не трогая
ИмяФайлаДекодированное += Кодировки.Base64.ДекодироватьВСтроку(ТекМалаяСтрока.ВСтроку())
;
иначе
ИмяФайлаДекодированное += Кодировки.Base64.ДекодироватьВСтроку(текСтрока.ВСтроку())
;
;
// Консоль.Записать(ИмяФайлаДекодированное)
возврат ИмяФайлаДекодированное
иначе
возврат ТекстДляДекодирования
;
;
Так как имя файла нам могли прислать и в той кодировке, которую исполнитель пока не умеет - можно заглянуть в MIME тип вложения. Ну и если это экселевская книга - сохраняем на диск
если ИмяВложенияраскодированноеЗначение.Содержит(".xlsx")
ПрограммноеИмяВложенияЗначение +=".xlsx"
иначе если ИмяВложенияраскодированноеЗначение.Содержит(".xls")
ПрограммноеИмяВложенияЗначение +=".xls"
иначе если ИмяВложенияНекодированноеЗначение.Содержит(".xlsx")
ПрограммноеИмяВложенияЗначение +=".xlsx"
иначе если ИмяВложенияНекодированноеЗначение.Содержит(".xls")
ПрограммноеИмяВложенияЗначение +=".xls"
иначе если ТипСодержимогоЗначение.Содержит("excel")
ПрограммноеИмяВложенияЗначение +=".xls"
иначе если ТипСодержимогоЗначение.Содержит(".xlsx")
ПрограммноеИмяВложенияЗначение +=".xlsx"
иначе если ТипСодержимогоЗначение.Содержит(".xls")
ПрограммноеИмяВложенияЗначение +=".xls"
иначе
СохраняемФайл = Ложь // все что не имеет отношения к Excel не сохраняем
;
если СохраняемФайл
пер ФайлДляВложения = новый Файл(КаталогВыгрузки() + ПрограммноеИмяВложенияЗначение)
если не ФайлДляВложения.Существует()
исп ПотокЗаписи = ФайлДляВложения.ОткрытьПотокЗаписи()
ПотокЗаписи.Записать(текВложение.Данные)
ПотокЗаписи.Закрыть() // Файл сохранили - теперь неплохо бы немного раскрыть тему - как он назывался, чей и прочее прочее.
;
;
Ну и вишенка на торте - помните структуры, которые мы создавали в самом начале - теперь они нам пригодятся, чтобы сериализовать значимые для нас свойства письма в отдельном файлике
знч СериализованнаяЗаписьОВложении = новый ОписаниеВложения( ИмяВложенияНекодированное = ИмяВложенияНекодированноеЗначение,
ИмяВложенияРаскодированное = ИмяВложенияраскодированноеЗначение,
ПрограммноеИмяВложения = ПрограммноеИмяВложенияЗначение,
КаталогВыгрузки = КаталогВыгрузки(),
НомерВложения = НомерВложенияЗначение,
ТипСодержимого = ТипСодержимогоЗначение) // помните мы в начале структуры создавали - вот и пригодились
МассивВложенийЗначение.Добавить(СериализованнаяЗаписьОВложении) // строго говоря в Исполнителе через Добавить() с массивом работать не рекомендуют но мы никому не расскажем
НомерВложенияЗначение += 1
;
знч СериализованноеПисьмо = новый СтруктураСериализованногоПисьма(Отправитель = ОтправительЗначение,
ДатаОтправления = ДатаОтправленияЗначение,
ИДСообщения = ИДСообщенияЗначение,
УИД = УИДЗначение,
ЕмейлыТекста = ЕмейлыТекстаЗначение,
МассивВложений = МассивВложенийЗначение) //А это шапочка письма
пер ТекстJSON = СериализацияJson.ЗаписатьОбъект(СериализованноеПисьмо) // Легким двежением руки созданные структуры сериализуем в JSON
пер ФайлJSON = новый Файл(КаталогВыгрузки() + ИмяФайлаJSON)
если не ФайлJSON.Существует()
исп ПотокЗаписи = ФайлJSON.ОткрытьПотокЗаписи()
ПотокЗаписи.Записать(ТекстJSON)
ПотокЗаписи.Закрыть()
;
// Консоль.Записать(ТекстJSON.ВСтроку())
;
Входящие.Закрыть()
Скрипт.ЗавершитьРаботу() //без этого иногда может виснуть - но вообще не обязательно
;
Вот, собственно, и все - запустив скрипт, получим ворох книжек экселя и описания к ним - кто, где, почем и откуда.
Разбирать их будем в следующей статье, если тема будет интересна.