Телеграм бот 1С

Телеграм бот 1С

Задача:
Создать телеграм бота и научится работать с ним в «1С:Предприятие 8», через long polling и webhooks, используя Telegram bot API.

02.04.2021 Обновлено:
— Добавлено видео «Интеграция 1С с Telegram за 12 минут», где показано как с помощью системы взаимодействия работать с телеграм ботом.

1. Регистрация телеграм бота.

Регистрация нового телеграм бота, происходит с помощью бота https://t.me/botfather:


В результате, будет сформирован токен доступа, который необходимо сохранить и держать в секрете, а созданного бота добавить себе в приложение «Телеграм».

2. Два способа работы с телеграм ботом.

При помощи «Telegram Bot API», есть два взаимоисключающих способа работы с телеграм ботом:

  1. Метод «getUpdates» – самостоятельно, выполняем запрос получения новых обновлений.
  2. Использование «Webhooks» – обновления приходят автоматически от сервера, на указанный URL-адрес.

Оба способа, работают по протоколу HTTPS, поддерживают HTTP методы GET и POST. Запросы к методам API, должны быть представлены в виде:

https://api.telegram.org/bot<token>/METHOD_NAME

Ответ приходит в виде JSON, не обработанные обновления хранятся на сервере до тех пор, пока бот их не получит, но они не будут храниться дольше 24 часов.

3. Подготовка конфигурации.

3.1 Для удобвства отправки и получения данных по HTTP, будет использоваться «Коннектор», Владимира Бондаревского. По адресу https://github.com/vbondarevsky/Connector скачиваем и распаковываем «Коннектор: удобный HTTP-клиент для 1С:Предприятие 8»:

Коннектор: удобный HTTP-клиент для 1С:Предприятие 8

Коннектор: удобный HTTP-клиент для 1С:Предприятие 8


3.2 Создаем пустую (вспомогательную) конфигурацию и выполняем «Загрузить конфигурацию из файлов…», используя папку «src» (см. п. 3.1):
Коннектор: Загрузка конфигурации

Коннектор: Загрузка конфигурации


3.3
Создаем пустую (основную) конфигурацию и переносим общий модуль «КоннекторHTTP»:
Перенос общего модуля

Перенос общего модуля


3.4 Создаем константу «Токен» (тип «Строка», длина 100) и сохраняем в ней полученный токен из п. 1.
3.5 Добавляем подсистему и обработку «РаботаСБотом».
3.6 В обработке добавляем форму, далее реквизит формы «Журнал» (тип «ТекстовыйДокумент») и команду «ТестТокена», для которой создаем функцию обработчик с директивой «&НаСервереБезКонтекста». В обработчике, отправляем запрос к методу API «getMe» — который используется для проверки токена аутентификации и возвращает основную информацию о боте:

&НаКлиенте
Процедура ТестТокена(Команда)
	
	РезультатТеста = ТестТокенаНаСервере();
	
	Если НЕ ТипЗнч(РезультатТеста) = Тип("Соответствие") Тогда
		Журнал.ДобавитьСтроку(СтрШаблон("Тест не выполнен: %1", РезультатТеста));
		Возврат;
	КонецЕсли;
	
	ТестВыполнен = РезультатТеста["ok"];
	
	Журнал.ДобавитьСтроку(СтрШаблон("Тест выполнен: %1", ТестВыполнен));
	
	Если НЕ ТестВыполнен Тогда
		Возврат;
	КонецЕсли;
	
	Для каждого ЭлементРезультата Из РезультатТеста["result"] Цикл
		
		СтрокаРезультата = СтрШаблон("%1 : %2", ЭлементРезультата.Ключ, ЭлементРезультата.Значение);
		Журнал.ДобавитьСтроку(СтрокаРезультата);
		
	КонецЦикла;
	
