
Всем привет! Данное расширение призвано сократить ваше время на написание логики работы с Telegram-ботами при различных сценариях их использования. На текущий момент осуществлена поддержка следующих сценариев:
- Рассылка отчетов 1С
- Поэтапное общение с пользователями
- Исполнение произвольного кода
Кроме того расширение включает в себя еще ряд других функциональных возможностей, таких как подключение вебхуков, настройка регламентных заданий и контроль авторизации чатов. Обо всем этом (с примерами) и поговорим в этой статье.
Расширение создано для конфигураций на основе БСП, в частности разработка велась под конфигурацию "Управление торговлей 11.5.16.74" (версия БСП 3.1.9.232).
Проект опенсорсный, с исходным кодом можно ознакомиться в репозитории на GitHub.
Содержание:
Настройка бота
Для начала работы нам понадобится, собственно, сам бот в Telegram. Если бота у вас еще нет, то создать его можно буквально в несколько кликов с помощью BotFather: начинаем чат с BotFather, вводим команду /newbot, придумываем для нашего бота наименование и идентификатор, и вуаля. Теперь у вас есть свой бот в Telegram. Сразу после создания бота вы получите его уникальный токен авторизации, который должен выглядеть примерно так: 123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11
Теперь мы можем переключиться в 1С и продолжить настройку нашего бота там. После подключения расширения к вашей конфигурации в разделе "НСИ и администрирование" у вас появится новая подсистема "Управление ботами Telegram", где будет расположена ссылка на справочник "Боты Telegram". Создадим новый экземпляр справочника.

На вкладке "Настройки" укажем наименование бота и тот самый токен, что мы получили при создании бота.
Далее нужно будет определиться с вариантом взаимодействия между 1С и ботом Telegram - регламентное задание или с вебхук.
Если выбрать вариант с регламентным заданием, то для бота будет создано отдельное регламентное задание, которое по заданному расписанию будет обращаться к серверу Telegram, забирать оттуда все полученные ботом новые сообщения, после чего обрабатывать каждое из них в отдельности и отправлять ответы. Естественно, если мы хотим, чтобы отклик от бота на каждое введеное пользователем сообщение был бы быстрым, то расписание регламентного задания придется выставить с довольно высокой периодичностью.
Если для вас это проблема, то, конечно, лучше будет воспользоваться вариантом взаимодействия через вебхук, однако он потребует от вас некоторых дополнительных усилий, таких как публикация базы без авторизации, установка сертификата SSL и доменного имени. Подробнее узнать о том, как это все сделать, можно в замечательной статье от пользователя solidsun. В скобочках только замечу, что приведенный в статье вариант с бесплатными доменами от my.freenom.com более неактуален, вместо него могу посоветовать субдомены от duckdns. Кроме того, создавать HTTP-сервис, как это указано в статье, вам тоже не нужно - встроенный HTTP-сервис уже имеется в расширении.
Далее в форме настройки бота можно будет ввести полный URL-адрес вебхука, который будет выглядеть следующим образом:
Адрес публикации базы + /hs/telegram-bot-management/webhook/ + Имя бота
Например, https://mydomain.ru/1c-basename/hs/telegram-bot-management/webhook/my_bot_name
Дополнительно можно ввести секретный заголовок для вебхука, чтобы удостовериться, что запросы к HTTP-сервису действительно приходят от Telegram, а не из какого-нибудь стороннего сервиса.
Теперь можем нажать на кнопку "Установить вебхук", и если все было настроено правильно, то в 1С будет выведено оповещение об успешной установке вебхука. С помощью соответствующих кнопок на форме можно также отключить вебхук или получить информацию об уже подключенном к боту вебхуке.
Чаты, команды и сценарии
Все взаимодействие между пользователем и ботом в Telegram осуществляется через команды. Задать список доступных команд бота можно на вкладке "Команды" формы справочника бота. Новую команду нужно добавить в соответствующую табличную часть на форме бота, присвоить ей имя, написанное на латинице по правилам Telegram, текстовое описание и сценарий. Для непосредственной установки команд или их очистки, т.е. для синхронизации с реальными командами бота в Telegram, можно воспользоваться соответствующими кнопками, расположенными над табличной частью.
Что такое сценарий? Это отдельный справочник, внутри которого прописывается вся логика поведения бота при получении им от пользователя той иной команды. На текущий момент имеется возможность создать три типа сценариев: "Отправка отчета", Общение" и "Произвольный сценарий". Кроме того, здесь же можно настроить дополнительные свойства поведения, такие как авторизация чатов или регламентное задание исполнения.
Информация о каждом пользователе, написавшим боту, будет автоматически сохраняться в справочнике "Чаты ботов Telegram". Каждый такой чат закреплен за конкретным ботом.
Рассылка отчетов
Представим, что перед нами стоит следующая задача: при вызове пользователем определенной команды в Telegram, например /report, бот должен вернуть ответ в виде файла отчета, сформированного на стороне 1С.
Для решения этой задачи создадим новый сценарий с типом "Отправка отчета". На вкладке "Сценарий" в таком случае станет доступна табличная часть, в которой нужно будет указать вариант отчета, планируемый к отправке в качестве ответа, а также указать одно или несколько расширений файла.

