Как отправить PDF файл из 1С в Telegram бота методом sendDocument при использовании вебхуков?

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

При интеграции 1С с Telegram ботом разработчики часто сталкиваются с задачей отправки файлов, таких как PDF-документы, в ответ на действия пользователя в боте. Это особенно актуально, когда данные о нажатии кнопки или запросе документа приходят в 1С через механизм вебхуков. В данном руководстве мы подробно рассмотрим, как реализовать отправку файлов методом sendDocument из 1С, учитывая особенности работы с вебхуками и форматированием HTTP-запросов.

Изначально может показаться логичным попытаться отправить файл в качестве прямого ответа на входящий вебхук от Telegram. Однако, как показал опыт многих разработчиков, включая участников данного обсуждения, такой подход не является рабочим. Телеграм ожидает быстрый ответ с подтверждением получения обновления (обычно HTTP 200), а не файл. Отправка файла – это отдельная операция, которую должен инициировать ваш HTTP-сервис 1С.

Понимание механики вебхуков и отправки файлов

Прежде чем перейти к коду, давайте разберем ключевые концепции:

  1. Вебхук (Webhook): Это механизм, при котором Telegram сам «стучится» в ваш HTTP-сервис 1С (обычно методом POST) каждый раз, когда происходит новое событие (например, пользователь отправляет сообщение боту или нажимает кнопку). Ваш сервис должен быстро принять этот запрос и ответить на него статусом HTTP 200 OK, чтобы Telegram знал, что обновление было успешно получено. Если вы не ответите вовремя, Telegram будет повторять запросы или считать вебхук нерабочим.
  2. getUpdates: Это альтернативный механизм, при котором ваш 1С-сервис сам периодически обращается к серверам Telegram для получения новых событий. В этом случае 1С является инициатором связи.
  3. Метод sendDocument: Это метод Telegram Bot API, предназначенный для отправки файлов. Он может принимать файл как:
    • file_id: Если файл уже был загружен в Telegram ранее.
    • HTTP URL: Telegram загрузит файл по указанной ссылке.
    • Новый файл: Файл загружается непосредственно с вашего сервера с использованием формата multipart/form-data. Максимальный размер файла для этого метода обычно составляет 50 МБ.

Критически важно: Когда ваш HTTP-сервис 1С получает запрос от Telegram через вебхук, он должен его обработать, а затем отдельным HTTP-запросом обратиться к Telegram API методом sendDocument для отправки файла. Это позволяет не «зависать» вебхуку и корректно передать файл.

Пошаговая реализация отправки файла методом sendDocument в 1С

Рассмотрим подробный пример кода для 1С, который демонстрирует, как сформировать и отправить POST-запрос с файлом в формате multipart/form-data. Этот подход использует временные файлы для правильного объединения текстовых и двоичных частей запроса.

1. Подготовка данных и формирование границ (Boundary)

Прежде всего, нам понадобятся ваш токен бота, идентификатор чата, путь к файлу и уникальный разделитель для multipart/form-data.


МойToken = "ВАШ_ТОКЕН_БОТА"; // Замените на реальный токен вашего бота
АдресTelegramAPI = "api.telegram.org";
// Если нужен прокси:
// ПроксиАдрес = "159.138.22.170";
// ПроксиПорт = 3128;

ЧатID = "882646417"; // Идентификатор чата, куда отправляем файл
ПутьКФайлу = "C:\1.pdf"; // Полный путь к отправляемому PDF-файлу

// Создаем уникальный разделитель (Boundary) для multipart/form-data
// Это строка, которая гарантированно не встречается в содержимом файла
Boundary = "----"+Строка(Новый УникальныйИдентификатор);
РазделительСтрок = Символы.ВК + Символы.ПС; // CRLF

2. Формирование тела POST-запроса в формате multipart/form-data

Формат multipart/form-data требует, чтобы тело запроса было разделено на части с помощью специальной строки-разделителя (Boundary). Каждая часть содержит заголовки (например, Content-Disposition, Content-Type) и само содержимое поля. Двоичные данные файла должны быть вставлены между соответствующими текстовыми частями.

