Ну вот, когда где-нибудь едешь, то в голове столько мыслей, что написать, а когда садишься писать, где вы, все мысли?!
Вдох
Выдох
Поехали (с)
Давно хотел написать что нибудь интересно, но все интересно, что мне поддавалось, было уже давно написано. Про связку телеграм + 1с тоже уже все давно написано, причем раз в несколько недель выходит новая публикация, но мне эта разработка так понравилась, что, пожалуй, напишу еще одну. Но хватит лирики.
P.S. Поскольку вышел там какой то закон, все картинки рисовал сам.
Сказ о том, как телеграм путь к 1С искал.
Однажды я подумал... и понеслось.
...
А потом думаю - "оповещать покупателей в телеграме, из 1с, о статусах заказов было бы прикольно". Но на деле оказалось все куда интересней. Получение данных из 1с для сотрудников! Эта идея показалась мне очень интересной. Есть один маааааленький минус... телеграм запрещен в РФ. Вайбер? Да, как вариант. Может быть когда нибудь)
"Я смутно помню тот момент, это был не легкий выбор. Все как в туманном сне, я плохо контролирую свое тело, вокруг паника, нужно встать и идти. В голове была одна мысль - нельзя останавливаться. Нужно было выбрать направление. Передо мной появился он... протянув руку и раскрыв кулак, он сказала:
- Выберешь голубую, и история закончится прямо здесь.
На ладони лежали две таблетки - голубая и красная. Это была точка не возврата " (с) bot_telegram
Конечно, для себя я сразу определил, что буду пользоваться вебхуком, когда узнал, что такое возможно) Итак, что нам понадобится для вебхука:
2. Статический(белый ip) на сервере.
3. Виртуальный сервер - если у вас заблокирован телеграм.
5. Чашечка чая.
6. Две чашечки кофе.
Теперь подробней... По факту я конечно начал с шестого пункта, но здесь пойдем по порядку.
Качаем apach 2.4 и vc_redist с этого сайта. Не перепутайте разрядности(версия apach на момент написания статьи).
- Разархивируем папку apach24 в корень системного диска. По умолчанию конфигурационный файл настроен на диск C:\, так что если у вас нет такого диска, или у системного диска другая буква, необходимо будет заменить это в файле.
Открываем файл C:\Apache24\conf\httpd.conf блокнотом. Находим в нем строку Listen 80, и ниже нее добавляем допустим Listen 8018. Именно на порт 8018 будет идти запрос. Можете установить какой предпочитаете.
- Установим vc_redist
- Для установки службы, откроем командную строку от имени администратора. Перейдем в каталог bin командой:
cd C:\Apache24\bin
и следующей командой установим службу:
httpd.exe -k install
Кажись служба установлена, ну зайдем в список служб проверим на всякий случай) Если не получилось, то выполним пункт назначения 5 или 6, на ваше усмотрение, и попробуем еще раз.
Здесь все еще проще. Чтобы телеграмму было куда отправить телеграм мог отправить запрос, нужен статический ip все потому что он белый
Если он уже есть - хорошо, если нет - нужно как то чтобы был. Далее пробрасываем порт на наш сервер apach на порт 8018, чтобы открыв наш адрес через белый ip, мы увидели что It works!
PS Если не получиться, но вы уверены, что все сделали правильно, то проверьте не блокирует ли ваш запрос антивирус или брандмауэр.
3. VPS или Виртуальный сервер.
Здесь все достаточно просто - поскольку обращения телеграма к нашему серверу будет блокировать провайдер, то нам нужен посредник. Есть замечательное видео. по настройки этого посредника, так же у автора на сайте есть статья по этому видео, откуда можно скопировать все команды. Я арендовал VPS в другом месте, так как мне показалось, что рекомендуемый VPS дороговат. Я нашел за 3 евро в месяц. Так же выбрал ОС debian и все настраивал полностью по видео. Все получилось!
Единственное, что у меня не получилось - это установить вебхук))))))))))) Так как в видео этого нет, а я не совсем бумбум) Но я не сдрейфил, связался с автором и попросил помочь, за что ему большое спасибо)
Если у вас на VPS не установлен curl(а если вы только развернули сервер, то скорей всего так и будет), нужно выполнить команду:
apt install curl
И установка вебхука:
curl -F "url=https://IP_VPS:Port" -F "certificate=@/etc/ssl/certs/nginx-selfsigned.crt" "https://api.telegram.org/bot{ТокенБота}/setWebhook"
IP_VPS - ip ващего виртуального сервера.
Port - порт, который установили в конфигурационном файле telegram.conf.
nginx-selfsigned.crt - сгенерированный сертификат.
Не забудьте указать порт, при установки вебхука, а то я порядком так тупил в этот момент)))
Если кому то интересно, могу продлить аренду еще на пару месяцев, и попробовать пробросить порт на ваш ip, исключительно для тестов. Но не уверен, что у меня это получится) Если что, пишите в лс, можем попробовать))
Ну да ладно, будем откровенны - прямые руки нужны на каждом пункте этой эпопеи. И даже щелчок Таноса здесь не поможет.
Предположим что бота вы уже создали. Это делается довольно просто и есть куча мануалов, а еще есть хорошая статья.
Теперь, в 1с. Запускаем конфигуратор от имени администратора.
Нам нужно создать http сервис, который будет принимать запросы телеграма. Корневой каталог укажем bot_telegram
На закладке Шаблоны URL добавим шаблон, предположим ОбработатьВходящиеСообщение, с двумя методами GET и POST.
В обработчике GET напишем следующий код:
Ответ = Новый HTTPСервисОтвет(200);
Ответ.Заголовки.Вставить("Content-Type","text/html; charset=utf-8");
Попытка
Ответ.УстановитьТелоИзСтроки("Связь есть, продолжаем");
Исключение
Ответ.КодСостояния = 400;
Ответ.УстановитьТелоИзСтроки(ОписаниеОшибки());
КонецПопытки;
Возврат Ответ;
Обновим конфигурацию базы данных. Теперь необходимо опубликовать наш сервис. Переходим на закладку Администрирование - Публикация на веб сервере... Заполняем необходимые данные
Сразу же включим отладку веб сервера. Перейдем на закладку Прочее
Жмем Опубликовать, и перезагружаем службу apach(в моем случае службу приходилось перезагружать каждый раз, когда вносили изменения в конфигурацию). 1с предложит самой перезапустить службу, но не верьте ей, сделайте это сами. Переходим по нашему VPS IP Если все сделано правильно, то должны увидеть наше сообщение:
Теперь напишем команду приветствия нового пользователя. Переходим в обработчик POST запроса. Сразу скажу что при принятии сообщения, нужно различать текстовую команду, от клавиатурной команды сообщения (inline клавиатура). Я делаю это проверкой свойства структуры callback_query. Функцией РазобратьПакет json разберем пакет на структуры.
Добавил справочник Боты, куда заношу токен, и сервер. Потом если в параметрах процедуры отправки сообщения эти параметры пустые, тогда получаю бота по умолчанию, у которого активен соответствующий реквизит. Вы же можете прописать настройки бота в коде для тестирования, и передавать в параметрах. Во вложении будет конфигурация со справочником.
Функция ОбработатьВходящиеСообщенияPOST(Запрос)
ПакетСообщения = Разобратьпакет(Запрос.ПолучитьТелоКакСтроку());
ПакетСообщения = ?(ПакетСообщения.Свойство("callback_query"), ПакетСообщения.callback_query, ПакетСообщения); // Если ответ с inline клавиатуры, тогда берет подпакет
Подписчик = ПакетСообщения.message.chat.id;
Если ПакетСообщения.message.Свойство("text") Тогда // Если нет текста то ничего не делаем
Если ПакетСообщения.Свойство("data") Тогда // Если есть свойство data значит ответ пришел с inline клавиатуры
//ОбработатьКомандуinline(ПакетСообщения, Подписчик);
Иначе
Если ПакетСообщения.message.text = "/start" Тогда // Определяем какую команду отправил пользователь. первая команда всегда /start, так как пользователь только подписался
ТекстСообщения = "Добро пожаловать в чат =/";
Успешно = Неопределено;
Кнопки = СформироватьМассивКомандКлавиатуры(); // Сформируем команды начальной клавиатуры
ОтправитьСообщениеПользователю(Подписчик,,,ТекстСообщения,Успешно,,Кнопки);
ИначеЕсли ПакетСообщения.message.text = "ЕстьCHO??" Тогда
//ПоказатьПодписчикуinlineКлавиатуру(Подписчик);
Иначе
//ОтветКомандаНеНайдена(Подписчик, Строка(ПакетСообщения.message.text));
КонецЕсли;
КонецЕсли;
КонецЕсли;
Ответ = Новый HTTPСервисОтвет(200);
Возврат Ответ;
КонецФункции
Функция Разобратьпакет(СтрокаJSON)
Чтение = Новый ЧтениеJSON;
Чтение.УстановитьСтроку(СтрокаJSON);
Данные = ПрочитатьJSON(Чтение, Ложь);
Чтение.Закрыть();
Возврат Данные;
КонецФункции
Функция СформироватьМассивКомандКлавиатуры()
МассивКнопок = Новый Массив;
МассивКнопок.Добавить("ЕстьCHO??");
Строки = Новый Массив;
Строки.Добавить(МассивКнопок);
КнопкиJs = ЗаписатьJS(Новый Структура("keyboard", Строки));
Возврат КнопкиJs;
КонецФункции
Функция ЗаписатьJS(СтруктураJS)
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON,СтруктураJS);
Возврат ЗаписьJSON.Закрыть();
КонецФункции
Процедура ОтправитьСообщениеПользователю(id_Чата, Токен = Неопределено, Сервер = Неопределено, Сообщение, Успешно = Неопределено, ПутьФайла = Неопределено, Кнопки = Неопределено) Экспорт
Если Токен = Неопределено Тогда
ПолучитьБотаПоУмолчанию(Токен, Сервер);
КонецЕсли;
Если ЗначениеЗаполнено(ПутьФайла) Тогда
//ОтправитьФайл(id_Чата, Токен, Сервер, ПутьФайла);
Иначе
ИнтернетПрокси = Новый ИнтернетПрокси(Ложь);
ИнтернетПрокси.Установить("https","195.171.27.244",3128, "", "", Ложь);
Ресурс = "bot" + Токен + "/sendMessage?chat_id=" + СтрЗаменить(Формат(id_Чата, "ЧДЦ=; ЧС=; ЧРГ=."), ".", "") + "&text=" + Сообщение+ ?(Кнопки<>Неопределено, "&reply_markup="+Кнопки ,"");
ЗС = Новый ЗащищенноеСоединениеOpenSSL();
Соединение = Новый HTTPСоединение(Сервер,443,,,ИнтернетПрокси,,ЗС);
Запрос = Новый HTTPЗапрос(Ресурс);
Попытка
Ответ = Соединение.Получить(Запрос);
Исключение
Успешно = ОписаниеОшибки();
Возврат;
КонецПопытки;
Успешно = Ответ.КодСостояния = 200;
КонецЕсли;
КонецПроцедуры
Процедура ПолучитьБотаПоУмолчанию(Токен, Сервер)
Запрос = Новый Запрос("ВЫБРАТЬ
| Боты.Токен КАК Токен,
| Боты.Сервер КАК Сервер
|ИЗ
| Справочник.Боты КАК Боты
|ГДЕ
| Боты.Основной");
Выборка = Запрос.Выполнить().Выбрать();
Выборка.Следующий();
Токен = Выборка.Токен;
Сервер = Выборка.Сервер;
КонецПроцедуры
Совсем забыл упомянуть, для стран где телеграм заблокирован, нужно использовать прокси. Я нашел один, который стабильно работал на момент написания статьи, и прямо в коде его прописал. Вот очень хорошая статья по этому поводу.
Обновляем конфигурацию, перезапускаем службу apach.
Так же при отправке ответа, формируется статическая клавиатура с командами, ну чтобы не писать их каждый раз. В результате должны получить что то типа такого:
Бот у нас будет не особо доброжелательным. Ну еще бы...
Далее на нашу единственную команду клавиатуры сформируем inline клавиатуру. Смысл такой клавиатуры, что она привязана к конкретному сообщению. В текущей разработке у них нет особого смысла, но да ладно мы тут вроде как вебхук поднимаем, а не переписываемся
Процедура ПоказатьПодписчикуinlineКлавиатуру(Подписчик)
ТекстСообщения = "А чо нада???";
Успешно = Неопределено;
Кнопки = СформироватьМассивКомандКлавиатуры_inline();
ОтправитьСообщениеПользователю(Подписчик,,,ТекстСообщения,Успешно,,Кнопки);
КонецПроцедуры
Функция СформироватьМассивКомандКлавиатуры_inline()
МассивКнопок = Новый Массив;
МассивКнопок.Добавить("Картинка");
МассивКнопок.Добавить("Аудио");
МассивКнопок.Добавить("Документ");
Кнопки = Новый Массив;
Для каждого Кнопка ИЗ МассивКнопок Цикл
Кнопки.Добавить(Новый Структура("text, callback_data", кнопка, СтрЗаменить(Кнопка, " ", "")));
КонецЦикла;
Строки = Новый Массив;
Строки.Добавить(Кнопки);
КнопкиJs = ЗаписатьJS(Новый Структура("inline_keyboard", Строки));
Возврат КнопкиJs;
КонецФункции
Сформируется три кнопки:
Ну и раз уж на то пошло, тогда давайте напишем обработчик этих команд. Будем указывать путь в файлу, в зависимости от того, что выбрал пользователь.
Процедура ОбработатьКомандуinline(ПакетСообщения, Подписчик)
Отказ = ложь;
Если ПакетСообщения.data = "Картинка" Тогда
ПутьКФайлу = "C:\fil_bot\картинка.jpg";
ТекстСообщения = "Ваша картинка, сЭр";
ИначеЕсли ПакетСообщения.data = "Аудио" Тогда
ПутьКФайлу = "C:\fil_bot\audio.mp3";
ТекстСообщения = "Ваше аудио, сЭр";
ИначеЕсли ПакетСообщения.data = "Документ" Тогда
ПутьКФайлу = "C:\fil_bot\document.xlsx";
ТекстСообщения = "Ваш документ, сЭр";
Иначе
Отказ = истина;
КонецЕсли;
Если Не Отказ Тогда
ОтправитьСообщениеПользователю(Подписчик,,,ТекстСообщения,, ПутьКФайлу);
КонецЕсли;
КонецПроцедуры
И процедура отправки файла, которую я честно стырил отсюда и немного переделал(надеюсь не будут судебные иски и вот это вот все)
Процедура ОтправитьФайл(id_Чата, Токен, Сервер, ПутьКФайлу)
Файл = Новый Файл(ПутьКФайлу);
ИмяОтправляемогоФайла = Файл.Имя;
СтрокаСоединения = "/bot" + Токен + "/sendDocument";
Boundary = "----"+Строка(Новый УникальныйИдентификатор());
//Определяем массив для процедуры ОбъединитьФайлы
МассивФайловДляОбъединения = Новый Массив;
//Формируем начальный фрагмент файла POST-запроса
ИмяФайлаОтправкиНачало = ПолучитьИмяВременногоФайла("txt");
ФайлОтправкиНачало = Новый ЗаписьТекста(ИмяФайлаОтправкиНачало, КодировкаТекста.UTF8);
//Формируем конечный фрагмент файла POST-запроса
ИмяФайлаОтправкиКонец = ПолучитьИмяВременногоФайла("txt");
ФайлаОтправкиКонец = Новый ЗаписьТекста(ИмяФайлаОтправкиКонец, КодировкаТекста.UTF8);
ТекстДляОтправки = "";
ТекстДляОтправки = ТекстДляОтправки + "--"+Boundary + Символы.ПС;
ТекстДляОтправки = ТекстДляОтправки + "Content-Disposition: form-data; name=""chat_id""" + Символы.ПС + Символы.ПС;
ТекстДляОтправки = ТекстДляОтправки + id_Чата + Символы.ПС;
ТекстДляОтправки = ТекстДляОтправки + "--"+Boundary + Символы.ПС;
ТекстДляОтправки = ТекстДляОтправки + "Content-Disposition: form-data; name=""document""; filename="""+ИмяОтправляемогоФайла+"""" + Символы.ПС;
ФайлОтправкиНачало.ЗаписатьСтроку(ТекстДляОтправки );
ФайлОтправкиНачало.Закрыть();
МассивФайловДляОбъединения.Добавить(ИмяФайлаОтправкиНачало);
МассивФайловДляОбъединения.Добавить(СокрЛП(ПутьКФайлу));
ТекстДляОтправки = "" + Символы.ПС;
ТекстДляОтправки = ТекстДляОтправки + "--"+Boundary+"--";
ФайлаОтправкиКонец.ЗаписатьСтроку(ТекстДляОтправки);
ФайлаОтправкиКонец.Закрыть();
МассивФайловДляОбъединения.Добавить(ИмяФайлаОтправкиКонец);
ИмяФайлаОтправки = ПолучитьИмяВременногоФайла("txt");
ОбъединитьФайлы(МассивФайловДляОбъединения, ИмяФайлаОтправки);
HTTPЗапрос = Новый HTTPЗапрос;
Заголовки = Новый Соответствие;
HTTPЗапрос.Заголовки.Вставить("Connection", "keep-alive");
HTTPЗапрос.Заголовки.Вставить("Content-Type", "multipart/form-data; boundary="+Boundary);
HTTPЗапрос.УстановитьИмяФайлаТела(ИмяФайлаОтправки);
HTTPЗапрос.АдресРесурса = СтрокаСоединения;
ЗС = Новый ЗащищенноеСоединениеOpenSSL(Новый СертификатКлиентаWindows, Новый СертификатыУдостоверяющихЦентровWindows);
ИнтернетПрокси = Новый ИнтернетПрокси(Ложь);
ИнтернетПрокси.Установить("https","195.171.27.244",3128, "", "", Ложь);
HTTPСоединение = Новый HTTPСоединение(Сервер,,,,ИнтернетПрокси,, ЗС);
Попытка
ОтветHTTP = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
Исключение
КонецПопытки;
КонецПроцедуры
И получаем то, что выбрали
Так же сделал обработку, для отправки сообщений в чат пользователя от имени бота
Как то так...
Получился какой то набор ссылок) Nо возможно этого я и хотел, так как уже при повторном поднятии вебхука, у меня не все получалось)
Мне понравилась это направление, в дальнейшем хочу реализовать отправку отчетов и получение данных из базы 1с. Единственное - не знаю в каком направлении двигаться дальше - какие отчеты отправлять, какие данные получать, для каких конфигураций. На моем предприятии данный функционал не актуален, поэтому если кому то понадобится, или у кого то есть идеи, пишите в лс или в комментарии, буду благодарен.
Конфигурацию прикреплю во вложение.
Разрабатывалось на платформе 8.3.15.1656, но на более низких версиях 8.3 должно работать без проблем.
Используемые источники:
Телеграм + 1С + Вебхуки + Апач + Самоподписанный сертификат
HTTP Сервисы: Путь к своему сервису. Часть 1