КонецПроцедуры
&НаСервереБезКонтекста
Функция ТестТокенаНаСервере()
	
	Результат = Неопределено;
	
	АдресURL = СтрШаблон("https://api.telegram.org/bot%1/getMe", Константы.Токен.Получить());
	
	Попытка
		Результат = КоннекторHTTP.GetJson(АдресURL);	
	Исключение
		Результат = ОписаниеОшибки();
	КонецПопытки;
	
	Возврат Результат;
	
КонецФункции

Пояснение кода: На стороне клиента вызываем функцию «ТестТокенаНаСервере», в которой формируется «АдресURL» состоящий из адреса API, токена и метода. После выполняем HTTP запрос, при помощи функции «GetJson», в результате получаем десериализованный из JSON ответ сервера, с последующей обработкой и выводом в текстовый документ. Результат выполнения:

Метод "getMe"

Метод «getMe»

4. Метод «getUpdates».

Из 1С, самостоятельно, периодически обращаемся к методу API «getUpdates», для получения 100 самых ранних неподтвержденных (не полученных ранее) обновлений, в виде JSON объекта «Update».

Метод "getUpdates"

Метод «getUpdates»


4.1 На форму обработки, добавляем команду «Обновить» и функцию обработчик с директивой «&НаСервереБезКонтекста». Перед отправкой запроса, при необходимости заполняются параметры:

offset Идентификатор возвращаемого обновления — каждое обновление имеет свой числовой идентификатор «update_id».
limit Ограничивает количество получаемых обновлений. Принимаются значения от 1 до 100.
timeout Тайм-аут в секундах для длительного опроса.
allowed_updates Задается список типов обновлений, которые должен получать бот.

Для нашего примера, будет заполнен только параметр offset равным 0, для того чтобы получить все не обработанные обновления. Процедура обработки команды «Обновить» на клиенте:

&НаКлиенте
Процедура Обновить(Команда)
	
	Журнал.ДобавитьСтроку(Символы.ПС);
	
	РезультатОбновления = ОбновитьНаСервере();
	
	Если НЕ ТипЗнч(РезультатОбновления) = Тип("Соответствие") Тогда
		Журнал.ДобавитьСтроку(СтрШаблон("Обновление не выполнено: %1", РезультатОбновления));
		Возврат;
	КонецЕсли;
	
	ОбновлениеВыполнено = РезультатОбновления["ok"];
	
	Журнал.ДобавитьСтроку(СтрШаблон("Обновление выполнено: %1", ОбновлениеВыполнено));
	
	Если НЕ ОбновлениеВыполнено Тогда
		Возврат;
	КонецЕсли;
	
	МассивОбновлений = РезультатОбновления["result"];
	Для каждого Обновление Из МассивОбновлений Цикл
		
		Журнал.ДобавитьСтроку(СтрШаблон("%1%2", Символы.ПС, " -***- "));
		
		ОбработатьОбновление(Обновление);
		
	КонецЦикла;
	
КонецПроцедуры

Методы «ОбновитьНаСервере» и «ОбработатьОбновление»:

&НаСервереБезКонтекста
Функция ОбновитьНаСервере()
	
	РезультатОбновления = Неопределено;
	
	АдресURL = СтрШаблон("https://api.telegram.org/bot%1/getUpdates", Константы.Токен.Получить());
	
	ПараметрыЗапроса = Новый Структура;
	ПараметрыЗапроса.Вставить("offset", 0);
	
	Попытка
		РезультатОбновления = КоннекторHTTP.GetJson(АдресURL, ПараметрыЗапроса);	
	Исключение
		РезультатОбновления = ОписаниеОшибки();
	КонецПопытки;
	
	Возврат РезультатОбновления;
	
КонецФункции

&НаКлиенте
Процедура ОбработатьОбновление(Обновление)

	Для каждого ЭлементОбновления Из Обновление Цикл
		
		Ключ		= ЭлементОбновления.Ключ;
		Значение	= ЭлементОбновления.Значение;
		
		Если ТипЗнч(Значение) = Тип("Соответствие") Тогда
			
			Журнал.ДобавитьСтроку(СтрШаблон("%1%2", Символы.ПС, Ключ));
			ОбработатьОбновление(Значение);
			
		Иначе
			Журнал.ДобавитьСтроку(СтрШаблон("%1 : %2", Ключ, Значение));
		КонецЕсли;
		
	КонецЦикла;

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