Сохраняем сценарий, после чего в справочнике ботов на вкладке "Команды" добавляем новую команду /report, которой присваиваем сценарий отправки отчета. Дополнительно указываем краткое описание команды, которое будет отображаться у пользователей бота. Сохраняем изменения и далее нажимаем на кнопку "Установить команды", чтобы синхронизировать заданный список команд в 1С с реально доступными командами бота в Telegram.

Проверяем результат:

Произвольный сценарий
Возможно, что вы хотите обработать полученную ботом команду неким особым образом. Это может быть что угодно. Например, вы хотите вернуть информацию о продажах за день в простом текстовом формате. Или, предположим, вы хотите вернуть сообщение с информацией об остатках средств на банковских счетах.
Реализовать абсолютно любую логику поведения бота можно с помощью сценария произвольного типа.

В текстовом поле, доступном при выборе данного типа сценария, вам нужно будет прописать ваш скрипт на встроенном языке платформы 1С. Выполнение кода происходит в безопасном режиме.
Описание доступных переменных
Внутри контекста исполнения доступны следующие переменные:
Переменная |
Тип |
Описание |
Параметры |
Структура |
Содержит в себе объекты, описывающие окружение исполняемого кода |
Результат |
Структура |
Структура, в которую помещается результат исполнения сценария |
Описание структуры "Параметры":
Ключ |
Тип |
Описание |
Бот |
СправочникСсылка.
УБТ_БотыTelegram |
Бот, в который поступила команда |
Сценарий |
СправочникСсылка.
УБТ_СценарииБотовTelegram |
Текущий исполняемый сценарий |
Чат |
СправочникСсылка.
УБТ_ЧатыTelegram |
Чат, от которого поступил запрос на исполнение команды |
ИдентификаторЧата |
Строка |
Отдельно идентификатор чата, от которого поступил запрос |
Описание структуры "Результат":
Ключ |
Тип |
Описание |
Сообщение |
Структура:
* Текст |
Сообщение, которое будет возвращено пользователю по завершении исполнения сценария |
Файлы |
Массив Из Структура:
* ДвоичныеДанные - ДвоичныеДанные - Двоичные данные файла
* НаименованиеСРасширением - Строка - Полное наименование файла. Например, document.pdf |
Массив, содержащий структуры двоичных данных файлов, которые необходимо вернуть пользователю по завершении исполнения сценария. Доступна функция-конструктор
УБТ_РаботаСБотамиTelegram.
НоваяСтруктураФайлаСообщенияОтвета() |
Возможен комбинированный ответ: текст + файлы.
Ниже пример произвольного скрипта для выдачи информации о продажах за день:
Произвольный сценарий "Продажи за день с учетом открытых кассовых смен"
ТекущаяДата = ТекущаяДатаСеанса();
ВалютаУчета = Константы.ВалютаУправленческогоУчета.Получить();
Запрос = Новый Запрос("ВЫБРАТЬ РАЗРЕШЕННЫЕ
| Таблица.Ссылка.Склад КАК Склад,
| Таблица.Сумма КАК СуммаВыручки
|ПОМЕСТИТЬ ВтДанные
|ИЗ
| Документ.ЧекККМ.Товары КАК Таблица
|ГДЕ
| Таблица.Ссылка.Дата МЕЖДУ &НачалоПериода И &КонецПериода
| И Таблица.Ссылка.Проведен
| И Таблица.Ссылка.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыЧековККМ.Пробит)
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
| Таблица.Ссылка.Склад,
| -Таблица.Сумма
|ИЗ
| Документ.ЧекККМВозврат.Товары КАК Таблица
|ГДЕ
| Таблица.Ссылка.Дата МЕЖДУ &НачалоПериода И &КонецПериода
| И Таблица.Ссылка.Проведен
| И Таблица.Ссылка.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыЧековККМ.Пробит)
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
| Таблица.Склад,
| Таблица.СуммаВыручкиОборот
|ИЗ
| РегистрНакопления.ВыручкаИСебестоимостьПродаж.Обороты(&НачалоПериода, &КонецПериода, Авто, ) КАК Таблица
|ГДЕ
| НЕ Таблица.Регистратор ССЫЛКА Документ.ОтчетОРозничныхПродажах
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВтДанные.Склад КАК Склад,
| СУММА(ВтДанные.СуммаВыручки) КАК СуммаВыручки
|ИЗ
| ВтДанные КАК ВтДанные
|
|СГРУППИРОВАТЬ ПО
| ВтДанные.Склад
|ИТОГИ ПО
| ОБЩИЕ");
Запрос.Установитьпараметр("НачалоПериода", НачалоДня(ТекущаяДата));
Запрос.Установитьпараметр("КонецПериода", КонецДня(ТекущаяДата));
ТекстСообщенияНачало = СтрШаблон("ПРОДАЖИ ЗА ДЕНЬ (%1):", Формат(ТекущаяДата, "ДФ=dd.MM.yyyy"));
ТекстСообщения = "";
ТекстСообщенияИтоги = "";
ВыборкаИтоги = Запрос.Выполнить().Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Если ВыборкаИтоги.Следующий() Тогда
ФорматСуммы = "ЧЦ=15; ЧДЦ=2; ЧН=0";
ТекстСообщенияИтоги = СтрШаблон("ИТОГО:
|Выручка: %1 %2",
Формат(ВыборкаИтоги.СуммаВыручки, ФорматСуммы),
ВалютаУчета,);
Выборка = ВыборкаИтоги.Выбрать();
Пока Выборка.Следующий() Цикл
ТекстСообщения = ТекстСообщения + Символы.ПС + СтрШаблон("%1:
|Выручка: %2 %3",
Выборка.Склад,
Формат(Выборка.СуммаВыручки, ФорматСуммы),
ВалютаУчета);
ТекстСообщения = ТекстСообщения + Символы.ПС
КонецЦикла;
КонецЕсли;
Если ЗначениеЗаполнено(ТекстСообщения) Тогда
ТекстСообщения = ТекстСообщенияНачало + Символы.ПС + ТекстСообщения + Символы.ПС + ТекстСообщенияИтоги;
Иначе
ТекстСообщения = ТекстСообщенияНачало + Символы.ПС + "Нет данных о продажах";
КонецЕсли;
Результат.Сообщение.Текст = ТекстСообщения;

