Разработка Telegram-бота на 1С обычно уводит команду в сторону: ставят Node.js-прослойку, стороннюю CRM или платный коннектор, а потом синхронизируют это с учётной системой. Мы пошли иначе и собрали Telegram-бота программы лояльности прямо внутри конфигурации 1С:Розница — без единого внешнего сервиса. Покупатель детской розничной сети открывает бота, делится номером телефона, и 1С сама заводит ему карту лояльности. Ниже — как устроена интеграция 1С с Telegram Bot API на чистом встроенном языке и почему это оказалось надёжнее «прослоек».
Задача
У розничной сети детских товаров работала программа лояльности на дисконтных картах в 1С:Рознице. Нужно было дать покупателю возможность зарегистрироваться в клубе за минуту, без визита в магазин и без ручного заведения карты продавцом. Классический канал для этого — Telegram: покупатель нажимает «Старт», делится телефоном, получает карту. Требование бизнеса — чтобы регистрация сразу попадала в 1С и карта была совместима с уже действующей дисконтной системой на кассе.
Решение: Bot API целиком на BSL
Вместо внешнего бота мы реализовали слой Telegram Bot API прямо в конфигурации, в отдельной подсистеме с префиксом tl_. Базовый модуль отправки — единственная точка выхода в сеть: он открывает HTTPS-соединение с api.telegram.org через ЗащищённоеСоединениеOpenSSL, подставляет токен бота из константы и собирает адрес вида bot<token>/<method>. Обычные вызовы уходят как GET с query-параметрами, а отправка файлов (например, изображения карты) — как multipart/form-data с вручную сформированным boundary.
Функция ОтправитьHTTPЗапрос(method, method_param = Неопределено, Данные = Неопределено) Экспорт
Результат = Неопределено;
Попытка
СоединениеHTTP = Новый HTTPСоединение("api.telegram.org", 443, , , , , Новый ЗащищенноеСоединениеOpenSSL());
ПараметрыЗапроса = Новый Соответствие;
ПараметрыЗапроса.Вставить("token", tl_ПовтИсп.ПолучитьToken());
ПараметрыЗапроса.Вставить("method", method);
ПараметрыЗапроса.Вставить("method_param", method_param);
HTTPЗапрос = Новый HTTPЗапрос;
Если Данные = Неопределено Тогда
HTTPЗапрос.Заголовки.Вставить("Content-type", "application/json");
Иначе
ТекстЗапроса = СформироватьТелоЗапроса(Данные);
HTTPЗапрос.УстановитьТелоИзДвоичныхДанных(ТекстЗапроса);
HTTPЗапрос.Заголовки.Вставить("Content-Type", "multipart/form-data; boundary=" + Данные["Boundary"]);
КонецЕсли;
HTTPЗапрос.АдресРесурса = СформироватьМетод(ПараметрыЗапроса); // bot<token>/<method>?params
Если Данные = Неопределено Тогда
РезультатЗапроса = СоединениеHTTP.Получить(HTTPЗапрос);
Иначе
РезультатЗапроса = СоединениеHTTP.ОтправитьДляОбработки(HTTPЗапрос);
КонецЕсли;
Если РезультатЗапроса.КодСостояния = 200 Тогда
Результат = РезультатЗапроса.ПолучитьТелоКакСтроку();
Иначе
РегистрыСведений.tl_Логи.ЛогHTTP(РезультатЗапроса);
КонецЕсли;
Исключение
РегистрыСведений.tl_Логи.ЗаписатьОшибку(ИнформацияОбОшибке(), Истина);
КонецПопытки;
Возврат Результат;
КонецФункции
Поверх этой функции лежит библиотека методов Bot API — sendMessage, sendPhoto, getUpdates, setWebhook, answerInlineQuery и другие. Каждый метод только собирает Соответствие параметров и вызывает базовую отправку, а ответ разбирает штатным ПрочитатьJSON с кастомным преобразованием Unix-поля date в дату 1С.
Два режима приёма обновлений: webhook и long polling
Бот умеет работать обоими способами, которые поддерживает Telegram. В боевом режиме — webhook: публикуется HTTP-сервис 1С, Telegram сам шлёт на него POST с каждым обновлением. Обработчик — единая точка входа, которая маршрутизирует апдейт по типу (message, callback_query, inline_query) и всегда возвращает 200: любой другой код заставит Telegram повторять доставку, поэтому ошибки мы гасим и пишем в журнал, но наружу отдаём успех.
Функция ОбработатьВходящийHTTPЗапрос(Запрос) Экспорт
Попытка
РегистрыСведений.tl_Логи.ЛогHTTP(Запрос);
Данные = tl_Служебные.ОбработатьJSON(Запрос.ПолучитьТелоКакСтроку());
Если НЕ Данные["inline_query"] = Неопределено Тогда
Возврат Обработать_inline_query(Данные["inline_query"]);
ИначеЕсли НЕ Данные["message"] = Неопределено Тогда
Возврат Обработать_message(Данные["message"]);
ИначеЕсли НЕ Данные["callback_query"] = Неопределено Тогда
Возврат Обработать_callback_query(Данные["callback_query"]);
Иначе
Возврат Новый HTTPСервисОтвет(200);
КонецЕсли;
Исключение
// Telegram повторяет доставку при коде != 200 — всегда отвечаем 200,
// а ошибку фиксируем в журнале.
Возврат Новый HTTPСервисОтвет(200);
КонецПопытки;
КонецФункции
Второй режим — long polling через регламентное задание: по расписанию 1С сама опрашивает getUpdates, начиная со смещения, сохранённого в константе tl_UpdateID. Это удобно на стендах, где нет «белого» IP и валидного сертификата под webhook. Один и тот же код обработки сообщений переиспользуется в обоих режимах.
Регистрация покупателя по номеру телефона
Сценарий лояльности прост для покупателя и полностью автоматичен со стороны 1С. На команду /start бот присылает клавиатуру с единственной кнопкой, у которой выставлен флаг request_contact — Telegram сам предлагает поделиться номером. Как только приходит объект contact, данные (имя, телефон, chat_id, user_id) складываются в регистр сведений tl_Карты — это очередь на выпуск карт.
Процедура Обработать_message(message)
chat = message["chat"];
id = chat["id"];
Если message["text"] = "/start" Тогда
Сообщение = "Для регистрации в программе лояльности нажмите кнопку под сообщением."
+ " В течение 1 минуты придёт подтверждение регистрации.";
tl_Методы.sendMessage(id, Сообщение, , , , , СформироватьКлавиатуруРегистрация());
ИначеЕсли НЕ message.Получить("contact") = Неопределено Тогда
Результат = ЗарегистрироватьКартуЛояльности(id, message.Получить("contact"));
Если Результат = Ложь Тогда
tl_Методы.sendMessage(id, "Ошибка регистрации. Обратитесь к администратору.");
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Функция СформироватьКлавиатуруРегистрация()
МассивРядов = Новый Массив;
МассивКнопок = Новый Массив;
// request_contact = Истина — Telegram сам просит поделиться номером телефона
МассивКнопок.Добавить(tl_Типы.KeyboardButton("Отправить телефон для регистрации", Истина));
МассивРядов.Добавить(МассивКнопок);
Возврат tl_Типы.ReplyKeyboardMarkup(МассивРядов, Истина, Истина);
КонецФункции
Автоматический выпуск карт лояльности
Отдельное регламентное задание разбирает очередь регистраций и заводит карты в транзакции. Телефон нормализуется к единому формату (учитываются варианты +7, 8 и казахстанская запись), проверяется по уже выпущенным картам — так исключается дубль, — и создаётся новая карта копированием эталонного шаблона из константы. Карта получает код, равный номеру телефона, и сразу работает на кассе. Покупателю уходит поздравление, а обработанная запись удаляется из очереди.
Функция СоздатьКарты() Экспорт
Попытка
НачатьТранзакцию();
// Уже существующие карты — чтобы не выдать дубль
Запрос = Новый Запрос("ВЫБРАТЬ ИнформационныеКарты.КодКарты ИЗ Справочник.ИнформационныеКарты КАК ИнформационныеКарты");
ДанныеКарт = Новый Соответствие;
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
ДанныеКарт.Вставить(Выборка.КодКарты, Истина);
КонецЦикла;
НаборЗаписей = РегистрыСведений.tl_Карты.СоздатьНаборЗаписей();
НаборЗаписей.Прочитать();
Шаблон = Константы.tl_ШаблонКарты.Получить(); // эталонная карта-образец
Для каждого Стр Из НаборЗаписей Цикл
Телефон = ФорматироватьНомерТелефона(Стр.phone_number);
Если ЗначениеЗаполнено(ДанныеКарт[Телефон]) Тогда
tl_Методы.sendMessage(Стр.chat_id, "Вы уже зарегистрированы!");
Продолжить;
КонецЕсли;
НоваяКарта = Шаблон.Скопировать();
НоваяКарта.Наименование = Телефон + "_бот";
НоваяКарта.ВладелецКарты = Телефон;
НоваяКарта.КодКарты = Телефон;
НоваяКарта.ДатаОткрытия = ТекущаяДата();
НоваяКарта.Записать();
tl_Методы.sendMessage(Стр.chat_id, "Поздравляем! Вы успешно зарегистрированы!");
КонецЦикла;
НаборЗаписей.Очистить();
НаборЗаписей.Записать(); // обработанные регистрации убираем из очереди
ЗафиксироватьТранзакцию();
Исключение
Если ТранзакцияАктивна() Тогда ОтменитьТранзакцию(); КонецЕсли;
РегистрыСведений.tl_Логи.ЗаписатьОшибку(ИнформацияОбОшибке(), Истина);
КонецПопытки;
КонецФункции
Обмен, логирование (регистр tl_Логи со всеми HTTP-запросами и ответами), консоль разработчика и форма настройки токена — тоже часть подсистемы. Инлайн-режим реализован полностью: типы InputTextMessageContent, InputLocationMessageContent, InputVenueMessageContent и inline-клавиатуры вынесены в отдельный модуль на случай расширения бота.
Результат
Программа лояльности получила самостоятельный канал регистрации в Telegram, полностью живущий внутри 1С:Розницы: ни внешнего сервера бота, ни платных коннекторов, ни ручного заведения карт. Токен, режим (webhook/polling) и шаблон карты настраиваются в самой конфигурации, а весь трафик и ошибки видны в журнале. Подсистема получилась переиспользуемой — тот же слой Bot API можно навесить на любую типовую конфигурацию, где нужен Telegram-канал.
Смежный кейс по той же теме лояльности, но со стороны учёта в 1С:УТ — доработка программы лояльности с анкетой и автобонусами. Если нужен полноценный интерфейс внутри Telegram (Web App), а не только команды бота — это тема курса Telegram Web App 1С, где разбирается связка HTTP-сервиса 1С и мини-приложения Telegram.
Нужен Telegram-бот или Web App, встроенный прямо в вашу 1С? Мы проектируем и внедряем такие решения под ключ — начните с курса Telegram Web App 1С или напишите нам по задаче.