Пояснение кода: На стороне клиента вызываем функцию «ОбновитьНаСервере», в которой формируется «АдресURL» состоящий из адреса API, токена и наименовании метода «getUpdates». После заполняем параметры и выполняем HTTP запрос, при помощи функции «GetJson», в результате получаем десериализованный из JSON ответ сервера, который передаем в процедуру «ОбработатьОбновление», где рекурсивно выводим данные в текстовый документ. В приложении «Телеграм», отправляем боту сообщение «Привет мир!». В обработке нажимаем «Обновить», в результате, будет получена информация обновления и отправленное сообщение:

Метод "getUpdates"

Метод «getUpdates»


Добавим немного функциональности в наш телеграм бот 1С и сделаем «Эхо бота», то есть при получении сообщения, бот будет отправлять его обратно.
4.2 Добавляем константу «НомерПоследнегоОбновления», значение которой будет использоваться в параметре offset, который необходим, для получения и подтверждения обработки обновлений.
4.3 На форму обработки добавим команду «ЭхоОтвет» и создадим обработчик с директивой «&НаСервере».

&НаСервере
Процедура ЭхоОтветНаСервере()
	
	Токен =  Константы.Токен.Получить();
	АдресURL = СтрШаблон("https://api.telegram.org/bot%1/getUpdates", Токен);
	
	НомерПоследнегоОбновления = Константы.НомерПоследнегоОбновления.Получить(); 
	СмещениеНомераОбновления  = НомерПоследнегоОбновления + 1;
	
	ПараметрыЗапроса = Новый Структура;
	ПараметрыЗапроса.Вставить("offset", Формат(СмещениеНомераОбновления, "ЧГ=0"));
	
	РезультатОбновления = Неопределено;
	Попытка
		РезультатОбновления = КоннекторHTTP.GetJson(АдресURL, ПараметрыЗапроса);	
	Исключение
		Журнал.ДобавитьСтроку(ОписаниеОшибки());
		Возврат;
	КонецПопытки;
	
	ОбновлениеВыполнено = РезультатОбновления["ok"];
	Если НЕ ОбновлениеВыполнено Тогда
		Журнал.ДобавитьСтроку(СтрШаблон("Ошибка: %1 - %2", РезультатОбновления["error_code"], РезультатОбновления["description"]));
		Возврат;
	КонецЕсли;
	
	МассивОбновлений = РезультатОбновления["result"];
	Если МассивОбновлений.Количество() = 0 Тогда
		Журнал.ДобавитьСтроку("Нет сообщений.");
		Возврат;
	КонецЕсли;
	
	Для каждого Обновление Из МассивОбновлений Цикл
		
		ИдентификаторОбновления = Обновление["update_id"];
		
		Сообщение = Обновление.Получить("message");
		
		ИдентификаторСообщения	= Сообщение.Получить("message_id");
		ТектСообщения			= Сообщение.Получить("text");
		ДатаСообщения			= Сообщение.Получить("date");
		
		ОтправительСообщения	= Сообщение.Получить("from");
		ИмяПользователя			= ОтправительСообщения.Получить("username");
		
		Чат						= Сообщение.Получить("chat");
		ИдентификаторЧата		= Чат.Получить("id");
		
		АдресURL = СтрШаблон("https://api.telegram.org/bot%1/sendMessage", Токен);
		
		ПараметрыЗапроса = Новый Структура;
		ПараметрыЗапроса.Вставить("chat_id",	Формат(ИдентификаторЧата, "ЧГ=0"));
		ПараметрыЗапроса.Вставить("text",		ТектСообщения);
		
		РезультатОтправки = Неопределено;
		Попытка
			РезультатОтправки = КоннекторHTTP.GetJson(АдресURL, ПараметрыЗапроса);	
		Исключение
			Журнал.ДобавитьСтроку(ОписаниеОшибки());
			Продолжить;
		КонецПопытки;
		
		СообщениеОтправлено = РезультатОтправки["ok"];
		Если СообщениеОтправлено Тогда
			
			Журнал.ДобавитьСтроку(СтрШаблон("Сообщение: %1 - Отправлено пользователю: %2", ТектСообщения, ИмяПользователя));
			НомерПоследнегоОбновления = ИдентификаторОбновления;
			
		Иначе
			Журнал.ДобавитьСтроку(СтрШаблон("Ошибка: %1 - %2", РезультатОтправки["error_code"], РезультатОтправки["description"]));
		КонецЕсли;
		
	КонецЦикла;
	
	Если НомерПоследнегоОбновления >= СмещениеНомераОбновления Тогда
		Константы.НомерПоследнегоОбновления.Установить(НомерПоследнегоОбновления);		
	КонецЕсли;
	