Поэтапное общение
Порой простого одинарного исполнения скрипта при обработке команд ботом бывает недостаточно. Зачастую требуется построить более сложную логику взаимодействия с пользователем, которая выстраивается в целую цепочку действий типа запрос-ответ: пользователь отправляет команду, а бот в ответ просит пользователя выполнить определенное действие. Так продолжается до тех пор, пока цепочка не будет полностью завершена.
Реализация такого варианта взаимодействия доступна с помощью сценария с типом "Общение".

Так же, как и в случае и произвольным сценарием, от вас потребуется прописать всю логику исполнения команды на встроенном языке 1С, однако теперь вы сможете ввести сразу несколько таких скриптов, разбив их по этапам.
Помимо предопределенных переменных сценария, в общении вам так же доступны особые методы, с помощью которых вы можете запоминать и позже восстанавливать определенные значения, указывать кнопки клавиатуры, а также переходить от одного этапа к другому.
Доступные переменные и методы
Доступные переменные:
Переменная |
Тип |
Описание |
Бот |
СправочникСсылка.УБТ_БотыTelegram |
Бот, в который поступила команда |
Чат |
СправочникСсылка.УБТ_ЧатыTelegram |
Чат, от которого поступил запрос на исполнение команды |
ИдентификаторЧата |
Строка |
Идентификатор чата, от которого поступил запрос |
Сценарий |
СправочникСсылка.
УБТ_СценарииБотовTelegram |
Текущий исполняемый сценарий |
Параметры |
Структура |
Содержит в себе объекты, описывающие окружение исполняемого кода |
Результат |
Структура |
Структура, в которую помещается результат исполнения сценария |
Описание структуры "Параметры":
Ключ |
Тип |
Описание |
НаименованиеТекущегоЭтапа |
Строка |
Наименование текущего этапа сценария как оно указано в табличной части "Этапы" справочника "Сценарии ботов Telegram" |
ИдентификаторТекущегоЭтапа |
Строка |
GUID текущего этапа сценария как он указан в табличной части "Этапы" справочника "Сценарии ботов Telegram" |
ИдентификаторСообщения |
Строка |
Идентификатор полученного сообщения, сформированный на стороне Telegram |
ТекстСообщения |
Строка |
Текст полученного ботом сообщения |
Контакт |
Структура:
* Имя - Строка
* Номер телефона - Строка |
В случае если боту был передан контакт, то в данной структуре будет заполнено имя и номер телефона контакта |
ПроизвольныйКод |
Строка |
Текст исполняемого кода |
КнопкиКлавиатуры |
Массив Из Структура:
* Наименование - Строка
* ЗапросНомераТелефона - Булево |
Массив кнопок клавиатуры чата. Доступна функция-конструктор НоваяСтруктураКнопкаКлавиатуры() |
Доступные методы:
Имя метода |
Параметры |
Возвращаемое значений |
Описание |
ЗавершитьЭтап |
НаименованиеЭтапа - Строка |
Нет |
Завершает текущий этап общения и переводит общение на следующий этап. Если все этапы завершены, то общение закрывается. |
СохранитьЗначениеОбщения |
* Ключ - Строка
* Значение - Произвольный |
Нет |
Сохраняет значение общения, к которому позже можно будет обратиться на любом из этапов общения |
СохранитьЗначениеОбщенияВМассиве |
* Ключ - Строка
* Значение - Произвольный |
Нет |
Работает так же, как и метод СохранитьЗначениеОбщения, но в отличие от него помещает значение сразу в массив |
УдалитьСохраненноеЗначениеОбщения |
* Ключ - Строка |
Нет |
Удаляет сохраненное значение по переданному ключу |
УдалитьСохраненноеЗначениеОбщенияИзМассива |
* Ключ - Строка
* Значение - Произвольный |
Нет |
Удаляет значение из массива сохраненных значений |
СохраненныеЗначенияОбщения |
Нет |
Структура |
Структура сохраненных значений общения |
Пример общения: прием заказов от клиентов
Рассмотрим пример построения сложного поэтапного общения между пользователем и ботом: мы создадим команду, с помощью которой можно можно будет оформлять заказы от клиентов. Весь процесс будет состоять из следующих действий:
- Пользователь запускает команду создания заказа
- В ответ бот приветствует пользователя и информирует его о том, что дальше от пользователя потребуется ввести в определенном формате артикулы и количество номенклатурных позиций, которые он планирует заказать.
- Пользователь вводит артикулы и номенклатуры
- Бот возвращает информацию о введенных товарах и добавляет их в корзину.
- Пункт 4 может повторяться до тех пор, пока пользователь не нажмет на кнопку "Завершить"
- После этого бот завершает подбор товаров и просит пользователя нажать на кнопку, чтобы отправить свой номер телефона
- Пользователь нажимает на кнопку "Отправить свой номер телефона"
- Бот создает заказ клиента и при необходимости нового контрагента, после чего сообщает пользователю номер заказа и его итоговую сумму.
Для того чтобы реализовать подобную логику мы создадим сценарий общения из трех этапов:
Результат.Сообщение.Текст = "Давайте оформим новый заказ.
|
|Введите список товаров, которые вы хотите заказать, в следующем формате:
|
|Артикул товара 1: Количество
|Артикул товара 2: Количество
|
|После окончания подбора товаров нажмите на кнопку ""Завершить""";
ЗавершитьЭтап(Параметры.НаименованиеТекущегоЭтапа);
Этап 2 - Ввод товаров и номера телефона
В данном этапе мы также укажем предопределенную кнопку клавиатуры "Завершить". Обратите внимание, что позже кнопки клавиатуры будут программным образом переиначены, чтобы вывести пользователю кнопку "Отправить свой номер телефона".

Если Параметры.ТекстСообщения = "Завершить" Тогда
ВидЦены = Справочники.ВидыЦен.НайтиПоНаименованию("Оптовая");
Валюта = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(ВидЦены, "ВалютаЦены");
СохраненныеЗначения = СохраненныеЗначенияОбщения();
ТекстСообщения = "Выбраны следующие товары:
|";
Итого = 0;
Для Каждого ЗаказаннаяПозиция Из СохраненныеЗначения.Товары Цикл
ТекстСообщения = ТекстСообщения + Символы.ПС + СтрШаблон("%1 (%2) - %3 шт. - %4 %5", ЗаказаннаяПозиция.Номенклатура, ЗаказаннаяПозиция.Артикул, ЗаказаннаяПозиция.Количество, ЗаказаннаяПозиция.Сумма, Валюта);
Итого = Итого + ЗаказаннаяПозиция.Сумма;
КонецЦикла;
ТекстСообщения = ТекстСообщения + СтрШаблон("
|
|Итого: %1 %2
|
|Теперь отправьте нам свой номер телефона, по которому мы сможем с вами связаться.", Итого, Валюта);
Параметры.КнопкиКлавиатуры.Очистить();
КнопкаКлавиатуры = Справочники.УБТ_ОбщенияБотовTelegramСЧатами.НоваяСтруктураКнопкаКлавиатуры();
КнопкаКлавиатуры.Наименование = "Отправить свой номер телефона";
КнопкаКлавиатуры.ЗапросНомераТелефона = Истина;
Параметры.КнопкиКлавиатуры.Добавить(КнопкаКлавиатуры);
Результат.Сообщение.Текст = ТекстСообщения;
ЗавершитьЭтап(Параметры.НаименованиеТекущегоЭтапа);
Иначе
СтрокиТоваров = СтрРазделить(Параметры.ТекстСообщения, Символы.ПС);
ТаблицаТоваров = Новый ТаблицаЗначений;
ТаблицаТоваров.Колонки.Добавить("Артикул", Новый ОписаниеТипов("Строка", , , , Новый КвалификаторыСтроки(50)));
ТаблицаТоваров.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число", , , Новый КвалификаторыЧисла(15, 3)));
ТекстОшибки = "";
Для Каждого СтрокаТовара Из СтрокиТоваров Цикл
АртикулИКоличество = СтрРазделить(СтрокаТовара, ":");
Если АртикулИКоличество.Количество() = 2 Тогда
Артикул = СокрЛП(АртикулИКоличество[0]);
КоличествоСтрокой = СокрЛП(АртикулИКоличество[1]);
Количество = СтроковыеФункцииКлиентСервер.СтрокаВЧисло(КоличествоСтрокой);
Если Количество = Неопределено Или Количество <= 0 Тогда
ТекстОшибки = ТекстОшибки + ?(ЗначениеЗаполнено(ТекстОшибки), Символы.ПС, "") + СтрШаблон("Укажите правильное количесто товара с артикулом %1", Артикул);
КонецЕсли;
СтрокаТаблицыТоваров = ТаблицаТоваров.Добавить();
СтрокаТаблицыТоваров.Артикул = Артикул;
СтрокаТаблицыТоваров.Количество = Количество;
Иначе
ТекстОшибки = "Пожалуйста, введите список товаров в следующем формате:
|
|Артикул товара 1: Количество
|Артикул товара 2: Количество";
Прервать;
КонецЕсли;
КонецЦикла;
Если ЗначениеЗаполнено(ТекстОшибки) Тогда
Результат.Сообщение.Текст = ТекстОшибки;
Иначе
ВидЦены = Справочники.ВидыЦен.НайтиПоНаименованию("Оптовая");
Валюта = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(ВидЦены, "ВалютаЦены");
Запрос = Новый Запрос("ВЫБРАТЬ
| ТаблицаТоваров.Артикул КАК Артикул,
| ТаблицаТоваров.Количество КАК Количество
|ПОМЕСТИТЬ ВтТовары
|ИЗ
| &ТаблицаТоваров КАК ТаблицаТоваров
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВтТовары.Артикул КАК Артикул,
| ВтТовары.Количество КАК Количество,
| МАКСИМУМ(ЕСТЬNULL(Номенклатура.Ссылка, ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка))) КАК Номенклатура
|ПОМЕСТИТЬ ВтНоменклатура
|ИЗ
| ВтТовары КАК ВтТовары
| ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Номенклатура
| ПО ВтТовары.Артикул = Номенклатура.Артикул
| И (НЕ Номенклатура.ПометкаУдаления)
|
|СГРУППИРОВАТЬ ПО
| ВтТовары.Артикул,
| ВтТовары.Количество
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВтНоменклатура.Артикул КАК Артикул,
| ВтНоменклатура.Количество КАК Количество,
| ВтНоменклатура.Номенклатура КАК Номенклатура,
| ЦеныНоменклатуры25СрезПоследних.Цена * ВтНоменклатура.Количество КАК Сумма
|ИЗ
| ВтНоменклатура КАК ВтНоменклатура
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры25.СрезПоследних(
| ,
| ВидЦены = &ВидЦены И ХарактеристикаЦО = ЗНАЧЕНИЕ(Справочник.ХарактеристикиНоменклатурыДляЦенообразования.ПустаяСсылка)
| И СерияЦО = ЗНАЧЕНИЕ(Справочник.СерииНоменклатурыДляЦенообразования.ПустаяСсылка)
| И УпаковкаЦО = ЗНАЧЕНИЕ(Справочник.УпаковкиЕдиницыИзмерения.ПустаяСсылка)) КАК ЦеныНоменклатуры25СрезПоследних
| ПО (ВтНоменклатура.Номенклатура = ЦеныНоменклатуры25СрезПоследних.Номенклатура)
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВтНоменклатура.Артикул КАК Артикул
|ИЗ
| ВтНоменклатура КАК ВтНоменклатура
|ГДЕ
| ВтНоменклатура.Номенклатура = ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)");
Запрос.УстановитьПараметр("ТаблицаТоваров", ТаблицаТоваров);
Запрос.УстановитьПараметр("ВидЦены", ВидЦены);
МассивРезультатов = Запрос.ВыполнитьПакет();
ВыборкаТовары = МассивРезультатов[МассивРезультатов.Количество() - 2].Выбрать();
ВыборкаОшибки = МассивРезультатов[МассивРезультатов.Количество() - 1].Выбрать();
ТекстОшибки = "";
Пока ВыборкаОшибки.Следующий() Цикл
ТекстОшибки = ТекстОшибки + ?(ЗначениеЗаполнено(ТекстОшибки), Символы.ПС, "") + СтрШаблон("Не найден товар с артикулом %1, ВыборкаОшибки.Артикул)");
КонецЦикла;
Если ЗначениеЗаполнено(ТекстОшибки) Тогда
Результат.Сообщение.Текст = ТекстОшибки;
Иначе
ТекстСообщения = "";
Пока ВыборкаТовары.Следующий() Цикл
ЗаказаннаяПозиция = Новый Структура;
ЗаказаннаяПозиция.Вставить("Номенклатура", ВыборкаТовары.Номенклатура);
ЗаказаннаяПозиция.Вставить("Артикул", ВыборкаТовары.Артикул);
ЗаказаннаяПозиция.Вставить("Количество", ВыборкаТовары.Количество);
ЗаказаннаяПозиция.Вставить("Сумма", ВыборкаТовары.Сумма);
СохранитьЗначениеОбщенияВМассиве("Товары", ЗаказаннаяПозиция);
ТекстСообщения = ТекстСообщения + ?(ЗначениеЗаполнено(ТекстСообщения), Символы.ПС, "") + СтрШаблон("%1 (%2) - %3 шт. - %4 %5", ВыборкаТовары.Номенклатура, ВыборкаТовары.Артикул, ВыборкаТовары.Количество, ВыборкаТовары.Сумма, Валюта);
КонецЦикла;
Результат.Сообщение.Текст = "Выбраны следующие товары:" + Символы.ПС + ТекстСообщения;
КонецЕсли;
КонецЕсли;
КонецЕсли;
НаименованиеКонтрагента = Параметры.Контакт.Имя;
НомерТелефона = Параметры.Контакт.Имя;
ВидЦены = Справочники.ВидыЦен.НайтиПоНаименованию("Оптовая");
Валюта = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(ВидЦены, "ВалютаЦены");
ЗаказКлиента = Документы.ЗаказКлиента.СоздатьДокумент();
ЗаказКлиента.Заполнить(Неопределено);
ЗаказКлиента.Дата = ТекущаяДатаСеанса();
ЗаказКлиента.Организация = Справочники.Организации.НайтиПоНаименованию("Торговый дом ""Комплексный""");
ЗаказКлиента.Валюта = Валюта;
ЗаказКлиента.ЦенаВключаетНДС = Истина;
ЗаказКлиента.НалогообложениеНДС = Перечисления.ТипыНалогообложенияНДС.ПродажаОблагаетсяНДС;
Запрос = Новый Запрос("ВЫБРАТЬ
| КонтактнаяИнформация.Ссылка КАК Партнер
|ИЗ
| Справочник.Партнеры.КонтактнаяИнформация КАК КонтактнаяИнформация
|ГДЕ
| КонтактнаяИнформация.Представление = &НомерТелефона
| И КонтактнаяИнформация.Тип = ЗНАЧЕНИЕ(Перечисление.ТипыКонтактнойИнформации.Телефон)");
Запрос.УстановитьПараметр("НомерТелефона", НомерТелефона);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
ЗаказКлиента.Партнер = Выборка.Партнер;
ПартнерыИКонтрагенты.ЗаполнитьКонтрагентаПартнераПоУмолчанию(ЗаказКлиента.Партнер, ЗаказКлиента.Контрагент);
Иначе
ПартнерОбъект = Справочники.Партнеры.СоздатьЭлемент();
ПартнерОбъект.Наименование = НаименованиеКонтрагента;
ПартнерОбъект.НаименованиеПолное = НаименованиеКонтрагента;
ПартнерОбъект.ЮрФизЛицо = Перечисления.КомпанияЧастноеЛицо.ЧастноеЛицо;
ПартнерОбъект.Клиент = Истина;
ПартнерОбъект.Записать();
ТелефонXML = УправлениеКонтактнойИнформацией.КонтактнаяИнформацияВXML(НомерТелефона, НомерТелефона, Перечисления.ТипыКонтактнойИнформации.Телефон);
ТелефонJSON = УправлениеКонтактнойИнформацией.КонтактнаяИнформацияВJSON(ТелефонXML);
УправлениеКонтактнойИнформацией.ДобавитьКонтактнуюИнформацию(ПартнерОбъект, ТелефонJSON, Справочники.ВидыКонтактнойИнформации.ТелефонПартнера, ТекущаяДатаСеанса(), Ложь);
КонтрагентОбъект = Справочники.Контрагенты.СоздатьЭлемент();
КонтрагентОбъект.Наименование = НаименованиеКонтрагента;
КонтрагентОбъект.НаименованиеПолное = НаименованиеКонтрагента;
КонтрагентОбъект.Партнер = ПартнерОбъект.Ссылка;
КонтрагентОбъект.ЮрФизЛицо = Перечисления.ЮрФизЛицо.ФизЛицо;
КонтрагентОбъект.Записать();
ЗаказКлиента.Партнер = ПартнерОбъект.Ссылка;
ЗаказКлиента.Контрагент = КонтрагентОбъект.Ссылка;
КонецЕсли;
СохраненныеЗначения = СохраненныеЗначенияОбщения();
СтруктураДействий = Новый Структура;
СтруктураПересчетаСуммы = ОбработкаТабличнойЧастиКлиентСервер.ПараметрыПересчетаСуммыНДСВСтрокеТЧ(ЗаказКлиента);
СтруктураДействий.Вставить("ЗаполнитьСтавкуНДС", ОбработкаТабличнойЧастиКлиентСервер.ПараметрыЗаполненияСтавкиНДС(ЗаказКлиента, Истина));
СтруктураДействий.Вставить("ЗаполнитьЦенуПродажи", ОбработкаТабличнойЧастиКлиентСервер.ПараметрыЗаполненияЦеныВСтрокеТЧ(ЗаказКлиента));
СтруктураДействий.Вставить("ПересчитатьКоличествоЕдиниц");
СтруктураДействий.Вставить("ПересчитатьСуммуНДС", СтруктураПересчетаСуммы);
СтруктураДействий.Вставить("ПересчитатьСуммуСНДС", СтруктураПересчетаСуммы);
СтруктураДействий.Вставить("ПересчитатьСумму");
СтруктураДействий.Вставить("ПересчитатьСуммуСУчетомРучнойСкидки", Новый Структура("Очищать", Ложь));
СтруктураДействий.Вставить("ПересчитатьСуммуСУчетомАвтоматическойСкидки", Новый Структура("Очищать", Истина));
СтруктураДействий.Вставить("ПересчитатьСуммуСУчетомСкидкиБонуснымиБаллами");
Для Каждого ЗаказаннаяПозиция Из СохраненныеЗначения.Товары Цикл
СтрокаТабличнойЧасти = ЗаказКлиента.Товары.Добавить();
СтрокаТабличнойЧасти.Номенклатура = ЗаказаннаяПозиция.Номенклатура;
СтрокаТабличнойЧасти.Количество = ЗаказаннаяПозиция.Количество;
СтрокаТабличнойЧасти.КоличествоУпаковок = ЗаказаннаяПозиция.Количество;
СтрокаТабличнойЧасти.ВидЦены = ВидЦены;
ОбработкаТабличнойЧастиСервер.ОбработатьСтрокуТЧ(СтрокаТабличнойЧасти, СтруктураДействий, Неопределено);
СтрокаТабличнойЧасти.ВариантОбеспечения = Перечисления.ВариантыОбеспечения.КОбеспечению;
КонецЦикла;
СтруктураПараметры = СкидкиНаценкиЗаполнениеСервер.НовыйПараметрыРассчитать();
СтруктураПараметры.ПрименятьКОбъекту = Ложь;
СтруктураПараметры.ТолькоПредварительныйРасчет = Ложь;
СтруктураПараметры.ВосстанавливатьУправляемыеСкидки = Истина;
СтруктураПараметры.УправляемыеСкидки = Неопределено;
СкидкиНаценкиЗаполнениеСервер.Рассчитать(ЗаказКлиента, СтруктураПараметры);
ЗаказКлиента.СкидкиРассчитаны = Истина;
ЗаказыСервер.УстановитьКлючВСтрокахТабличнойЧасти(ЗаказКлиента, "Товары");
ЗаказКлиента.СуммаДокумента = ЗаказКлиента.ПолучитьСуммуЗаказанныхСтрок();
ЗаказКлиента.Записать(РежимЗаписиДокумента.Проведение);
Результат.Сообщение.Текст = СтрШаблон("Ваш заказ принят!
|
|Номер заказа: %1
|Сумма: %2 %3
|
|Скоро с вами свяжется наш оператор для уточнения деталей.
|
|Спасибо!",
ЗаказКлиента.Номер, ЗаказКлиента.СуммаДокумента, ЗаказКлиента.Валюта);
ЗавершитьЭтап(Параметры.НаименованиеТекущегоЭтапа);
Проверяем результат:

Авторизация пользователей
Вполне возможно, что какие-то из команд вашего бота будут предназначены для получения конфиденциальной информации, доступ к которой должен будет иметь только ограниченный круг лиц. Для того чтобы предотвратить несанкционированное использование команд бота вы можете включить у сценария признак "Требуется авторизация".

При включенном режиме обязательной авторизации пользователь должен будет получить право доступа на команду одним из двух способов:
1. Ввести одноразовый пароль, сгенерированный на стороне 1С
Просмотреть одноразовый пароль можно нажав на соответствующую кнопку в панели "Авторизация" справочника сценариев. Пароли генерируются в разрезе сценариев, т.е. на каждую защищенную команду нужно будет получать новый пароль. Частота обновления пароля - 60 секунд.

2. Отправить свою контактную информацию и дождаться подтверждения со стороны администратора 1С
Контактная информация пользователя в таком случае будет подвязана к вашему чату в 1С (каждый чат с ботом сохраняется в 1С в виде элемента справочника "Чаты ботов Telegram"). Администратор таким образом сможет идентифицировать чат по номеру телефона и задать ему доступ на защищенную команду в регистре сведений "Авторизованные чаты Telegram".

Персональные команды чата
Для каждого чата вы можете настроить его персональный набор команд. Делается это в форме справочника "Чаты ботов Telegram". Для непосредственной установки команд или их очистки, т.е. для синхронизации с реальными командами бота в Telegram, можно воспользоваться соответствующими кнопками, расположенными над табличной частью. После успешной установки в заданном чате будет доступен свой персональный набор команд, который будет отображаться только у него.

Исполнение сценариев по расписанию
Каждый из сценариев может выполняться самостоятельно по заданному расписанию. Настроить расписание можно на вкладке "Регламентное задание". Задать список чатов-получателей сообщения можно в соответствующей табличной части. Если список чатов оставить пустым, то сообщение будет разослано во все известные боту чаты.

Что дальше?
С одной стороны, в документации Telegram есть еще масса интересных методов, которые можно было бы подкрутить к расширению. Из того, что первое приходит в голову:
- Работа с сообщениями формата HTML и Markdown
- Поддержка изображений, видео, аудио - причем не только отправка, но также их получение и последующая обработка
- Создание опросов и сбор их результатов
- Оптимизация работы с файлами
А с другой стороны было бы неплохо хорошенько причесать текущий функционал. Например, чтобы облегчить написание кода в режиме предприятия можно было подключить Monaco Editor, а также добавить простую кнопку проверки на синтаксические ошибки.
Все реализуемо, но тут уже дело упирается в вопрос, насколько это в целом будет востребовано, а соответственно будет ли мотивация дальше продолжать работу по этому проекту.
Кстати, чтобы поднять мотивацию автора на продолжение работы, не забудьте поставить плюсик статье на Инфостарт, а также звездочку репозиторию на GitHub. Предложения, а также сообщения об ошибках оставляйте в разделе Issues.
Спасибо за внимание!