Telegram-боты

Программирование - Практика программирования

telegram телеграм бот

141
Описание теории, разбор архитектуры и пример реализации telegram-ботов. Сразу скажу, со структурированием изложения мало что могу поделать. :) редакция от 18.07.2018 Правки последней редакции выделены жирным.

В языке 1С все мы в той или иной степени спецы, но того же нельзя сказать применительно к Telegram (далее «телега»). Почему? Причина в высоком уровне входа в API. Лично я, когда почитал страницу на сайте телеги, подумал «и где же дока по апишке?» Оказалось, это и была «типа самодостаточная дока по апишке» в понимании разработчиков. Потом был период ковыряния интернета на предмет реализации той или иной функции средствами платформы 1С. Что-то находилось, но в основном только вопросы без ответов или адски колхозные решения.

Сейчас я научился понимать текст на странице, которую разрабы называют описанием API. Это понимание нашло реализацию в виде разнообразных ботов. Сначала реализация была для одного бота, потом, когда в одной конфигурации их стало несколько, пришлось адаптировать некоторые механизмы под работу с произвольным количеством ботов, появились общие модули, сформировалась концепция работы с телегой.

Смотрю уже не первый месяц ИС и вижу отдельные попытки исследования API Telegram и даже создания конструкторов ботов. Попытки не систематизированные, часто, неоптимальные или устаревшие, не использующие нынешние возможности платформы в полной мере. А для конструкторов просто не вижу перспектив. Если целевая аудитория конструктора - пользователи, то они не смогут написать обработчик какой-либо команды бота и будут ограничены возможностями конструктора, а если программисты, то для них наиболее оптимальной реализацией ботов будет исключение сервисных механизмов конструктора. Понятно, что разбираться в API телеги мало кому хочется, было бы удобно, если бы оно уже было разобрано и имелся бы набор функций для бота. Это и хочу предложить вам ниже. Теория, затем разбор архитектуры решения, почему именно так, а не иначе, и наконец, сам код. Долго думал, надо ли мне это, ведь можно снимать сливки с рынка, обладая информацией. Под публикацией вы найдёте выгрузку из куцей конфигурации, которой хватит для понимания и демонстрации содержания статьи (я поставил 0sm, но сайт поставил 1).

 

Что такое телеграм-бот? Упрощу описание: это просто запись в таблицах на серверах телеги, что есть такой «пользователь» телеги с типом «бот». Значит, ему могут писать, он может отвечать, его можно добавлять в группы и пр. Нюанс только в том, что обычный пользователь – человек и он с помощью клиента телеги сам может прочитать, ответить, добавиться в группу и пр, а тут нам надо каким-то образом читать/отправлять сообщения и мониторить прочие события, которые происходят с ботом.

В телеграме нет спама. Достигается это и благодаря ограничению общения ботов: они могут писать только тем пользователям, которые сами обращались к боту. Т.е. отправить сообщение вы можете кому угодно, но увидят его только те пользователи, у которых есть история общения.

Читать можно двумя способами: лазить на сервера и проверять/забирать новые сообщения (способ «getUpdates») или, наоборот, отправить телеге ссылку, куда она должна скидывать новые сообщения сама (способ «вебхук»).

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

Для getUpdates есть история. Пока я писал первого бота, я отправлял ему тестовые сообщения и всё было хорошо, но когда я запустил его в «прод», бот замолчал. Я начал исследовать и выяснилось, что новые сообщения вообще не приходят боту. Ну, думаю, часто бот отвечал и телега его забанила. Прошло несколько дней, ситуация не изменилась. Тогда я полез в доку и выяснил, что хотя у метода getUpdates все операнды опциональны, всё же стОит обратить внимание на первый из них:

оffset – идентификатор первого апдейта, начиная с которого будут переданы все поступившие апдейты. После того, как апдейт был успешно передан любым способом, начинает тикать счётчик. Спустя 24 часа этот апдейт (не сообщение) будет удалён с серверов телеги, но пока он там, мы за какой-то надобностью можем его получить, если будем указывать отрицательное значение этого параметра. На практике мне это не было нужно, т.к. все запросы от ботов я записываю в ИБ.