КонецПроцедуры

Пояснение кода: На стороне клиента вызываем процедуру «ЭхоОтветНаСервере», в которой формируем запрос к методу «getUpdates», с учетом номера последнего обновления, для того чтобы предыдущие сообщения были «обработаны» и не приходили. Выполняем HTTP запрос, при помощи функции «GetJson» и получаем массив не «обработанных» обновлений. В каждом элементе массива содержится данные сообщения, из которых получаем идентификатор чата «chat_id» и текст сообщения «text».
Формируем новый запрос к методу «sendMessage» в результате которого будет отправлено ответное сообщение, отправителю. Протестируем, для этого, в приложении «Телеграм», отправим сообщение боту, после, в обработке нажимаем кнопку «Эхо ответ»:

Метод "sendMessage"

Метод «sendMessage»

5. Использование «Webhooks»

Webhooks — это механизм оповещения о происходящих в системе событиях посредством функций обратных вызовов. Когда случается интересующее клиента событие, сервер отправляет HTTP-запрос на URL-адрес, предоставленный клиентом для приема webhooks. См. «Вебхуки: как получать данные без промедления и опросов API».
Для локальной веб-разработки и тестирования используется «Open Server» — это портативная программная среда, созданная специально для веб-разработчиков, в составе которой множество необходимых модулей, в нашем случае это Apache.

5.1 Скачиваем и устанавливаем Open Server.
5.2 В «Командной строке» (запущенной от имени администратора) переходим в каталог «C:\OpenServer\modules\http\Apache_2.4-PHP_7.2-7.4\bin» и выполняем команду:

httpd -k install

Сервис Apache

Сервис Apache


Это необходимо, чтобы установить «Apache» как службу, для того чтобы «1С» смогла ее найти, более подробно описано на ИТС «8.10.2.2. Алгоритм поиска установленного веб-сервера».
5.3 Запускаем «1С» от имени администратора и добавляем HTTP сервис «ОбновленияТелеграм», в нем шаблон «Вебхук» и метод «POST» с обработчиком в модуле.
HTTP сервис 1С

HTTP сервис 1С

Функция ВебхукPOST(Запрос)
	
	Обновление = КоннекторHTTP.JsonВОбъект(Запрос.ПолучитьТелоКакСтроку());
	
	Сообщение = Обновление.Получить("message");
	
	ТектСообщения		= Сообщение.Получить("text");
	Чат					= Сообщение.Получить("chat");
	ИдентификаторЧата	= Чат.Получить("id");
	
	АдресURL = СтрШаблон("https://api.telegram.org/bot%1/sendMessage", Константы.Токен.Получить());
	
	ПараметрыЗапроса = Новый Структура;
	ПараметрыЗапроса.Вставить("text", ТектСообщения);
	ПараметрыЗапроса.Вставить("chat_id", Формат(ИдентификаторЧата, "ЧГ=0"));
	
	РезультатОтправки = Неопределено;
	Попытка
		РезультатОтправки = КоннекторHTTP.GetJson(АдресURL, ПараметрыЗапроса);	
	Исключение
		ЗаписьЖурналаРегистрации("ОбновленияТелеграм.ВебхукPOST", УровеньЖурналаРегистрации.Ошибка, , , ОписаниеОшибки());
		Возврат Новый HTTPСервисОтвет(503);
	КонецПопытки;
	
	Возврат Новый HTTPСервисОтвет(200);

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

