Создаем бота Telegram



Легко и непринужденно создаем бота, который поможет получать данные из 1С, используя API Teletram

Используя побликации  //infostart.ru/public/387433/ и //infostart.ru/public/419846/ решил создать бота, используя API Telegram

Для разбора JSON использовалась обработка из публикации: //infostart.ru/public/119601/

Регистрируем бот и получаем ТОКЕН, тут: //infostart.ru/public/419846/  есть описание, как это сделать

В текущей версии реализована реакция на тестовые сообщения от пользователя;

«скажи» — бот в ответ посылает сообщение

«отчеты» — показывается клавиатура со списком доступных отчетов

«файлы» — показывается клавиатура со списком доступных файлов

Также бот получает присланные фотографии.

Основные процедуры бота:

&НаСервере
Процедура ПрочитатьСобщенияНаСервере() Экспорт
Сервер = "api.telegram.org";

СтрокаСоединения = "/bot"+Токен+"/getUpdates" + ?(ПоследнийИД = "", "", "?offset=" + ПоследнийИД);

HTTPЗапрос = Новый HTTPЗапрос();
Заголовки = Новый Соответствие;

HTTPЗапрос.Заголовки.Вставить("Connection", "keep-alive");
HTTPЗапрос.АдресРесурса = СтрокаСоединения;

ЗащищенноеСоединение = Новый ЗащищенноеСоединениеOpenSSL(Новый СертификатКлиентаWindows, Новый СертификатыУдостоверяющихЦентровWindows);
HTTPСоединение =  Новый HTTPСоединение(Сервер,,,,Новый ИнтернетПрокси,, ЗащищенноеСоединение);
Попытка
ОтветHTTP = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
Исключение
Сообщить(ОписаниеОшибки());
Возврат;
КонецПопытки;

ДанныеКакСтрока = ОтветHTTP.ПолучитьТелоКакСтроку();

обОбработка = РеквизитФормыВЗначение("Объект");

Рез = обОбработка.мПрочитатьJSON(ДанныеКакСтрока);
Если Рез["ok"] <> Истина Тогда
Возврат;
КонецЕсли;


Рез = СоответвиеВСтруктуру(Рез);

result = Рез.result;
МаксИД = 0;

Для Каждого mes из result Цикл

ПоследнийИД = Формат(mes.update_id + 1, "ЧГ=");

Попытка
message  = mes.message;
Исключение
Продолжить;
КонецПопытки;

Дата = '19700101' + message.date +7*60*60;

Попытка
from = message.from.username;
Исключение
from = message.from.first_name;
КонецПопытки;

chatИД = Формат(message.chat.id, "ЧГ=");

Если message.Свойство("text") Тогда
Ок= истина;
Если Найти(ВРег(message.text), "СКАЖИ")<>0 Тогда

HTTPЗапрос.АдресРесурса = "/bot"+Токен+"/sendMessage?text=Hello I am BOT&chat_id="+chatИД+"&reply_to_message_id=" + message.message_id;
HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);

ИначеЕсли  Врег(message.text) = "ОТЧЕТЫ" Тогда

HTTPЗапрос.АдресРесурса = "/bot"+Токен+"/sendMessage?text=Доступные отчеты: &chat_id="+chatИД+"&reply_to_message_id=" + message.message_id +
"&reply_markup="+КлаваОтчеты;
HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);

ИначеЕсли  Врег(message.text) = "ФАЙЛЫ" Тогда

HTTPЗапрос.АдресРесурса = "/bot"+Токен+"/sendMessage?text=Доступные файлы: &chat_id="+chatИД+"&reply_to_message_id=" + message.message_id +
"&reply_markup="+КлаваФайлы;
HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);

ИначеЕсли Лев(message.text, 7) = "Отчет: " Тогда

HTTPЗапрос.АдресРесурса = "/bot"+Токен+"/sendMessage?text=Ждите. Отчет формируется&chat_id="+chatИД+"&reply_to_message_id=" + message.message_id;
HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);

ТекстОтчета = ПолучитьТекстОтчета(message.text);