Получается, для каждого бота надо хранить ид последнего апдейта, прибавлять к нему 1 и запрашивать с получившегося ида апдейты. По-умолчанию сервера выдают 100 новых апдейтов. Количество можно указать в операнде limit от 0 до 100. На практике этим параметром можно пренебречь. Например, бот умер на неделю, ему наслали 1000 сообщений. Он первым запросом получит первые 100, следующий запрос будет получать вторую сотню и т.д.

Я реализовал ботов обработками. Одна обработка – один бот. Обработки запускаются отдельными рег.заданиями, чтобы было удобно включать и выключать ботов. Получается, формы им не нужны, параметры тоже, работают они на сервере.

Как описывалось выше, в случае со способом получения апдейтов getUpdates мы заинтересованы в как можно более стремительной реакции бота. Исходя из этого следует, что соединение с сервером телеги должно устанавливаться один раз и общение бота будет происходить в рамках этого соединения. Нюанс в том, что рег.задание создаёт фоновое задание каждые 3 секунды и новое фоновое задание создаётся в новой среде, которая не знает, что соединение уже установлено. Способ передачи HTTPСоединения между фоновыми заданиями я пока не нашёл. Создавал на форуме уже две темы и получал неприемлемые ответы. Поэтому пока могу предложить колхоз – установку соединения каждым фоновым заданием.

Если у вас база в режиме файл-сервер, то вам стоит исследовать код, который отвечает за выполнение рег.заданий в файловом режиме. Столкнулся с тем, что хотя у рег.задания задан интервал 3 секунды, выполняется он раз в минуту.

Чтобы описать ботов, сделал отдельный справочник «Боты», где все боты задаются предопределёнными элементами для удобства ссылок на них в коде. Первый необходимый реквизит – ид бота, он же «токен». Вторым реквизитом можно указать имя обработки бота. Выше я упоминал ид последнего апдейта, полученного ботом. Можно хранить его в реквизите справочника. Считать каждый раз запросом МАКСИМУМ() от всех ид апдейтов данного бота, хранящихся в ИБ будет более затратным по мощностям и времени.

Далее получается, что сообщения надо читать многим ботам. Так зачем плодить этот механизм? Так появилось отдельное рег.задание «Чтение апдейтов». Оно запускается каждые 3 секунды и читает апдейты для всех ботов, у которых указано, что им апдейты надо читать автоматически. Галка «Автоматическое получение апдейтов» – ещё один реквизит справочника.

Теперь обратим внимание на ограничения ботов. Например, у одного бота мне надо было, чтобы он отвечал на запросы только из личек, а в группах не общался, а у другого – чтобы он имел возможность работать в инлайн-режиме. Не надо городить в коде то, что может быть реализовано средствами самОй телеги. У ботов есть создатель - @botFather, который может это всё настроить. Рекомендую вам полазить по его настройкам и изучить их.

Ещё одно ограничение, которое может быть полезно, - это ограничение типов сообщений, получаемых ботом. Например, у меня есть бот, который читает только локи (location). Убрать лишний флуд можно операндом allowed_updates метода getUpdates и этот список типов сообщений тоже может храниться в реквизите справочника «Боты».

Полученные апдейты не всегда, но в большинстве случаев целесообразно хранить в ИБ. Это пригодится и для анализа запросов при разработке, и для разделения механизмов чтения и обработки апдейтов. Для цели хранения апдейтов можно создать регистр сведений «СообщенияБотам». Структура апдейта весьма витиевата, поэтому создавать под каждый его реквизит отдельный реквизит в РС нет смысла. В конце разработки бота вы заметите, что из всех реквизитов часто вам нужны только несколько: автор сообщения, ид сообщения, ид чата, текст сообщения (если это текстовое сообщение). Сравните с полным описанием структуры сообщения. Всё остальное, как например, ид сообщения, в ответ на которое пришло текущее сообщение, может быть получено из отдельного реквизита регистра, где хранится полный текст сообщения со всей структурой. Т.о. структура РС может быть следующего вида:

update_id
Бот - Поскольку в РС хранятся сообщения от всех ботов, то надо знать владельца этого апдейта.
МоментВремени
Пользователь - Я рекомендую сразу создать доп.реквизит TelegramID у сущностей ИБ, описывающих персонажей, которые могут общаться с ботом - контрагенты, физ.лица, сотрудники и т.п. В реквизите я храню саму сущность, это удобно для анализа, но это не принципиально.
message_id - Часто нужен, например, для ответа на сообщение.
chat_id - Нужен всегда - в какой чат отвечаем?
Обработан - Признак того, что данное сообщение было обработано ботом. Удобен для отладки, когда вылетает ошибка на обработке сообщения. При устранении ошибки запускаем заново бота и пытаемся обработать сообщение повторно.
ТипСообщения - Этот реквизит нужен для анализа типа сообщения и реакции на него.
ТекстСообщения - Если тип сообщения - message, то в реквизите текст сообщения.