В 1С для корректной работы с двоичными данными в multipart/form-data удобно использовать механизм объединения файлов. Мы создадим три временных файла:

  1. Первый файл будет содержать начальные текстовые части запроса (chat_id, заголовки для document).
  2. Второй файл – это сам отправляемый PDF-файл.
  3. Третий файл будет содержать завершающую часть запроса.

Затем эти три файла будут объединены в один и отправлены.


Файл = Новый Файл(ПутьКФайлу);
Если Не Файл.Существует() Тогда
    Сообщить("Ошибка: Файл по пути " + ПутьКФайлу + " не найден.");
    Возврат;
КонецЕсли;

// 1. Формируем начальную часть запроса (chat_id и заголовки для document)
МассивФайловДляОбъединения = Новый Массив;
ИмяВременногоФайлаНачало = ПолучитьИмяВременногоФайла("txt");
МассивФайловДляОбъединения.Добавить(ИмяВременногоФайлаНачало);

ФайлОтправкиНачало = Новый ЗаписьТекста(ИмяВременногоФайлаНачало, КодировкаТекста.UTF8);

НачальныеДанные = "--" + Boundary + РазделительСтрок
                 + "Content-Disposition: form-data; name=""chat_id""" + РазделительСтрок + РазделительСтрок
                 + СтрЗаменить(Строка(ЧатID), Символы.НПП, "") + РазделительСтрок // Важно: chat_id приводим к строке
                 + "--" + Boundary + РазделительСтрок
                 + "Content-Disposition: form-data; name=""document""; filename=""" + Файл.Имя + """" + РазделительСтрок
                 + "Content-Type: application/pdf" + РазделительСтрок + РазделительСтрок; // Два разделителя перед бинарными данными

ФайлОтправкиНачало.ЗаписатьСтроку(НачальныеДанные);
ФайлОтправкиНачало.Закрыть();

// 2. Добавляем сам PDF-файл в массив для объединения
МассивФайловДляОбъединения.Добавить(ПутьКФайлу);

// 3. Формируем конечную часть запроса
ИмяВременногоФайлаКонец = ПолучитьИмяВременногоФайла("txt");
МассивФайловДляОбъединения.Добавить(ИмяВременногоФайлаКонец);

ФайлаОтправкиКонец = Новый ЗаписьТекста(ИмяВременногоФайлаКонец, КодировкаТекста.UTF8);
КонечныеДанные = РазделительСтрок + "--" + Boundary + "--" + РазделительСтрок;

ФайлаОтправкиКонец.ЗаписатьСтроку(КонечныеДанные);
ФайлаОтправкиКонец.Закрыть();

// Объединяем все части в один итоговый файл
ИмяИтоговогоФайла = ПолучитьИмяВременногоФайла("tmp"); // Важно: использовать расширение, чтобы 1С не пыталась его парсить как текст
ОбъединитьФайлы(МассивФайловДляОбъединения, ИмяИтоговогоФайла);

Мы использовали СтрЗаменить(Строка(ЧатID), Символы.НПП, "") для chat_id. Это необходимо, чтобы гарантировать, что chat_id будет корректной строкой без непечатаемых символов, которые могут возникнуть при преобразовании числа в строку в некоторых версиях 1С.

3. Установка HTTP-соединения и отправка запроса

Теперь, когда у нас есть готовый файл с телом запроса, мы можем отправить его в Telegram API.


// Настройка прокси, если необходимо
// Прокси = Новый ИнтернетПрокси;
// Прокси.Установить("https", ПроксиАдрес, ПроксиПорт);
// Прокси.Установить("http", ПроксиАдрес, ПроксиПорт);

// Устанавливаем соединение с Telegram API по HTTPS (порт 443)
// Если используете прокси, раскомментируйте Прокси
СоединениеHTTP = Новый HTTPСоединение(АдресTelegramAPI, 443, , , , , Новый ЗащищенноеСоединениеOpenSSL());

// Формируем адрес запроса к методу sendDocument
АдресЗапроса = "bot" + МойToken + "/sendDocument";

ЗапросHTTP = Новый HTTPЗапрос(АдресЗапроса);
ЗапросHTTP.Метод = "POST"; // Всегда используем POST для отправки файлов

// Устанавливаем заголовки запроса
ЗапросHTTP.Заголовки.Вставить("Connection", "keep-alive");
ЗапросHTTP.Заголовки.Вставить("Content-Type", "multipart/form-data; boundary=" + Boundary);

// Указываем, что тело запроса находится в файле
ЗапросHTTP.УстановитьИмяФайлаТела(ИмяИтоговогоФайла);

// Отправляем запрос и обрабатываем ответ
Попытка
    ОтветHTTP = СоединениеHTTP.Отправить(ЗапросHTTP);

    Если ОтветHTTP.Код = 200 Тогда
        Сообщить("Файл успешно отправлен в Telegram. Ответ: " + ОтветHTTP.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8));
    Иначе
        Сообщить("Ошибка при отправке файла. Код ответа: " + ОтветHTTP.Код);
        Сообщить("Тело ответа: " + ОтветHTTP.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8));
    КонецЕсли;

Исключение
    Сообщить("Произошла ошибка при выполнении HTTP-запроса: " + ОписаниеОшибки());
КонецПопытки;

// Удаляем временные файлы
УдалитьФайлы(ИмяВременногоФайлаНачало);
УдалитьФайлы(ИмяВременногоФайлаКонец);
УдалитьФайлы(ИмяИтоговогоФайла);

Интеграция с вебхуком в 1С

После того, как мы научились отправлять файл, рассмотрим, как это встроить в логику обработки вебхуков. Ваш HTTP-сервис 1С получает запрос от Telegram. Внутри обработчика этого вебхука вы выполняете следующие шаги:

  1. Парсите входящий JSON-запрос от Telegram, чтобы получить, например, chat_id и команду пользователя.
  2. Если команда соответствует условию (например, "Получить документ"), вы инициируете описанную выше процедуру отправки файла методом sendDocument.
  3. Обязательно в самом конце обработчика вебхука возвращаете HTTP-ответ со статусом 200. Это подтверждает Telegram, что ваш сервис успешно получил и обработал обновление.

// Пример обработчика HTTP-сервиса в 1С (псевдокод)
Функция ОбработатьHTTPЗапрос(Запрос) Экспорт

    // 1. Получаем и парсим входящие данные от Telegram
    СтрокаJSON = Запрос.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8);
    ДанныеЗапроса = ПрочитатьJSON(СтрокаJSON); // Предполагаем наличие функции ПрочитатьJSON

    Чат = ДанныеЗапроса.message.chat.id; // Пример получения chat_id

    // 2. Проверяем условие и инициируем отправку файла
    Если ДанныеЗапроса.message.text = "Получить документ" Тогда
        // Здесь вызываем процедуру, которая содержит код по отправке файла,
        // аналогичный приведенному выше.
        // Передаем в нее ЧатID и ПутьКФайлу
        // Например: ОтправитьPDFВTelegram(Чат, "d:\1111.pdf");
        
        Сообщить("Получена команда 'Получить документ', инициируем отправку файла...");
        
        // ВАЖНО: здесь вызов функции, которая отправляет файл отдельным POST-запросом
        Попытка
            ОтправитьФайлМетодомSendDocument(Чат, "d:\1111.pdf"); // Пример вызова вашей функции
        Исключение
            Сообщить("Ошибка при отправке файла: " + ОписаниеОшибки());
        КонецПопытки;
        
    КонецЕсли;

    // 3. Возвращаем HTTP-ответ 200, чтобы Telegram знал, что запрос обработан
    Ответ = Новый HTTPСервисОтвет(200);
    // Можно вернуть пустой JSON или просто пустой ответ.
    // Пример:
    // Ответ.УстановитьТелоИзСтроки("{""ok"": true}");
    // Ответ.Заголовки.Вставить("Content-Type", "application/json");
    
    Возврат Ответ;

КонецФункции

// Пример вспомогательной процедуры, которая инкапсулирует логику отправки файла
Процедура ОтправитьФайлМетодомSendDocument(ЧатID, ПутьКФайлу)

    // ... Весь код из разделов "1. Подготовка данных" и "3. Установка HTTP-соединения"
    //     должен быть здесь, используя переданные ЧатID и ПутьКФайлу
    //     Не забудьте определить МойToken, АдресTelegramAPI и другие параметры.

    МойToken = "ВАШ_ТОКЕН_БОТА"; 
    АдресTelegramAPI = "api.telegram.org";
    РазделительСтрок = Символы.ВК + Символы.ПС; 
    
    // ... остальная часть кода для формирования и отправки запроса
    //     как показано выше.
    //     Удаление временных файлов также должно быть здесь.

КонецПроцедуры

Часто встречающиеся проблемы и их решение

  1. Ошибка 400 Bad Request:
    • Причина: Чаще всего связана с некорректным формированием multipart/form-data запроса. Это может быть неправильный Boundary, отсутствующие пустые строки между заголовками и содержимым части, неверный Content-Type для файла или ошибки в структуре заголовков.
    • Решение: Внимательно проверьте структуру тела запроса. Используйте утилиты для отладки HTTP-запросов (например, Postman, Fiddler), чтобы сравнить ваш запрос с корректно сформированным. Убедитесь, что РазделительСтрок (Символы.ВК + Символы.ПС) используется последовательно и правильно, особенно перед двоичными данными файла.
  2. Файл не отправляется, но ошибок нет:
    • Причина: Возможно, ваш HTTP-сервис не отвечает на вебхук достаточно быстро, и Telegram прерывает сессию или считает его нерабочим.
    • Решение: Убедитесь, что ваш обработчик вебхука всегда возвращает HTTP 200, даже если отправка файла завершилась неудачей или заняла много времени. Отправка файла должна происходить как фоновая задача или после быстрого ответа на вебхук.
  3. Проблемы с chat_id:
    • Причина: chat_id может приходить в виде числа, и при преобразовании его в строку в 1С могут появиться непечатаемые символы (например, Символы.НПП). Telegram API ожидает чистую строковую репрезентацию.
    • Решение: Всегда обрабатывайте chat_id, приводя его к строке и удаляя непечатаемые символы, как показано: СтрЗаменить(Строка(ЧатID), Символы.НПП, "").
  4. Ограничения размера файла:
    • Причина: Telegram имеет лимиты на размер отправляемых файлов. Для sendDocument обычно это 50 МБ для новых файлов.
    • Решение: Убедитесь, что ваш файл не превышает эти лимиты. Если файл очень большой, рассмотрите возможность его сжатия или разделения.
  5. Доступность сервера 1С для вебхуков:
    • Причина: Для работы вебхуков ваш HTTP-сервис 1С должен быть доступен из интернета по публичному адресу (IP или доменному имени) и использовать HTTPS с действительным SSL-сертификатом.
    • Решение: Настройте свой веб-сервер (например, Apache, Nginx), чтобы он проксировал запросы к вашему HTTP-сервису 1С, обеспечивая при этом HTTPS-соединение. Самоподписанные сертификаты Telegram обычно не принимает.

Заключение

Отправка PDF-файлов из 1С в Telegram бот методом sendDocument через вебхуки — это вполне реализуемая задача. Ключевым моментом является понимание того, что файл отправляется не как прямой ответ на входящий вебхук, а как отдельный POST-запрос к Telegram API, инициируемый вашим HTTP-сервисом. Правильное формирование multipart/form-data запроса с использованием механизма объединения временных файлов в 1С обеспечивает надежную передачу двоичных данных. Всегда помните о необходимости быстрого ответа на вебхук и корректной обработке chat_id. Следуя этим рекомендациям, вы сможете успешно интегрировать 1С с Telegram ботом для обмена документами.

← На главную