При интеграции внешних ботов (например, ботов мессенджера MAX или Telegram) с Системой взаимодействия 1С через раздел Настройки сообщений из других приложений разработчики часто сталкиваются с проблемой: платформа 1С автоматически переопределяет адрес вебхука бота на свой шлюз (сервис 1С:Диалоги), из-за чего сторонний обработчик перестает получать события. Разберем, почему покупка локального Сервера взаимодействия не исправит данную ситуацию, и рассмотрим практические способы решения задачи.
Проанализируем базовый принцип работы Bot API большинства современных мессенджеров. На один токен бота платформы позволяют зарегистрировать только один активный URL для приема вебхуков. При подключении интеграции встроенные механизмы 1С отправляют запрос к API мессенджера и перезаписывают URL вебхука на собственный адрес.
Развертывание локального экземпляра 1С:Сервера взаимодействия (1С:Диалоги) не решает проблему, так как логика работы модуля интеграции остается прежней: сервер регистрирует свой внутренний URL для получения входящих сообщений. Тратить бюджет компании на покупку локального сервера взаимодействия исключительно ради сохранения вебхука не имеет смысла.
Рассмотрим основные подходы, которые позволяют сохранить текущую автоматизацию бота и одновременно использовать функционал 1С:
HTTP-сервис, после чего программно создаем обсуждения с помощью объекта СистемаВзаимодействия (метод СоздатьОбсуждение()) и записываем входящие сообщения методом ЗаписатьСообщение(). Для обратной связи перехватываем отправку сообщений операторами в 1С и отправляем запросы в API бота.Разберем по шагам реализацию отправки отчетов и файлов в мессенджер средствами встроенного языка 1С без использования сторонних внешних компонент. Процесс состоит из трех этапов: получение ссылки для загрузки, передача файла через multipart/form-data и отправка итогового сообщения с вложением.
Посмотрим на пример экспортной процедуры:
Процедура ОтправитьФайлВMax(ПутьКФайлу, IDЧата, ТокенБота, ИмяФайлаОтчета = "ФайлОтчета.xlsx", ТекстСообщения = "Отправляю файл") Экспорт
// 1. Формируем заголовки авторизации
ЗаголовкиАвторизации = Новый Соответствие;
ЗаголовкиАвторизации.Вставить("Authorization", ТокенБота);
// 2. Получаем временный URL для загрузки файла
HTTPСоединение = Новый HTTPСоединение("platform-api.max.ru", 443, , , , , Новый ЗащищенноеСоединениеOpenSSL());
HTTPЗапрос = Новый HTTPЗапрос("/uploads?type=file", ЗаголовкиАвторизации);
HTTPОтвет = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
Если HTTPОтвет.КодСостояния <> 200 Тогда
ВызватьИсключение "Ошибка при получении URL для загрузки: " + HTTPОтвет.КодСостояния + " - " + HTTPОтвет.ПолучитьТелоКакСтроку();
КонецЕсли;
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(HTTPОтвет.ПолучитьТелоКакСтроку());
ОтветМетаЗагрузки = ПрочитатьJSON(ЧтениеJSON);
ЧтениеJSON.Закрыть();
URLДляЗагрузки = ОтветМетаЗагрузки.url;
// 3. Разбираем полученный URL для отправки бинарных данных
РазборURL = СтрРазделить(URLДляЗагрузки, "/", Ложь);
СерверЗагрузки = РазборURL[1];
ПутьЗагрузки = СтрЗаменить("/" + СтрСоединить(РазборURL, "/"), "/https:/" + СерверЗагрузки, "");
// Формируем тело Multipart/form-data
Boundary = "----WebKitFormBoundary" + СтрЗаменить(Строка(Новый УникальныйИдентификатор), "-", "");
ДвоичныеДанныеФайла = Новый ДвоичныеДанные(ПутьКФайлу);
ПотокВПамяти = Новый ПотокВПамяти;
ЗаписьДанных = Новый ЗаписьДанных(ПотокВПамяти);
ЗаписьДанных.ЗаписатьСтроку("--" + Boundary);
ЗаписьДанных.ЗаписатьСтроку("Content-Disposition: form-data; name=""data""; filename=""" + ИмяФайлаОтчета + """");
ЗаписьДанных.ЗаписатьСтроку("Content-Type: application/octet-stream");
ЗаписьДанных.ЗаписатьСтроку("");
ЗаписьДанных.Записать(ДвоичныеДанныеФайла);
ЗаписьДанных.ЗаписатьСтроку("");
ЗаписьДанных.ЗаписатьСтроку("--" + Boundary + "--");
ЗаписьДанных.Закрыть();
ТелоЗапросаЗагрузки = ПотокВПамяти.ЗакрытьИПолучитьДвоичныеДанные();
HTTPСоединениеЗагрузки = Новый HTTPСоединение(СерверЗагрузки, 443, , , , , Новый ЗащищенноеСоединениеOpenSSL());
HTTPЗапросЗагрузки = Новый HTTPЗапрос(ПутьЗагрузки);
HTTPЗапросЗагрузки.Заголовки.Вставить("Content-Type", "multipart/form-data; boundary=" + Boundary);
HTTPЗапросЗагрузки.УстановитьТелоИзДвоичныхДанных(ТелоЗапросаЗагрузки);
HTTPОтветЗагрузки = HTTPСоединениеЗагрузки.ОтправитьДляОбработки(HTTPЗапросЗагрузки);
Если HTTPОтветЗагрузки.КодСостояния <> 200 Тогда
ВызватьИсключение "Ошибка при загрузке файла: " + HTTPОтветЗагрузки.КодСостояния;
КонецЕсли;
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(HTTPОтветЗагрузки.ПолучитьТелоКакСтроку());
ДанныеФайла = ПрочитатьJSON(ЧтениеJSON);
ЧтениеJSON.Закрыть();
// 4. Отправляем сообщение с прикрепленным файлом
HTTPЗапросСообщения = Новый HTTPЗапрос("/messages?user_id=" + IDЧата, ЗаголовкиАвторизации);
HTTPЗапросСообщения.Заголовки.Вставить("Content-Type", "application/json");
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписьJSON.ЗаписатьНачалоОбъекта();
ЗаписьJSON.ЗаписатьИмяСвойства("text");
ЗаписьJSON.ЗаписатьЗначение(ТекстСообщения);
ЗаписьJSON.ЗаписатьИмяСвойства("attachments");
ЗаписьJSON.ЗаписатьНачалоМассива();
ЗаписьJSON.ЗаписатьНачалоОбъекта();
ЗаписьJSON.ЗаписатьИмяСвойства("type");
ЗаписьJSON.ЗаписатьЗначение("file");
ЗаписьJSON.ЗаписатьИмяСвойства("payload");
ЗаписатьJSON(ЗаписьJSON, ДанныеФайла);
ЗаписьJSON.ЗаписатьКонецОбъекта();
ЗаписьJSON.ЗаписатьКонецМассива();
ЗаписьJSON.ЗаписатьКонецОбъекта();
СтрокаТелаЗапроса = ЗаписьJSON.Закрыть();
HTTPЗапросСообщения.УстановитьТелоИзСтроки(СтрокаТелаЗапроса, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
HTTPОтветСообщения = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапросСообщения);
Если HTTPОтветСообщения.КодСостояния <> 200 Тогда
ВызватьИсключение "Ошибка отправки сообщения: " + HTTPОтветСообщения.ПолучитьТелоКакСтроку();
КонецЕсли;
КонецПроцедуры
Рассмотрим важные нюансы при работе с Bot API через HTTP-запросы в 1С:
user_id на chat_id.attachment.not.ready), избегайте пустых циклов ожидания по текущей дате, так как они создают предельную нагрузку на процессор. Надежнее вынести отправку в ФоновыеЗадания или проверять статус готовности вложения повторными запросами.