Итак, у нас есть справочник, где хранятся настройки ботов, есть рег.задание, которое получает апдейты, есть обработка конкретного бота и рег.задание, которое запускает эту обработку. Что д.б. в обработке бота? В ней будет самое мясо – обработка новых непрочитанных сообщений.

Я не претендую на полный обзор всех возможностей ботов, но планирую описать наиболее интересные и полезные из них: отправку/получение файлов, inline-клавиатуры, «перманентные» клавиатуры, команды бота и некоторые другие возможности.

 

Начнём с самого простого – с команд бота. Сразу открою, что эти команды должны быть описаны у создателя ботов. Т.о. при вводе слеша первым символом сообщения пользователю будет показываться список предопределённых команд бота с заданным вами описанием. Это удобно. Учитывайте сразу, что пользователь может написать команду рАзНыМ регистром и добавить в конце @имя-вашего-бота. Последнее необходимо для точной идентификации бота-получателя команды. Например, в группу добавили трёх ботов. У каждого бота есть команда /start. Какому боту написали /start в группе? Именно @имя-вашего-бота после команды и уточняет получателя.

Поскольку список команд бота достаточно постоянен, его можно описать в справочнике команд ботов с указанием обработчика команды. При анализе сообщений будем фильтровать сообщения, начинающиеся со слеша и проверять по данному справочнику. Если нашли, вызываем обработчик и передаём ему сообщение.

Для анализа наличия команд удобно использовать свойство Message.Entities. Сервер телеги сам парсит пришедшее от пользователя текстовое сообщение и выделяет из него части вроде юзернеймов, команд бота, ссылок, хэштегов и пр. У каждого элемента этого массива есть свойство Type. Для команд бота у него будет значение "bot_command". Таким образом, вы можете не парсить ручками текст сообщения, а анализировать именно это свойство.

 

Теперь про клавиатуры. Они бывают двух типов - ReplyKeyboard, которые я называю "перманентными" и InlineKeyboard.

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

Куда интереснее дело обстоит с инлайн-клавиатурами. Это набор кнопок, которые привязаны к сообщению и выводятся под ним. Инлайн-клавиатура не может существовать без сообщения и сообщение д.б. от бота. Примечательно то, что по нажатию на кнопку во-первых, генерится апдейт с типом callback_query, а во-вторых, возвращаться может заданное вами значение. Даже если в качестве возвращаемого значения вы укажете строку, у вас будет гарантия по типу сообщения, что это не пользователь написал вручную, а была нажата такая-то кнопка клавиатуры, привязанной к такому-то сообщению.

Строго говоря, у апдейтов нет типов. Типы я ввёл для удобства дальнейшей обработки их ботами. На стадии записи апдейта в ИБ я проверяю наличие тех или иных параметров и устанавливаю тип апдейта. Например, если есть свойство callback_query, ставлю тип callbackquery, если у параметра message есть location, то ставлю тип location. Сразу становится понятно - прислали текст, или локу, или нажали инлайн-кнопку.

Как описать клавиатуры? У перманентных клавиатур кнопка имеет только один параметр - название (text). У инлайн - название (text) и значение (callback_data). На самом деле у них есть и другие параметры, открывающие потрясающие возможности, но их я описывать тут не буду пока. Значение инлайн-кнопок д.б. именно строковым, т.е. запихнуть туда ЗначениеВСтрокуВнутр() не получится. Рекоментую туда передавать УникальныйИдентификатор. И тут возникает хороший вопрос "зачем"...

Зачастую боты имеют многоуровневые меню. Например, сначала нам надо узнать у пользователя склад самовывоза, а потом время, когда он собирается приехать. Как это можно реализовать? Он даёт нам команду, что хочет забрать товар сегодня, мы в ответ выводим сообщение "да не вопрос, куда собираетесь ехать?" и с этим сообщением передаём инлайн-клавиатуру со списком возможных складов. Можно в качестве значения кнопки передавать уникальный идентификатор элемента справочника "МестаХранения" из ИБ, но это неудобно, т.к. при ответе надо понимать, на какой вопрос пришёл ответ.

