Интеграция корпоративных систем с мессенджерами — это один из наиболее востребованных инструментов современного бизнеса. Telegram-боты позволяют оперативно получать уведомления о ключевых событиях в базе данных, отправлять отчеты руководителям, координировать работу выездных сотрудников и даже принимать заказы от клиентов. В этой статье мы подробно разберем, как связать систему 1С с Telegram, рассмотрим частые ошибки начинающих разработчиков и найдем оптимальные пути их решения.
Для начала рассмотрим классический пример отправки текстового сообщения из 1С в чат Telegram. Взаимодействие строится на отправке HTTP-запросов к серверу Telegram API. Чтобы отправить сообщение, нам понадобятся две вещи: токен бота (выдается при создании бота у отца всех ботов — @BotFather) и идентификатор чата (chat_id) получателя.
Проанализируем практический пример реализации такой отправки на встроенном языке 1С. Допустим, нам необходимо информировать руководство или ответственного менеджера о пробитии чека ККМ в конфигурации «1С:Розница». Для этого мы перехватим событие проведения документа и выполним отправку сообщения.
&НаСервере
Процедура ПриПроведенииЧекаОбработкаПроведения(Источник, Отказ, РежимПроведения) Экспорт
Если Источник.СтатусЧекаККМ = Перечисления.СтатусыЧековККМ.Пробитый Тогда
// Формируем текст сообщения
ТекстЧек = Строка(Источник) + " (статус: " + Строка(Источник.СтатусЧекаККМ) + ")";
Товары = "Товары: " + Символы.ПС;
Для Каждого Строка Из Источник.Товары Цикл
Товары = Товары + Строка.Номенклатура.Наименование + ": " +
Строка(Строка.Количество) + " " +
Строка(Строка.Номенклатура.ЕдиницаИзмерения.Наименование) +
" по " + Строка(Строка.Цена) + " = " + Строка(Строка.Сумма) +
?(Строка.ПроцентАвтоматическойСкидки <> 0, " (Автоскидка = " + Строка(Строка.ПроцентАвтоматическойСкидки) + "%; Сумма автоскидки = " + Строка(Строка.СуммаАвтоматическойСкидки), "") +
?(Строка.ПроцентРучнойСкидки <> 0, " (Ручнаяскидка = " + Строка(Строка.ПроцентРучнойСкидки) + "%; Сумма ручной скидки = " + Строка(Строка.СуммаРучнойСкидки), "") +
";" + Символы.ПС;
КонецЦикла;
ТекстСумма = "Сумма чека: " + Строка(Источник.СуммаДокумента);
ТекстОплаты = "Оплата: ";
Для Каждого Строка Из Источник.Оплата Цикл
ТекстОплаты = ТекстОплаты + Строка(Строка.ВидОплаты.Наименование) + ": " + Строка(Строка.Сумма);
КонецЦикла;
ТекстСкидки = "Скидки: ";
Для Каждого Строка Из Источник.СкидкиНаценки Цикл
ТекстСкидки = ТекстСкидки + Строка(Строка.СкидкаНаценка) + ": " + Строка(Строка.Сумма) + Символы.ПС;
КонецЦикла;
ТекстСообщения = ТекстЧек + "; " + Символы.ПС + Товары + "; " + Символы.ПС + ТекстСумма + Символы.ПС + ТекстОплаты + Символы.ПС + ТекстСкидки;
// Параметры подключения к Telegram API
Сервер = "api.telegram.org";
Токен = "Укажите_Здесь_Ваш_Токен";
ID = "Укажите_Здесь_Ваш_Chat_ID";
// ВНИМАНИЕ: Данный способ формирования ресурса содержит уязвимость для спецсимволов!
Ресурс = "bot" + Токен + "/SendMessage?chat_id=" + ID + "&text=" + ТекстСообщения;
Соединение = Новый HTTPСоединение(Сервер, 443, , , , 5, Новый ЗащищенноеСоединениеOpenSSL());
Запрос = Новый HTTPЗапрос(Ресурс);
Ответ = Соединение.Получить(Запрос);
КонецЕсли;
КонецПроцедуры
Представленный алгоритм отлично работает, пока в тексте сообщения содержатся только стандартные латинские символы и цифры без пробелов и переносов строк. Однако при реальной эксплуатации разработчики неизбежно сталкиваются со следующей проблемой.
Если в переменной ТекстСообщения присутствует хотя бы один символ перевода строки (Символы.ПС), пробел или кириллический символ, платформа 1С вернет ошибку выполнения HTTP-запроса: "Ошибка работы с Интернет: URL using bad/illegal format or missing URL".
Выясним причину этой ошибки. При передаче параметров методом GET все специальные символы, кириллица, пробелы и управляющие символы (такие как перевод строки) должны быть преобразованы в специальный формат, пригодный для URL-адресов. Этот процесс называется URL Encoding (процентное кодирование).
Если вы используете GET-запросы, вам потребуется функция преобразования строки в URL-совместимый формат. Рассмотрим, как реализовать такую функцию на встроенном языке 1С без использования внешних компонент:
Функция КодСимволаASCII(Символ)
КодUNICODE = КодСимвола(Символ);
Если ((КодUNICODE > 1039) И (КодUNICODE < 1104)) Тогда
Возврат (КодUNICODE - 848);
ИначеЕсли КодUNICODE = 8470 Тогда
Возврат 185;
ИначеЕсли КодUNICODE = 1105 Тогда
Возврат 184;
ИначеЕсли КодUNICODE = 1025 Тогда
Возврат 168;
Иначе
Возврат КодUNICODE;
КонецЕсли;
КонецФункции
Функция URLEncode(Знач Значение) Экспорт
ТаблицаПерекодировки = "%00%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F%10%11%12%13%14" +
"%15%16%17%18%19%1A%1B%1C%1D%1E%1F%20%21%22%23%24%25%26%27%28" +
"%29%2A%2B%2C%2D%2E%2F%30%31%32%33%34%35%36%37%38%39%3A%3B%3C" +
"%3D%3E%3F%40%41%42%43%44%45%46%47%48%49%4A%4B%4C%4D%4E%4F%50" +
"%51%52%53%54%55%56%57%58%59%5A%5B%5C%5D%5E%5F%60%61%62%63%64" +
"%65%66%67%68%69%6A%6B%6C%6D%6E%6F%70%71%72%73%74%75%76%77%78" +
"%79%7A%7B%7C%7D%7E%7F%80%81%82%83%84%85%86%87%88%89%8A%8B%8C" +
"%8D%8E%8F%90%91%92%93%94%95%96%97%98%99%9A%9B%9C%9D%9E%9F%A0" +
"%A1%A2%A3%A4%A5%A6%A7%A8%A9%AA%AB%AC%AD%AE%AF%B0%B1%B2%B3%B4" +
"%B5%B6%B7%B8%B9%BA%BB%BC%BD%BE%BF%C0%C1%C2%C3%C4%C5%C6%C7%C8" +
"%C9%CA%CB%CC%CD%CE%CF%D0%D1%D2%D3%D4%D5%D6%D7%D8%D9%DA%DB%DC" +
"%DD%DE%DF%E0%E1%E2%E3%E4%E5%E6%E7%E8%E9%EA%EB%EC%ED%EE%EF%F0" +
"%F1%F2%F3%F4%F5%F6%F7%F8%F9%FA%FB%FC%FD%FE%FF";
Результат = "";
ДлинаСтроки = СтрДлина(Значение);
Для Шаг = 1 По ДлинаСтроки Цикл
ТекущийСимвол = Сред(Значение, Шаг, 1);
КодСимволаASCII = КодСимволаASCII(ТекущийСимвол);
Результат = Результат + Сред(ТаблицаПерекодировки, КодСимволаASCII * 3 + 1, 3);
КонецЦикла;
Возврат Результат;
КонецФункции
Теперь перед добавлением текста сообщения в строку ресурса достаточно вызвать функцию кодирования:
ЗакодированныйТекст = URLEncode(ТекстСообщения);
Ресурс = "bot" + Токен + "/SendMessage?chat_id=" + ID + "&text=" + ЗакодированныйТекст;
Это решит проблему с падением запросов, и переносы строк Символы.ПС начнут корректно отображаться в мессенджере.
Несмотря на работоспособность метода GET с кодированием URL, этот подход считается устаревшим и имеет серьезные ограничения по объему передаваемых данных (максимальная длина URL-строки ограничена). Более надежным и профессиональным стандартом является использование метода POST с телом запроса в формате JSON.
При использовании POST-запросов вам больше не нужно заботиться о ручном кодировании спецсимволов. Платформа 1С берет на себя сериализацию строк любой сложности. Разберем по шагам, как переписать процедуру отправки сообщения на метод POST:
&НаСервере
Процедура ОтправитьСообщениеПост(Токен, ChatID, ТекстСообщения)
Сервер = "api.telegram.org";
Ресурс = "bot" + Токен + "/sendMessage";
// Формируем структуру данных для отправки
ПараметрыЗапроса = Новый Структура;
ПараметрыЗапроса.Вставить("chat_id", ChatID);
ПараметрыЗапроса.Вставить("text", ТекстСообщения);
ПараметрыЗапроса.Вставить("parse_mode", "Markdown"); // Поддержка форматирования
// Сериализуем структуру в JSON
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, ПараметрыЗапроса);
СтрокаJSON = ЗаписьJSON.Закрыть();
// Настраиваем HTTP-запрос
Запрос = Новый HTTPЗапрос(Ресурс);
Запрос.Заголовки.Вставить("Content-Type", "application/json");
Запрос.УстановитьТелоИзСтроки(СтрокаJSON, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
// Выполняем отправку
Соединение = Новый HTTPСоединение(Сервер, 443, , , , 5, Новый ЗащищенноеСоединениеOpenSSL());
Попытка
Ответ = Соединение.ОтправитьДляОбработки(Запрос);
Если Ответ.КодСостояния <> 200 Тогда
ЗаписьЖурналаРегистрации("Telegram_Интеграция", УровеньЖурналаРегистрации.Предупреждение, , , Ответ.ПолучитьТелоКакСтроку());
КонецЕсли;
Исключение
ЗаписьЖурналаРегистрации("Telegram_Интеграция", УровеньЖурналаРегистрации.Ошибка, , , ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
Данный вариант архитектуры исключает любые проблемы со спецсимволами, поддерживает разметку Markdown (выделение жирным шрифтом, курсивом) и готов к передаче больших объемов текста.
Реализуем следующую задачу: пользователь вводит команду в боте, перед ним появляется клавиатура с названиями отчетов, и по нажатию на кнопку формируется и отправляется PDF-файл. Нам необходимо не просто отправить кнопки пользователю, но и обработать их последующее нажатие.
Для создания кнопок непосредственно под сообщением используется Inline Keyboard (инлайн-клавиатура). Каждой кнопке сопоставляется скрытый параметр — callback_data, который вернется в 1С при нажатии.
Рассмотрим пример формирования сообщения с кнопками на встроенном языке:
&НаСервере
Процедура ОтправитьКнопкиОтчетов(Токен, ChatID)
Сервер = "api.telegram.org";
Ресурс = "bot" + Токен + "/sendMessage";
// Описываем инлайн-кнопки
МассивКнопок = Новый Массив;
Кнопка1 = Новый Структура("text, callback_data", "Продажи за день", "report_sales_day");
Кнопка2 = Новый Структура("text, callback_data", "Остатки товаров", "report_stock");
СтрокаКнопок = Новый Массив;
СтрокаКнопок.Добавить(Кнопка1);
СтрокаКнопок.Добавить(Кнопка2);
МассивКнопок.Добавить(СтрокаКнопок);
Клавиатура = Новый Структура("inline_keyboard", МассивКнопок);
// Собираем тело запроса
ПараметрыЗапроса = Новый Структура;
ПараметрыЗапроса.Вставить("chat_id", ChatID);
ПараметрыЗапроса.Вставить("text", "Выберите интересующий вас отчет:");
ПараметрыЗапроса.Вставить("reply_markup", Клавиатура);
ЗаписьJSON = Новый RecordJSON; // Инициализируем запись JSON
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, ПараметрыЗапроса);
СтрокаJSON = ЗаписьJSON.Закрыть();
Запрос = Новый HTTPЗапрос(Ресурс);
Запрос.Заголовки.Вставить("Content-Type", "application/json");
Запрос.УстановитьТелоИзСтроки(СтрокаJSON, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
Соединение = Новый HTTPСоединение(Сервер, 443, , , , 5, Новый ЗащищенноеСоединениеOpenSSL());
Соединение.ОтправитьДляОбработки(Запрос);
КонецПроцедуры
Как только пользователь нажмет кнопку, Telegram отправит событие. Существует два способа его перехвата и обработки в 1С:
https://api.telegram.org/bot<Токен>/getUpdates, считывает массив новых событий, обрабатывает их и сохраняет идентификатор последнего обработанного события (offset). Этот метод прост, так как не требует публикации базы наружу, но создает задержки и нагружает сервер частыми холостыми запросами.HTTP-сервис, адрес которого регистрируется на сервере Telegram через вызов метода setWebhook. Теперь при любом действии пользователя Telegram мгновенно присылает POST-запрос на ваш HTTP-сервис. Это идеальное решение с мгновенной реакцией бота.При обработке входящего запроса от инлайн-кнопок вам придет структура JSON, содержащая объект callback_query. Ваша логика должна проанализировать поле data (там будет содержаться строка, например, "report_sales_day"), сгенерировать требуемый файл и отправить его обратно.
Важная рекомендация: После обработки нажатия инлайн-кнопки обязательно отправляйте ответный запрос методом answerCallbackQuery. Иначе пользователь будет наблюдать бесконечную анимацию загрузки на кнопке.
Для отправки сформированных отчетов в форматах PDF или XLS используется метод Telegram API sendDocument. В отличие от текста, передача файлов требует использования формата тела запроса multipart/form-data.
Проанализируем пример кода для программного формирования печатной формы отчета в PDF и отправки полученного файла пользователю:
&НаСервере
Процедура СформироватьИОтправитьОтчет(Токен, ChatID, ИмяФайлаОтчета)
// Шаг 1. Формируем печатную форму отчета в 1С
// Допустим, у нас есть табличный документ ТабДок
ТабДок = Новый ТабличныйДокумент;
// ... здесь логика заполнения табличного документа ...
ПутьКФайлу = ПолучитьИмяВременногоФайла("pdf");
ТабДок.Записать(ПутьКФайлу, ТипФайлаТабличногоДокумента.PDF);
// Шаг 2. Подготовка и отправка файла через HTTP-запрос (multipart/form-data)
Сервер = "api.telegram.org";
Ресурс = "bot" + Токен + "/sendDocument";
Разделитель = "----" + Строка(Новый УникальныйИдентификатор);
// Формируем тело запроса
ТелоЗапроса = Новый ПотокВПамяти;
ЗаписьТекста = Новый ЗаписьТекста(ТелоЗапроса, КодировкаТекста.UTF8);
// Текстовый параметр chat_id
ЗаписьТекста.ЗаписатьСтроку("--" + Разделитель);
ЗаписьТекста.ЗаписатьСтроку("Content-Disposition: form-data; name=""chat_id""");
ЗаписьТекста.ЗаписатьСтроку("");
ЗаписьТекста.ЗаписатьСтроку(ChatID);
// Параметр документа
ЗаписьТекста.ЗаписатьСтроку("--" + Разделитель);
ЗаписьТекста.ЗаписатьСтроку("Content-Disposition: form-data; name=""document""; filename=""" + ИмяФайлаОтчета + ".pdf""");
ЗаписьТекста.ЗаписатьСтроку("Content-Type: application/pdf");
ЗаписьТекста.ЗаписатьСтроку("");
ЗаписьТекста.СовместитьСПотоком();
// Записываем бинарные данные файла
ДвоичныеДанныеФайла = Новый ДвоичныеДанные(ПутьКФайлу);
ДвоичныеДанныеФайла.Записать(ТелоЗапроса);
// Закрываем границу запроса
ЗаписьТекста.ЗаписатьСтроку("");
ЗаписьТекста.ЗаписатьСтроку("--" + Разделитель + "--");
ЗаписьТекста.СовместитьСПотоком();
// Возвращаем указатель потока в начало
ТелоЗапроса.Перейти(0, ПозицияВПотоке.Начало);
// Отправляем запрос
Запрос = Новый HTTPЗапрос(Ресурс);
Запрос.Заголовки.Вставить("Content-Type", "multipart/form-data; boundary=" + Разделитель);
Запрос.УстановитьТелоИзПотока(ТелоЗапроса);
Соединение = Новый HTTPСоединение(Сервер, 443, , , , 10, Новый ЗащищенноеСоединениеOpenSSL());
Попытка
Соединение.ОтправитьДляОбработки(Запрос);
Исключение
ЗаписьЖурналаРегистрации("Telegram_Файлы", УровеньЖурналаРегистрации.Ошибка, , , ОписаниеОшибки());
КонецПопытки;
// Удаляем временный файл с диска
УдалитьФайлы(ПутьКФайлу);
КонецПроцедуры
С помощью этой схемы вы сможете отправлять любые документы, счета на оплату, акты сверки или аналитические отборы напрямую в мессенджер вашим клиентам или руководству.
Используя предложенные методики, вы сможете построить отказоустойчивую и современную систему коммуникации между вашей базой 1С и пользователями Telegram.