HTTPЗапрос.АдресРесурса = "/bot"+Токен+"/sendMessage?parse_mode=HTML&text="+Кодировать(ТекстОтчета)+"&chat_id="+chatИД+"&reply_to_message_id=" + message.message_id;
HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);

ИначеЕсли message.text = "Файл: PDF" Тогда

ОтправитьФайлPDF(chatИД, HTTPСоединение, HTTPЗапрос)

ИначеЕсли message.text = "Файл: XLS" Тогда

ОтправитьФайлXLS(chatИД, HTTPСоединение, HTTPЗапрос)

КонецЕсли;

ИначеЕсли message.Свойство("photo") Тогда
МассивФото = message.photo;
Для Каждого Фото из МассивФото Цикл
HTTPЗапрос.АдресРесурса = "/bot"+Токен+"/getFile?file_id=" + Фото.file_id;
Ответ = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
СтрокаОтвета = Ответ.ПолучитьТелоКакСтроку();
Путь = СтрЗаменить(Сред(СтрокаОтвета, Найти(СтрокаОтвета,"photo/") + 7) , """}}", "");
СтрФото = Фотографии.Добавить();
СтрФото.Имя = Путь;
СтрФото.Размер = "" + Фото.height +"x" + Фото.width;
КонецЦикла;

КонецЕсли;
КонецЦикла;
КонецПроцедуры


&НаСервере
Функция СоответвиеВСтруктуру(Рез)
мРез = Новый Структура;
Для Каждого Эл из Рез Цикл
Ключ = Эл.Ключ;
мЗнач = Эл.Значение;
Если ТипЗнч(мЗнач) =Тип("Соответствие") Тогда
мЗнач = СоответвиеВСтруктуру(мЗнач);
ИначеЕсли ТипЗнч(мЗнач) =Тип("Массив") Тогда
мЗнач = Новый Массив;
Для Каждого элМ из Эл.Значение Цикл
мЗнач.Добавить(СоответвиеВСтруктуру(элМ));
КонецЦикла;
КонецЕсли;
мРез.Вставить(Ключ, мЗнач);
КонецЦикла;
Возврат мРез;
КонецФункции

&НаСервере
функция Кодировать(Текст)
Возврат ?(Текст = "", "Пусто", КодироватьСтроку(Текст,  СпособКодированияСтроки.URLВКодировкеURL));
КонецФункции

&НаСервере
Процедура ОтправитьФайлPDF(chat_id, HTTPСоединение, HTTPЗапрос)

Таб = РеквизитФормыВЗначение("Объект").ПолучитьМакет("МакетОтчета");


ИмяФайла = ПолучитьИмяВременногоФайла("PDF");
Таб.АвтоМасштаб = Истина;
Таб.Записать(ИмяФайла, ТипФайлаТабличногоДокумента.PDF);


СтрокаСоединения = "/bot" + Токен + "/sendDocument";

Boundary = "----"+Строка(Новый УникальныйИдентификатор());

//Определяем массив для процедуры ОбъединитьФайлы
МассивФайловДляОбъединения = Новый Массив;

//Формируем начальный фрагмент файла POST-запроса
ИмяФайлаОтправкиНачало = ПолучитьИмяВременногоФайла("txt");
ФайлОтправкиНачало = Новый ЗаписьТекста(ИмяФайлаОтправкиНачало, КодировкаТекста.UTF8);

//Формируем конечный фрагмент файла POST-запроса
ИмяФайлаОтправкиКонец = ПолучитьИмяВременногоФайла("txt");
ФайлаОтправкиКонец = Новый ЗаписьТекста(ИмяФайлаОтправкиКонец, КодировкаТекста.UTF8);

ТекстДляОтправки = "";

ТекстДляОтправки = ТекстДляОтправки + "--"+Boundary + Символы.ПС;
ТекстДляОтправки = ТекстДляОтправки + "Content-Disposition: form-data; name=""chat_id""" + Символы.ПС + Символы.ПС;
ТекстДляОтправки = ТекстДляОтправки + chat_id + Символы.ПС;

ТекстДляОтправки = ТекстДляОтправки + "--"+Boundary + Символы.ПС;
ТекстДляОтправки = ТекстДляОтправки + "Content-Disposition: form-data; name=""document""; filename=""report.PDF""" + Символы.ПС;

ФайлОтправкиНачало.ЗаписатьСтроку(ТекстДляОтправки );
ФайлОтправкиНачало.Закрыть();

МассивФайловДляОбъединения.Добавить(ИмяФайлаОтправкиНачало);

МассивФайловДляОбъединения.Добавить(СокрЛП(ИмяФайла));

ТекстДляОтправки = "" + Символы.ПС;
ТекстДляОтправки = ТекстДляОтправки + "--"+Boundary+"--";
ФайлаОтправкиКонец.ЗаписатьСтроку(ТекстДляОтправки);
ФайлаОтправкиКонец.Закрыть();
МассивФайловДляОбъединения.Добавить(ИмяФайлаОтправкиКонец);

ИмяФайлаОтправки = ПолучитьИмяВременногоФайла("txt");
ОбъединитьФайлы(МассивФайловДляОбъединения, ИмяФайлаОтправки);

HTTPЗапрос.Заголовки.Вставить("Connection", "keep-alive");
HTTPЗапрос.Заголовки.Вставить("Content-Type", "multipart/form-data; boundary="+Boundary);

HTTPЗапрос.УстановитьИмяФайлаТела(ИмяФайлаОтправки);
HTTPЗапрос.АдресРесурса = СтрокаСоединения;

Попытка
ОтветHTTP = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
Исключение
Сообщить(ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры

Добавлено:

Что бы клавиатура появлялась прямо в чате необходимо подавать другой текст клавиатуры в обработке он получается так:

 Строки = Новый Массив;
Кнопки = Новый Массив;
Кнопки.Добавить(Новый структура("text, callback_data", "Файл: PDF", "1"));
Кнопки.Добавить(Новый структура("text, callback_data", "Файл: XLS", "2"));
Строки.Добавить(Кнопки);

КлавиатураВСообщении = JSON.мЗаписатьJSON(Новый Структура("inline_keyboard", Строки), Ложь);

если текстом:

КлавиатураВСообщении =
"{""inline_keyboard"":[[{""text"":""Файл: PDF"",""callback_data"":""1""},{""text"":""Файл: XLS"",""callback_data"":""2""}]]}"

обработка нажатия на эту кнопку отличается от обработки нажатия на простую клавиатуру, которая впринципе просто отсылает сообщение с нужным текстом:

блок «сallback_query» как раз говорит нам о нажатии на такую кнопку, в нем будет содержаться информации о коде нажатой кноки, в нашем примере это «1» или «2»

КодНажатойКнопки = mes.callback_query.data

далее обратабываем так как нам надо

30 Comments

  1. TreeDogNight

    Баян =(

    Reply ↓
  2. ifal

    Желтый у вас заголовок, где же тут процесс создания бота?

    Reply ↓
  3. FirePyres

    (2) ifal, Как понять что он «желтый»?

    Reply ↓
  4. ifal

    (3) В заголовке статьи написано: » создаем бота…» -, я ожидал увидеть описание процесса, а что видим внутри? ссылки на другие статьи, где описан процесс, а все что ваше личное упрятано в обработку. Поэтому я делаю такой вывод. Правильней звучало бы «Пример использования бота Telegram».

    Reply ↓
  5. FirePyres

    (4) Добавил тексты основных процедур

    Хотел описать, но код вроде настолько простой что вроде и объяснений не надо

    Reply ↓
  6. FirePyres

    Добавил пример inline клавиатуры

    Reply ↓
  7. METAL

    Добрый день!

    Скажите, а не приходилось ли Вам делать интеграцию с Facebook API https://developers.facebook.com/ ?

    Иными словами, можно ли то же самое прикрутить не к Телеграм, а к Фейсбуку?

    Просто в нашей компании никто Телеграм не использует, а интеграцию с каким-нибудь популярным мессенджером хотят, и несмотря на мои уговоры, даже пробовать телеграм не хотят, аргумент такой — вот есть Фейсбук, приложение у всех стоит, АПИ есть, пусть корпоративный бот работает на этой платформе…

    Но мне пока не удалось найти ни одного примера реализации бота.. Это потому что на порядок сложней, чем с телеграмом, или просто руки не дошли ни у кого?..

    Спасибо!

    Reply ↓
  8. FirePyres

    (7) Я бы попробовал, но не совсем понимаю как это должно работать

    Как вы это себе представляете?

    Reply ↓
  9. METAL

    (8) аналогично

    Пользователь подписывается на бота

    И дальше например пишет в чат ОТЧЕТЫ и может выбрать нужный отчёт за нужную дату (sales today — без кнопок для выбора тоже подойдёт, непринципиально)

    Либо происходит событие в 1С какое-то, и пользователю в мессенджер приходит уведомление

    Но насколько я вчёра смог разобраться — при разработке придётся использовать некую php-прослойку, так как для ФБ обязательно использовать webhook, как я понял.. По крайней мере, именно на этом этапе я споткнулся http://prntscr.com/d4yojf

    Мнение одного специалиста, который делал и для ФБ (на php), и для телеграм (он писал мне в личку, поэтому имя не публикую):

    На 1С не делал, делал на php. но там чуть чуть меньше возможностей, есть ограничения и т.д. В принципе можно на php запустить на сайте готовый какой нить движок работы с фейсбуком, и написать для него АПИ интерфейса для 1с. Это самое простое решение будет
    Reply ↓
  10. PhoenixAOD

    Интересное решение

    Reply ↓
  11. FirePyres

    Сегодня по радио услышал что Viber запустил для всех возможность создания публичного аккаунта

    «Представленные паблик аккаунты — это чат-каналы, которые можно использовать для отправки важной (рекламной) информации пользователям, проводить презентации, размещать новости, полезные ссылки, GIF-изображения, всячески общаться и получать обратную реакцию от участников канала. Также создателям каналов доступны CRM-инструменты для управления чатом.»

    Reply ↓
  12. rayastar

    Коллеги, приветствую! С ботом разобрался, все отлично, но есть одно НО. Почему то иногда мои команды не обрабатываются. Опишу проблему. Отправляю команду — и надеюсь увидеть ее в входящем файле json. Но там остается какой то перечень старых команд…в чем может быть проблема? Вот только что отправил боту команду — пришел ответ, все ок. отправляю следующую команду — и в ответ тишина…Мне не совсем понятна логика формирования json файла

    Reply ↓
  13. terrorion

    Добрый день, коллеги.

    Никак не могу разобраться, как удалить клавиатуру в чате («inline_keyboard»).

    Был бы признателен за наглядный пример.

    Reply ↓
  14. FirePyres

    День добрый.

    Вроде работает если передать пустую клавиатуру: «{«»keyboard»»:[],»»one_time_keyboard»»:true}»

    Reply ↓
  15. terrorion

    (15) Спасибо за ответ. Но, к сожалению, ничего не получилось. Насколько я понял, как то нужно использовать ReplyKeyboardRemove.

    Reply ↓
  16. PLAstic

    (13) Внимательно прочитайте описание https://core.telegram.org/bots/api#getupdates

    Наверняка, вы увидели, что параметры не обязательны и юзаете без них. Поэтому получаете первые 100 сообщений по сроку давности. Если вчитаться, то там написано, что этот оффсет — это не смещение, а номер первого непрочитанного сообщения. Т.е. последний полученный ид сообщения + 1.

    Получается, чтобы получать свежие сообщения, надо указывать параметр offset.

    Reply ↓
  17. PLAstic

    (14) Передайте пустую клавиатуру.

    Процедура ИзменитьКлавиатуруСообщения(Чат, Сообщение, Клавиатура = «»)
    
    ТекстЗапроса = СтрШаблон(«bot%1/editMessageReplyMarkup?chat_id=%2&message_id=%3», ТокенБота, Формат(Чат, «ЧГ=»), Формат(Сообщение, «ЧГ=»));
    Если НЕ ПустаяСтрока(Клавиатура) Тогда
    ТекстЗапроса = ТекстЗапроса + «&reply_markup=» + Клавиатура;
    КонецЕсли;
    
    Запрос = Новый HTTPЗапрос(ТекстЗапроса);
    Соединение = Новый HTTPСоединение(«api.telegram.org», 443,,,,, Новый ЗащищенноеСоединениеOpenSSL());
    HTTPОтвет = Соединение.Получить(Запрос);
    ЧтениеJSON = Новый ЧтениеJSON;
    ЧтениеJSON.УстановитьСтроку(HTTPОтвет.ПолучитьТелоКакСтроку());
    Результат = ПрочитатьJSON(ЧтениеJSON);
    ЧтениеJSON.Закрыть();
    
    Если НЕ Результат.ok Тогда
    ТекстОписания = СтрШаблон(«%1
    |Чат: %2
    |Сообщение: %3», Результат.description, Чат, Сообщение);
    ЗаписьЖурналаРегистрации(«Бот», УровеньЖурналаРегистрации.Ошибка,,, ТекстОписания);
    КонецЕсли;
    
    КонецПроцедуры

    Показать

    Reply ↓
  18. terrorion

    (18) Спасибо, получилось!

    Reply ↓
  19. Euroset1

    Автор, как ты решил проблему с BOM кодом? Или веб сервис телеграма самостоятельно его вырезает перед первым boundary?

    Reply ↓
  20. stako8

    Может кто сталкивался: отправляю клавиатуру «bot******/sendMessage?chat_id=******&text=Меню&reply_markup={«inline_keyboard»:[[{«text»:»1″,»callback_data»:»1″},{«text»:»2″,»callback_data»:»2″}]]}», она приходит пользователю, тот нажимает на неё, но через «getUpdates» не приходит ответ что он нажал. Куда копать?

    Reply ↓
  21. user1061591

    (15)Здравствуйте, скажите возможно ли приобрести у вас лично вашу обработку, т.к. купить отдельно стартмани я не могу, а на дорогущий абонемент денег нет. Просто делаю диплом в колледже на похожу тему, вот и хотелось бы посмотреть как это реализовано.

    Reply ↓
  22. lishniy

    (21) Сообщения из inline_keyboard приходят чуть по другому. Добавьте в цикле

    Для Каждого mes из result Цикл
    Если message.Свойство(«text») Тогда

    условие

    ИначеЕсли mes.Свойство(«callback_query») Тогда
    Ответ = СтрокаМассива.callback_query.data;
    Reply ↓
  23. stako8

    (23) У меня проблема не с поиском, а с тем, что ответ вообще не приходит, чтобы я не нажимал и не делал. Приходит только сообщение о том, что я вызывал меню. А вот апдейт, что я нажал кнопку не приходит.

    Reply ↓
  24. lishniy

    (24) Странно. А клиент какой? Должно быть так.

    Reply ↓
  25. stako8

    (25) Если Вы про клиент телеграмма — пробовал нажимать и на десктопе и на мобилке, результат на скрине в предыдущем моём сообщении.

    Reply ↓
  26. lishniy

    (26)Конечно, это не совсем то, что вы делаете, попробуйте так сделать меню

    /sendMessage?chat_id=ЧЧЧ&text=Меню&reply_markup={«keyboard»:[[{«text»:»Пункт меню»}]],»resize_keyboard»:true}
    Reply ↓
  27. stako8

    (27) Так это обычная кнопка, которая отправляется как текст

    Reply ↓
  28. lishniy

    (28) Так и есть. Разница, что первая в чате под сообщением, а эта как отдельная клавиатура

    Reply ↓
  29. stako8

    (29)Ещё раз повторюсь: возможно я не в том формате формирую строку и данные на сервер телеграмма приходят не совсем корректные (с виду строка с параметрами правильная), по-этому не могут быть возвращены через callback_query.

    Reply ↓

Leave a Comment

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