Создаём РС "ВариантыОтветов", где измерением ставим реквизит "Идентификатор" с типом УникальныйИдентификатор, добавляем текстовый ресурс "Значение" и реквизит "ДатаСоздания". Ресурс будет содержать ЗначениеВСтрокуВнутр() от нужного нам значения, а реквизит нужен для последующего удаления данных сборщиком мусора, чтобы эти варианты не висели в ИБ вечно. В качестве значения кнопки передаём Идентификатор. Это позволит "привязать" кнопку к структуре, которая по ходу ответов от пользователя будет заполняться значениями его ответов.

Итак, пользователь выбрал склад, нам пришёл апдейт callbackquery, в этом апдейте указан код кнопки и из него мы понимаем, что за кнопка была нажата. Теперь можно изменить текст сообщения с клавиатурой на "Самовывоз со склада такого-то. Уточните время:" и клавиатуру заменить на предопределённые значения часов типа 09:00, 10:00 и т.д. При этом, выводя клавиатуру с часами есть несколько способов учесть ответ пользователя на предыдущий вопрос. Нам где-то надо хранить предыдущие ответы. Мы можем их включать в структуру из РС "ВариантыОтветов", а можем создать отдельный РС "СессииБота". В нём будет измерением ид чата (у приватных чатов он совпадает с ид пользователя телеги) и знакомые нам ресурс "Значение" и реквизит "ДатаСоздания". Если вам удобнее, можете вместо одного ресурса "Значение" создать отдельные ресурсы под каждое значение ответа пользователя. Из примера выше это будут "Склад" и "Время". Универсальность единого ресурса в том, что в нём можно хранить структуры разного состава реквизитов.

При поступлении апдейта callbackquery смотрим по РС "ВариантыОтветов", что пришло. Смотреть удобнее, когда с идентификатором проассоциировано не просто значение объекта ИБ, а структура, где указано, что за меню это было и значение ответа. Т.е. структура вида:

Операция: "ВыборСклада"
Значение: a763cfbb-f94f-4c67-8e13-0e96a3a7f353

Отсюда сразу понятно, что надо в РС "СессииБота" в сессии из соответствующего чата присвоить реквизиту "Склад" ссылку на элемент справочника "МестаХранения" с идентификатором "a763cfbb-f94f-4c67-8e13-0e96a3a7f353".

Когда пользователь выберет конкретное время, нам придёт callbackquery со ссылкой на запись в РС "ВариантыОтветов". Также, как и со складом, достаём из РС информацию, что это выбиралось время и что было выбрано такое-то конкретное время. Устанавливаем его в РС "СессииБота" и получаем заполненную структуру ответов от пользователя. Обновляем текст сообщения, чтобы отразить в нём результат выбора пользователя и убираем клавиатуру вообще.

Все клавиатуры - это JSON. Исходно это текст и его можно хранить в текстовых реквизитах. Вспоминаем, что в платформе 1С есть объекты и методы для работы с этим форматом: ЧтениеJSON, ПрочитатьJSON. Клавиатура - это массив строк из массива кнопок. В интернете полно генераторов, где можно задать массив и он будет преобразован в JSON, но можно и поэкспериментировать на примерах из 1С. Создайте массив из массивов структур с элементами text и callback_data и преобразуйте его с помощью кода в JSON:

СтруктураОтвета = Новый Структура;
СтруктураОтвета.Вставить("inline_keyboard", МассивСтрок);

ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку(Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Нет,,,ЭкранированиеСимволовJSON.СимволыВнеASCII));
ЗаписатьJSON(ЗаписьJSON, СтруктураОтвета);

Должно получиться что-то вроде этого:

{
	"inline_keyboard":
	[
		[
			{"text":"Разрешить","callback_data":"%1"},
			{"text":"Отклонить","callback_data":"%2"}
		]
	]
}

Поскольку в перманентных клавиатурах у кнопок есть только один реквизит text, разработчики решили нас порадовать упрощённой формой описания клавиатуры:

{
	"keyboard":
	[
		[
			"Разрешить",
			"Отклонить"
		]
	]
}

 