Пояснение кода: При получении запроса от «Телеграм», десериализуем тело из JSON в объект, получаем исходные данные сообщения, такие как текст и идентификатор чата и тут же отправляем его обратно.

5.4 Откройте «Open Server Panel», но не запускайте, должен быть красный флажок. В конфигураторе открыть, меню «Администрирование – Публикация на веб-сервере…» выбрать каталог и нажать «Опубликовать», без перезапуска веб-сервера.

Публикация сервиса

Публикация сервиса

После чего, «1С» внесет изменения в файл «‪C:\OpenServer\modules\http\Apache_2.4-PHP_7.2-7.4\conf\httpd.conf»:‬

Apache config

Apache config


Их нужно сохранить в шаблоне «‪C:\OpenServer\userdata\config\Apache_2.4-PHP_7.2-7.4_server.conf», для того чтобы при следующем запуске «Open Server» они не затерлись.

5.5 Запускаем «Open Server», в результате чего у нас будет доступен HTTP сервис по адресу:

http://127.0.0.1/TelegramBot1C/hs/updates/

Для проверки воспользуемся приложением «Postman»:

Postman test

Postman test

5.6 Webhooks работают только по протоколу HTTPS, для которого необходим SSL сертификат, а для того чтобы «Telegram Bot API» смог прислать обновление, необходим «белый» статичный IP адрес, на котором должен быть опубликован HTTP сервис 1С. В текущий момент, адрес http://127.0.0.1/TelegramBot1C/hs/updates/ виден только внутри вашего компьютера, это так называемый localhost.
Для получения обновлений, воспользуемся сервисом «ngrok» — который создает туннели на локальный компьютер, к нашему localhost. Скачиваем и распаковываем «ngrok», запускаем «Командную строку» в папке где расположен исполняемый файл «ngrok.exe» и выполняем команду:

ngrok.exe http 80

В результате, внутренние локальные сервисы, станут доступны для внешнего взаимодействия:

Postman test ngrok

Postman test ngrok

5.7 Устанавливаем webhooks, для этого необходимо вызвать метод API «setWebhook» с заполненным параметром «url», в котором укажем полученный из «ngrok» адрес HTTPS туннеля, плюс путь публикации.

Для эксперимента, попробуем это сделать через «Postman»:

  1. В скрытой части адреса, указывается токен, пример: https://api.telegram.org/bot{Токен}/setWebhook
  2. Параметр «url» должен содержать адрес, полученный из «ngrok» и путь публикации, пример:
    https:// 8f7a4346266b.ngrok.io/TelegramBot1C/hs/updates
Postman setWebhook

Postman setWebhook

Для удаление webhook и переход на метод «getUpdates», достаточно вызвать метод «deleteWebhook»:

https://api.telegram.org/bot{Токен}/deleteWebhook

Просмотр получаемых «ngrok» данных осуществляется локально, по адресу: http://localhost:4040

Теперь схема работы выглядит следующий образом:

Telegram webhooks

Telegram webhooks


После успешной установки webhook, в приложении «Телеграм» отправляем сообщение и тут же получаем его обратно.

Используемые источники информации:

Дополнительная информация:

  • 1С:Предприятие 8.3 (8.3.18.1334).
  • PostgreSQL 12.5-6.1C(x64).
  • Apache 2.4.43 (Open Server 5.3.8).

Метки:

3 комментария для “Телеграм бот 1С”

  1. Уведомление: Телеграм бот Node.js Webhook - Благин Константин

  2. Константин, сориентируйте, пожалуйста, а Вы занимаетесь разработкой ботов под заказ? (не нашел на сайте радела с контактными данными пишу сюда)

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *