Дисклеймер:
Это "хелловорлд", написанный в познавательных целях. Это не инструкция, не стандарты. Просто "интересно посмотреть" и ничего более.
Появилась потребность в боте для telegram. Потребность, скорее, в рамках НИОКР и тратить много времени на разработку не хотелось, но хотелось, чтобы все работало.
Сразу же вспомнилась библиотека "Открытый пакет интеграций" и было решено ей воспользоваться: раздел telegram, за одно изучить, наконец.
Хочу выразить авторам большую благодарность - библиотека замечательная.
Если вы с ней не знакомы, то рекомендую это сделать и обязательно поставить звезду на GitHub.
ОПИ скачал в виде расширения, скопировал модули для работы с мессенджером в пустую конфигурацию для разработки и начал изучать методы библиотеки через внешнюю обработку - так было проще.
На этапе готовности прототипа, то есть, благодаря ОПИ - через 10 минут, задумался о вебхуке.
Полет фантазии был примерно такой:
1) получать белый ip на собственную машину и публиковать сервис не хочется, так как потребуется сильно задуматься о безопасности;
1.1) можно было бы воспользоваться ngrok, но идею оставил "на потом" по причине сторонних сервисов "не у нас";
1.2) идеи о каком бы то ни было администрировании не внушали былого энтузиазма;
2) арендовать виртуальную машину, где публиковать сервис - дорого и тоже неудобно;
2.1) лицензии. Нужно помнить, что мы обязаны...
И вдруг вспомнил про 1С:Элемент.
Ни разу ничего на нем не делал. Отличный повод попробовать, ведь разработка на нем опубликована сразу, а значит можно разработать сервис, который назначить вебхуком для бота.
BotFather
Первое, что необходимо сделать - создать самого бота в telegram.
Как это сделать, описано в разделе telegram ОПИ.
Далее буду исходить из того, что токен бота получен.
Открытый пакет интеграций (ОПИ)
Поскольку в этой статье технология 1С:Предприятие.Элемент (далее 1С:Элемент) является объектом изучения, то все настройки бота и тестирование методов взаимодействия с ботом сначала будут проходить на платформе 1С.
Нехитрый интерфейс тестовой обработки выглядит так:
А код она имеет только в модуле формы:
Весь код обработчиков всех команд был скопирован из раздела telegram ОПИ.
Введем полученный от BotFather токен в поле "Токен" и выполним команду "Информация о боте":
Отправим нашему боту сообщение:
По команде обработки "Информация о новых событиях" получим json:
свойство объекта "id" - это id чата с пользователем:
"chat": {
"username": "smithse",
"type": "private",
"last_name": "Кузнецов (ЯЗ{}Ь)",
"first_name": "Сергей",
"id": 123456
},
Скопируем его в поле обработки "ID чата", в поле "Текст сообщения" напишем ответ и выполним команду "Отправить сообщение":
То есть, когда вебхука нет, то можно получать и отправлять данные запросом.
Но здесь про вебхук на 1С:Элемент, начнем.
Коллеги, я намеренно обхожу стороной вопрос регистрации и получения доступа в 1С:Элемент, потому что не знаю, как сейчас это происходит. Ранее я оставлял запрос на сайте и через сутки получил письмо с приглашением.
1С:Предприятие.Элемент
Жмем "Войти":
Вводим логин (потом пароль):
И попадаем сюда:
(у меня создан как раз тот бот, о котором рассказываю, но для статьи будет создан еще один)
По команде "Новое приложение" создаем новое приложение. Заполняем, нажимаем "Создать". У меня будет так:
Возвращаемся к предыдущему окну, а в списке появляется новое приложение(создается):
Создалось, нажимаем на "Разработать...":
Появится окно со ссылкой и предложением на нее нажать, нажимаем, снова вводим пароль и попадаем в такое пространство:
1) Правой кнопкой мыши (ПКМ) по корню -> Новый -> Подсистема -> вводим имя подсистемы и обязательно жмем в конце "ВВОД":
получаем:
2) ПКМ по созданной подсистеме -> Новый -> Элемент проекта:
Выбираем "Http сервис":
то же самое - вводим имя и жмем "ВВОД":
справа красным будет сообщение "Не задан корневой url" - задаем.
3) ПКМ по Шаблоны -> Новый -> Шаблон URL -> задаем имя и справа снизу задаем URL шаблона:
4) ПКМ по добавленному шаблону -> и снова это вот все....
задаем имя, жмем ввод. Тут надо оговориться, что имя должно быть не любым, а "GET", "POST", "PUT" и так далее (посмотреть какие есть методы http тут или тут).
Telegram шлет POST запросы, так что создаем метод шаблона "POST" и в правом нижнем углу задаем обработчик нажимая на лупу. Оказываемся в модуле сервиса, где наконец можно писать код.
ПКМ по корню -> Опубликовать проект:
Соберем адрес только что созданного сервиса
ПКМ по корню -> Открыть приложение:
Копируем ссылку открывшегося приложения, у меня оно выглядит так:
к ссылке добавил следущее:
https://testbot.1cmycloud.com/applications/testBot + /api + /testBot + /messages
/testBot - корневой URL HTTP Сервиса
/messages - URL шаблона
/api - доступ к api. Пусть будет "субдеррикторией"
в итоге получим адрес нашего сервиса, который уже опубликован:
https://testbot.1cmycloud.com/applications/testBot/api/testBot/messages
Выполнив к нему запрос в Postman получим 403:
В дереве левой кнопкой мыши выделим метод шаблона POST, справа на панели свойств найдем Разрешения, установим РазрешеноВсем:
ПКМ по корню -> Опубликовать проект и снова выполним запрос из Postman - ответ 200:
Установим сервис вебхуком для бота и запустим режим отладки в 1С:Элементе
В обработке в поле "Адрес webhook" вставляем адрес нашего сервиса, в поле "Токен" - токен бота и нажимаем "Установить webhook". Получаем ответ:
Теперь мы не сможем больше запросом получать данные от бота. Если нажать на "Информация о новых событиях", то:
На стороне 1С:Элемента: ПКМ по корню -> Начать отладку приложения:
Откроется новое окно в браузере, а среда разработки примет такой вид (сразу поставлю точку останова на строке 3):
Теперь напишем боту что-нибудь еще:
И ловим выполнение кода на точке останова. Слева можно посмотреть локальные переменные:
Начнем писать код
Для начала пусть бот отправляет нам то, что мы ему напишем.
По примерам из синтакс-помощника состряпал следующее:
метод ОбработкаЗапроса(Запрос: HttpСервисЗапрос)
знч ТелоЗапроса = Запрос.Тело.ПрочитатьКакСтроку()
знч ЧтениеJson = new ЧтениеJson(ТелоЗапроса)
знч ТелоКакСоответствие = ЧтениеJson.ПрочитатьСодержимоеКакСоответствие()
знч Message = ТелоКакСоответствие.Получить("message") как Соответствие<Строка, Объект?>
знч Chat = Message.Получить("chat") как Соответствие<Строка, Объект?>
знч ТекстСообщения = Message.Получить("text") как Строка
знч idЧата = Chat.Получить("id") как Число
знч ИмяПользователя = Chat.Получить("first_name") как Строка
пер ОтветСтрокой = Проект.ОтправитьТекстовоеСообщение("ТОКЕН_1234567890:dfghdsjkghfjkdshgjkfhglfhjgkf",
idЧата,
ИмяПользователя + ", вы написали: " + ТекстСообщения)
;
Строка с методом "Проект.ОтправитьТекстовоеСообщение()" интересна тем, что "Проект" здесь выступает "общим модулем", который я создал тем же способом: ПКМ по "Описание проекта"-> Новый -> Модуль, а сам код отправки сообщения подсмотрел в ОПИ и упростил его до элементарного. После "перевода на 1С:Элемент" получилось:
@Глобально
метод ОтправитьТекстовоеСообщение(Токен: Строка, IDЧата: Число, Текст: Строка): Строка
пер URL = "/bot" + Токен + "/sendMessage"
пер МойКлиент = КлиентHttp.СБазовымUrl("https://api.telegram.org")
пер Запрос = МойКлиент.ЗапросGet(URL + "?parse_mode=Markdown&text=" + Текст + "&chat_id=" + IDЧата)
Запрос.ДобавитьЗаголовок("Accept-Encoding", "gzip")
Запрос.ДобавитьЗаголовок("Accept", "*/*")
Запрос.ДобавитьЗаголовок("Connection", "keep-alive")
Запрос.ДобавитьЗаголовок("Accept-Charset", "utf-8")
пер Ответ = Запрос.Выполнить()
возврат Ответ.Тело.ПрочитатьКакСтроку()
;
Опубликовал проект и проверил бота:
Сейчас токен "зашит" в коде, что нехорошо. По наитию я сделал:
и попытался найти регистр сведений, он есть:
Что ж, создаем, называем "Настройки", видим знакомое:
Добавляю ресурс "Токен":
Запускаю отладку и приложение просит авторизацию. После ввода логина и пароля вот что можно увидеть:
Создадим запись с токеном:
А теперь получить его как-то надо.
На этом шаге автор сильно "затупил" и писал запрос руками, параллельно пытаясь найти ответ на "как это сделать", хороший опыт :) но все проще... Об этом ниже, когда справочник буду создавать.
Вернемся к регистру. К его свойствам. Надо разрешить получать из него данные, сделать его глобальным, а еще запросы в 1С:Элемент - это не то, как конструктором в конфигураторе...:
В итоге код модуля "Проект":
@Глобально
метод ОтправитьТекстовоеСообщение(Токен: Строка, IDЧата: Число, Текст: Строка): Строка
пер URL = "/bot" + Токен + "/sendMessage"
пер МойКлиент = КлиентHttp.СБазовымUrl("https://api.telegram.org")
пер Запрос = МойКлиент.ЗапросGet(URL + "?parse_mode=Markdown&text=" + Текст + "&chat_id=" + IDЧата)
Запрос.ДобавитьЗаголовок("Accept-Encoding", "gzip")
Запрос.ДобавитьЗаголовок("Accept", "*/*")
Запрос.ДобавитьЗаголовок("Connection", "keep-alive")
Запрос.ДобавитьЗаголовок("Accept-Charset", "utf-8")
пер Ответ = Запрос.Выполнить()
возврат Ответ.Тело.ПрочитатьКакСтроку()
;
@Глобально
метод ТокенБота(): Строка
исп Результат = Запрос{
Выбрать
Т.Токен как Токен
Из
sse::testBot::Основное::Настройки как Т
}.Выполнить()
знч Токен = Результат.ПервыйИлиУмолчание().Токен
Результат.Закрыть()
возврат Токен
;
код обработчика метода POST:
метод ОбработкаЗапроса(Запрос: HttpСервисЗапрос)
знч ТелоЗапроса = Запрос.Тело.ПрочитатьКакСтроку()
знч ЧтениеJson = new ЧтениеJson(ТелоЗапроса)
знч ТелоКакСоответствие = ЧтениеJson.ПрочитатьСодержимоеКакСоответствие()
знч Message = ТелоКакСоответствие.Получить("message") как Соответствие<Строка, Объект?>
знч Chat = Message.Получить("chat") как Соответствие<Строка, Объект?>
знч ТекстСообщения = Message.Получить("text") как Строка
знч idЧата = Chat.Получить("id") как Число
знч ИмяПользователя = Chat.Получить("first_name") как Строка
пер Токен = Проект.ТокенБота()
пер ОтветСтрокой = Проект.ОтправитьТекстовоеСообщение(Токен,
idЧата,
ИмяПользователя + ", вы написали: " + ТекстСообщения)
;
На этом повествование можно было бы закончить. За скобками остались вопросы доступов и видимости. Так же вопрос тарификации пока не особо понятен.
Есть статься-обзор на ИС где есть описание тарифов. Но так же сказано, что в сентябре все может стать иначе.
Однако, будущее наступило :)
Поэтому напоследок предлагаю добавить запись справочника "Пользователи telegram" и хотя бы одну команду отправки сообщения в чат пользователю.
Записываем пользователей тех, кто обращался к боту, для последующей рассылки, например.
Создадим справочник:
Свойство "Область видимости" установлю в "Глобально" и "Разрешения" в "РазрешеноВсем":
Добавлю реквизиты: "Имя", "Фамилия", "Код". А стандартный "Наименование" буду формировать при записи как "Имя" и "Фамилия:
1) Код:
Устанавливаю ему тип "Число" и длину в 20.
2) Имя:
3) Фамилия:
Объявлю глобальный метод "ЗаписатьПользователя" в "общем модуле" "Проект", который ничего не будет возвращать(если так можно сказать).
Сначала буду искать пользователя в базе, чтобы не создавать дубли.
Если настройки видимости сделали верно, то среда сама подскажет запрос:
выбрав "... ИЗ Пользователи" получим:
и, судя по всему, это другой справочник "Пользователи". Не тот, что я создал, потому что я не создавал этих реквизитов... Переименовав справочник в "ПользователиTelegram" и воспользовавшись контекстной подсказкой, запрос получился такой:
Забавно :) Детали пока опустим.
При написании "Пользов..." среда показывает стандартный справочник "Пользователи" (видимо стандартный) и искомый. А так же предлагает "НатйтиПоКоду()", т.е. можно было и без запроса:
///
В 1С:Элемент создать элемент справочника можно с помощью конструкторов. Вот документация.
Итак, метод получился такой:
@Глобально
метод ЗаписатьПользователя(Имя: Строка, Фамилия: Строка, idПользователя: Число)
исп Результат = Запрос{
ВЫБРАТЬ
П.Код
ИЗ
ПользователиTelegram КАК П
ГДЕ
П.Код == %idПользователя
}.Выполнить()
знч СтрокаРезультатаЗапроса = Результат.ПервыйИлиУмолчание()
Результат.Закрыть()
если СтрокаРезультатаЗапроса != Неопределено
возврат
иначе
знч Ид = новый Ууид()
пер ПользовательТГ = новый ПользователиTelegram.Объект(Ид = Ид,
Наименование = Имя + " " + Фамилия,
Код = idПользователя,
Имя = Имя,
Фамилия = Фамилия)
ПользовательТГ.Записать()
;
;
Отправив сообщение боту, проверим, что запись работает:
Теперь создадим команду для рассылки.
1) Создаем форму списка:
2) Добавляем команду:
меняем Представление на "Поприветствовать" и создаем обработчик команды:
У меня будет такой код:
метод Обработчик(Команда: ОбычнаяКоманда)
ОтправитьПриветствие()
;
@НаСервере
@ДоступноСКлиента
статический метод ОтправитьПриветствие()
исп Результат = Запрос{
ВЫБРАТЬ
ПользователиTelegram.Код КАК Код,
ПользователиTelegram.Имя КАК Имя,
ПользователиTelegram.Фамилия КАК Фамилия
ИЗ
sse::testBot::Основное::ПользователиTelegram КАК ПользователиTelegram
}.Выполнить()
пер Массив = Результат.ВМассив()
знч Токен = Проект.ТокенБота()
для Элемент из Массив
Проект.ОтправитьТекстовоеСообщение(Токен,
Элемент.Код,
Элемент.Имя + " " + Элемент.Фамилия + ", приветсвую Вас.")
;
;
Публикуем приложение, запускаем и вот:
"Поприветствуем":
P.S.
Если вспомнить, с чего все началось, то мне хотелось быстро и без особых усилий состряпать сервис для бота. Получилось совсем не быстро - разбираться в новом так же интересно, как утомительно и долго :)
Но, надеюсь, описание моих изысканий поможет кому-то потратить меньше времени на первое знакомство.
Документация по 1С:Элемент
Синтакс-помощник 1С:Элемент