Не всегда общение возможно с помощью клавиатур, иногда от пользователя ожидается текстовый ввод. Выходит, при поступлении текстового сообщения боту надо знать: это просто пользователю скучно или он отвечает на какой-то вопрос, заданный ему на каком-то этапе общения? Рассмотрим для примера функцию отправки отзыва о боте. Пользователь нажимает кнопку перманентной клавиатуры "Оставить отзыв". Мы видим, что есть такая кнопка, выводим ему "Следующим сообщением оставьте всё, что вы думаете обо мне" и ожидаем от пользователя сообщение. Это ожидание надо где-то зафиксировать. Я для этого создал РС "СессииКонтрагентов", очень похожий по структуре на "СессииБота". В нём хранятся текущие статусы общения с пользователем, если они есть. Например, после вывода сообщения пользователю про написание отзыва, следует записать в этом регистре, что в таком-то чате у нас теперь статус "ОжиданиеОтзыва". И когда придёт следующее сообщение, мы предварительно проверим статус и выполним соответствующую его обработку.

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

Параметры = Новый Структура("reply_markup, parse_mode", "{""force_reply"":true}", "Markdown");
НовоеСообщение = Боты.ОтправитьСообщение(Бот, Сообщение.Чат, "Укажите дату последнего медицинского осмотра в формате дд.мм.гггг:
|Например: _28.01.2018_",, Параметры);

В примере я передаю два параметра:

  1. parse_mode со значением Markdown говорит, что надо использовать при показе сообщения клиенту условное оформление (те самые подчёрки вокруг даты дают курсив);
  2. reply_markup со значением force_reply:true (JSON) говорит, что надо принудительно перевести клиента в режим ответа на это сообщение.

В результате клиент не задумывается о том, чтобы выделить нужное сообщение и выбрать "Reply", а вы получаете ответ, который привязан к вашему сообщению: в полученном сообщении надо анализировать реквизит Message.reply_to_message .

 

Теперь немного про отправку и приём файлов. Каждый залитый в телегу файл получает свой ид. Если вы хотите залить тот же файл ещё раз, выгоднее сделать это, указав тот самый ид файла. В этом случае перекачка выполняться не будет, а будет дана ссылка на файл на серверах телеги. Подробно об этом сказано здесь. Из трёх способов, о которых говорится по ссылке, как правило, всех интересует только один способ - как файл с диска или из ИБ залить в телегу. Сказано про это весьма лаконично:

Передайте файл, используя multipart/form-data как это обычно делает браузер.

Такая краткость вводила и меня в ступор поначалу. Потом начал гуглить, как же заливает файлы браузер и нашёл несколько статей, в.т.ч. и с реализацией на платформе 1С. К сожалению, примеры писались ещё под платформу 8.2, где всё было не столь радужно как в 8.3. Не буду подробно описывать, как происходит перекачка файлов, предоставлю вам эмоции первооткрывателей-гуглёжников. Во вложении содержится кусок кода для отправки различных видов медиа в чаты. В данной конфигурации он у вас не будет работать, потому что общий модуль "ПрисоединенныеФайлы" я просто дёрнул из БСП и он алярмит об ошибках. Практически все типовые конфигурации построены на базе БСП, поэтому часть функций по работе с присоединёнными файлами к объектам ИБ я не стал колхозить, а использовал стандартную функциональность. Если ваша конфигурация почему-то не использует БСП, можете написать свою функцию получения прикреплённого файла.

Один момент колхоза в реализации работы с файлами у меня есть. Как я говорил выше, если файл уже заливался на сервера телеги, то нам выгоднее использовать его ид, полученный после закачки. Я не стал создавать отдельный реквизит, потому как пришлось бы его создавать во всех справочниках "*ПрисоединенныеФайлы", а задействовал под хранение ида реквизит "Описание", который у меня не использовался. Если в вашей конфигурации он используется, тогда придётся создавать отдельный.

 

На этом пока остановлюсь. Ожидаю ваши вопросы и замечания в комментариях. Буду реагировать и дописывать либо, если вопросов будет много, сделаю отдельную публикацию.
Также, достаточно много информации по пониманию описания API содержится на ИС в комментариях к другим статьям про телегу.

141

Скачать файлы

Наименование Файл Версия Размер
Telegram-боты:
.dt 95,75Kb
01.09.17
99
.dt 09.06.3 95,75Kb 99 Скачать

См. также

Комментарии
Избранное Подписка Сортировка: Древо
1. zeltyr 04.09.17 11:09 Сейчас в теме
Эх, эту бы статью, да неделю назад, когда я только со всем этим начал разбираться.
Сейчас 90% статьи уже своим умом дошёл.
Но всё равно, большое спасибо вам за труд.
NewLifeMan; +1 Ответить
26. user853472 26.02.18 11:39 Сейчас в теме
(1)

Доброго времени суток! Подскажите как отправить фото с клавиатурой inline_keyboard?

ддКартинка = Блюдо.Ссылка.ОсновноеИзображение.Хранилище.Получить().ПолучитьДвоичныеДанные();

времКаталог = КаталогВременныхФайлов();
времФайлКартинки = ПолучитьИмяВременногоФайла("jpg");
имяВремяФайла = СтрЗаменить(времФайлКартинки,времКаталог,"");

ддКартинка.Записать(времФайлКартинки);

СтрокаСоединения = "/bot" + Константы.Токен.Получить() + "/sendPhoto";

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

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

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

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

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

// ЧАТ
ТекстДляОтправки = ТекстДляОтправки + "--" + Boundary + Символы.ПС;
ТекстДляОтправки = ТекстДляОтправки + "Content-Disposition: form-data; name=""chat_id"""+ Символы.ПС + Символы.ПС;
ТекстДляОтправки = ТекстДляОтправки + СокрЛП(Формат(Клиент.Код,"ЧГ=0")) + Символы.ПС;

// ПОДПИСЬ
Подпись = "Наименование: " + Блюдо.Ссылка.Наименование + Символы.ПС + "Цена: " + Блюдо.Ссылка.Цена;

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

// ФОТО
ТекстДляОтправки = ТекстДляОтправки + "--" + Boundary + Символы.ПС;
ТекстДляОтправки = ТекстДляОтправки + "Content-Disposition: form-data; name=""photo""; filename=""" + имяВремяФайла + """" + Символы.ПС;
ТекстДляОтправки = ТекстДляОтправки + "Content-Type: image/jpg" + Символы.ПС;

//КЛАВИАТУРА
клСтруктура = Новый Структура;
Строки = Новый Массив;
Кнопки = Новый Массив;
Кнопки.Добавить(Новый Структура("text, callback_data", "В корзину", Блюдо.Ссылка.Код));
Строки.Добавить(Кнопки);
клСтруктура.Вставить("inline_keyboard",Строки);

ЗаписьJSON = Новый ЗаписьJSON;
//ЗаписьJSON.УстановитьСтроку(Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Нет,,,ЭкранированиеСимволовJSON.Нет)); //Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Нет,,,ЭкранированиеСимволовJSON.СимволыВнеASCII)
ЗаписатьJSON(ЗаписьJSON,клСтруктура,,);
Клавиатура = ЗаписьJSON.Закрыть();

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

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

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

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

ИмяФайлаОтправки = ПолучитьИмяВременногоФайла("txt");

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

HTTPЗапрос = Новый HTTPЗапрос;
HTTPЗапрос.Заголовки.Вставить("Connection", "keep-alive");
HTTPЗапрос.Заголовки.Вставить("Content-Type", "multipart/form-data; boundary="+Boundary);
HTTPЗапрос.УстановитьИмяФайлаТела(ИмяФайлаОтправки);
HTTPЗапрос.АдресРесурса = СтрокаСоединения;

Результат = ОтправитьЗапросНаОбработкуВTelegramPOST(HTTPЗапрос);

Для Каждого Файл Из МассивФайловДляОбъединения Цикл
УдалитьФайлы(Файл);
КонецЦикла;
Показать


Если убрать блок который отвечает за клавиатуру, то картинку отправляет, а с клавиатурой сервер возвращает ответ "IMAGE_PROCESS_FAILED"
2. eskor 82 04.09.17 14:56 Сейчас в теме
+ Слежу за этим вопросом еще со времен первой публикации.
"Телега" единственный мессенджер с вменяемым API, поэтому в корпоративных проектах ему быть.
Мой "питомец" уже пару лет отчеты шлет :)
kamervan; +1 Ответить
3. V_V_V 05.09.17 11:12 Сейчас в теме
Адский поток сознания, а не статья... На разделы побить бы, что-ли.
Имею собственноручно созданного бота - вот ничем бы не помог этот набор слов.
VachKirp; zzumma; dj_serega; +3 Ответить
4. PLAstic 204 05.09.17 13:05 Сейчас в теме
Друзья, кто скачал конфигурацию, в неё закралась ошибка. Я выложил новую версию конфигурации, но вы можете устранить ошибку и вручную: в РС "ЗапросыБоту" реквизит "ТипСообщения" должен иметь тип "Перечисление.ТипыСообщения".

Для оперативной связи моя телега.
5. imedeev 3 06.09.17 09:34 Сейчас в теме
Тяжело читать - сплошной текст, но тема интересная.
Когда то сам делал - для вечерней отправки отчета по ОРП для босса
6. volodya82 06.09.17 09:41 Сейчас в теме
{ОбщийМодуль.Боты.Модуль(72)}: Метод объекта не обнаружен (ПриПолученииСообщения)
7. PLAstic 204 06.09.17 09:56 Сейчас в теме
(6) Спасибо, закомментировал. Этот кусок кода относился к ботам, которым нужна дополнительная обработка при получении апдейтов. Поскольку фича мало интересная, я не стал её описывать и удалил упоминание.
8. more 07.09.17 21:19 Сейчас в теме
Хорошая информация, для первичного погружения очень хорошо.
9. flyer 207 26.09.17 13:12 Сейчас в теме
подскажите почему возникает ошибка для getUpdates : Conflict: can't use getUpdates method while webhook is active?
10. PLAstic 204 26.09.17 14:15 Сейчас в теме
(9) У вас был установлен вебхук для получения апдейтов. В апи подробно про это было сказано: https://core.telegram.org/bots/api#making-requests-when-getting-updates
Если его надо выключить, то надо отправить deleteWebhook.
11. flyer 207 28.09.17 13:14 Сейчас в теме
(10) с этим разобрался спасибо!
еще подскажите такая ситуация. а то что то видимо делаю не так. добавил бота. test_123456789_bot я с телефона пишу ему и на компьютере вижу сообщение. попросил другого пользователя найти этого бота и ему написать но его сообщения не появляются у меня.
12. sdwggg 05.10.17 15:33 Сейчас в теме
кто-нибудь знает, как реализовать следующее?
Бот должен ожидать от пользователя ввода даты. При этом на самом устройстве (в данном случае на iPhone) должна появиться клавиатура выбора даты, а не обычная текстовая и не циферная, чтобы в конечном итоге боту была отправлена команда вида "ДД.ММ.ГГГГ"
13. PLAstic 204 05.10.17 23:51 Сейчас в теме
(137) Виды клавиатур я уже описывал. Для данного случая подходит только инлайн, но делать клавиатуру для ввода времени - это весьма забавно, имхо.
14. Lyolik 8 31.10.17 11:44 Сейчас в теме
Интересно, а как к кнопке добавить картину - для наглядности. На пример, как на скрине...
Прикрепленные файлы:
15. botokash 38 01.11.17 13:52 Сейчас в теме
(14) Это эмоджи, добавляйте просто в текст кнопки
16. Lyolik 8 02.11.17 11:04 Сейчас в теме
(15) Что, именно, нужно добавить? У эмоджи есть какое-то текстовое представление или код? Если да, то где можна это посмотреть? Спасибо.
17. botokash 38 02.11.17 11:50 Сейчас в теме
(16) Эмоджи это текст. Заходишь например на emojipedia.org, находишь понравившееся, и копипастом добавляешь себе.
21. Kabz 74 08.11.17 14:50 Сейчас в теме
(17) что не все вставляеться.
29. GlebBR 03.07.18 21:28 Сейчас в теме
(21) решил проблему с эмоджи так. Захожу на https://apps.timwhitlock.info/emoji/tables/unicode. Беру столбец Bytes. Пример: \xF0\x9F\x93\x9E и заменяю на %F0%9F%93%9E и вставляю в текст ,в telegram отображается эмоджи. Может кто то знает как это гуманно сделать средствами 1с, я делаю Стрзаменить.
18. guy_septimiy 02.11.17 13:44 Сейчас в теме
Доходил аналогично автору тестовым путем в июне.
В целом, хорошая статья, проясняющая для новичка не в теме, многие моменты.
Автору респект
NewLifeMan; PLAstic; +2 Ответить
19. Master598 07.11.17 14:42 Сейчас в теме
кто может потерпеть мои вопросы новичка? в личном чате. а то запутался.
хочу сделать бота в группе и чтобы через 1с туда напоминала и люди уже не могли отмазаться, что Почты много, затерялось письмо, или в 1с не заходил уже 3 дня.

п.с. не халявщик, сижу читаю ваши коды, и пробую понять как это все мне под себя прикрутить.
20. PLAstic 204 08.11.17 09:04 Сейчас в теме
Вполне можешь задавать здесь. Ответы на вопросы могут быть полезны и остальным.
NewLifeMan; +1 Ответить
22. taishy 50 21.12.17 16:29 Сейчас в теме
Кто знает, можно ли подключаться из 1С к телеграм от имени клиента?(не бота)
23. PLAstic 204 22.12.17 11:31 Сейчас в теме
(22) Теоретически это возможно. Где-то на гитхабе есть исходники tgmcli (или как-то так). Это библиотека для работы от имени пользователя телеги. Сам не сталкивался, только слышал. Думаю, ваши шансы всё равно стремятся к нулю, но удачи.
25. taishy 50 22.12.17 11:48 Сейчас в теме
(23) Есть бредовая идея сделать обмен на базе телеграма. В качестве бота думал сделать главный узел, а в удаленных - в режиме клиента (для инициирования диалога с ботом и отправки запросов)
24. Alexey_ 23 22.12.17 11:44 Сейчас в теме
27. GlebBR 01.07.18 10:35 Сейчас в теме
полезная статья, кто нибудь решал такую проблему: режим обмена через webhook, задаю вопрос пользователю он быстро набирает много сообщений пока я не ответил. Как мне отсекать ответы пользователя которые лишние. Пример: Я задал вопрос , пользователь набрал быстро 3 ответа. Бот должен отработать только один ответ(самый первый.).Как только ответит бот, общение продолжается.
28. PLAstic 204 01.07.18 14:14 Сейчас в теме
(27) если ты будешь делать сессии по сообщениям с клавиатурой, то после получения ответа я их удаляю обычно. Т.е. все последующие запросы не найдут сессии и не будут обработаны.
31. GlebBR 04.07.18 11:54 Сейчас в теме
решил проблему с эмоджи так. Захожу на https://apps.timwhitlock.info/emoji/tables/unicode. Беру столбец Bytes. Пример: \xF0\x9F\x93\x9E и заменяю на %F0%9F%93%9E и вставляю в текст ,в telegram отображается эмоджи. Может кто то знает как это гуманно сделать средствами 1с, я делаю Стрзаменить.
32. PLAstic 204 18.07.18 12:47 Сейчас в теме
Статья обновлена, жирным выделены дополнения.
33. ROM_1C 664 15.09.18 00:52 Сейчас в теме
34. slavik_s 12.10.18 18:27 Сейчас в теме
Скачал конфигурацию, а в ней почти ничего нет. Очень интересует вопрос как решить перенос кнопок на другую строку, чтобы кнопки выстроились одна под другой?
36. PLAstic 204 12.10.18 18:45 Сейчас в теме
(34) в статье про кнопки написано.
37. slavik_s 13.10.18 01:31 Сейчас в теме
(36) По этому коду
{
"inline_keyboard":
[
[
{"text":"Разрешить","callback_data":"%1"},
{"text":"Отклонить","callback_data":"%2"}
]
]
}
Кнопки выстраиваются в одну строку [Разрешить] [Отклонить] а надо чтобы друг под другом
[Разрешить]
[Отклонить]
На моем примере это так:
{
"inline_keyboard": [
[
{
"text": "08:10 - Сидоров",
"callback_data": "1539327600;000000002292"
},
{
"text": "10:10 - Иванов",
"callback_data": "1539334800;000000003398"
},
{
"text": "10:10 - Петров",
"callback_data": "1539336600;000000003404"
},
{
"text": "12:10 - Голубев",
"callback_data": "1539343800;000000003397"
}
]
]
}

На каждой кнопке Время и Фамилия пациента получающего услугу в указанное на кнопке время и они должны размещаться одна под другой иначе их просто не видно если в одну строку.
35. slavik_s 12.10.18 18:31 Сейчас в теме
еще интересует как отправлять кнопки ReplyKeyboard ? В сообщении?
Оставьте свое сообщение