HackMD
  • New!
    New!  “Bookmark” and save your note
    Find a note that's worth keeping or want reading it later? “Bookmark” it to your personal reading list.
    Got it
      • Create new note
      • Create a note from template
    • New!  “Bookmark” and save your note
      New!  “Bookmark” and save your note
      Find a note that's worth keeping or want reading it later? “Bookmark” it to your personal reading list.
      Got it
      • Options
      • Versions and GitHub Sync
      • Transfer ownership
      • Delete this note
      • Template
      • Save as template
      • Insert from template
      • Export
      • Dropbox
      • Google Drive
      • Gist
      • Import
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
      • Download
      • Markdown
      • HTML
      • Raw HTML
      • ODF (Beta)
      • Sharing Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Note Permission
      • Read
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Write
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • More (Comment, Invitee)
      • Publishing
        Everyone on the web can find and read all notes of this public team.
        After the note is published, everyone on the web can find and read this note.
        See all published notes on profile page.
      • Commenting Enable
        Disabled Forbidden Owners Signed-in users Everyone
      • Permission
        • Forbidden
        • Owners
        • Signed-in users
        • Everyone
      • Invitee
      • No invitee
    Menu Sharing Create Help
    Create Create new note Create a note from template
    Menu
    Options
    Versions and GitHub Sync Transfer ownership Delete this note
    Export
    Dropbox Google Drive Gist
    Import
    Dropbox Google Drive Gist Clipboard
    Download
    Markdown HTML Raw HTML ODF (Beta)
    Back
    Sharing
    Sharing Link copied
    /edit
    View mode
    • Edit mode
    • View mode
    • Book mode
    • Slide mode
    Edit mode View mode Book mode Slide mode
    Note Permission
    Read
    Owners
    • Owners
    • Signed-in users
    • Everyone
    Owners Signed-in users Everyone
    Write
    Owners
    • Owners
    • Signed-in users
    • Everyone
    Owners Signed-in users Everyone
    More (Comment, Invitee)
    Publishing
    Everyone on the web can find and read all notes of this public team.
    After the note is published, everyone on the web can find and read this note.
    See all published notes on profile page.
    More (Comment, Invitee)
    Commenting Enable
    Disabled Forbidden Owners Signed-in users Everyone
    Permission
    Owners
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Invitee
    No invitee
       owned this note    owned this note    
    Published Linked with
    Like BookmarkBookmarked
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    --- title: Введение в разработку диалоговых ботов. День 1 --- # Что такое чат-бот и зачем он нужен Диалоговый бот (**чат-бот**) - программа, взаимодействующая с пользователем в режиме онлайн-переписки. Находит применение в самых разных сферах бизнеса и общественной жизни: - Банкинг и финансы - Заказ товаров и услуг - Персональные ассистенты - Подписки на СМИ - Текстовые игры - и многое другое! ## Преимущества Главные преимущества диалоговых ботов перед другими способами взаимодействия с пользователем, такими как сайты и приложения: - Отсутствие необходимости установки дополнительного ПО для каждого продукта (пользователю достаточно мессенджера или программы-ассистента для работы с вашим сервисом) - Возможность наладить более органичное и интуитивно понятное взаимодействие (переписываться и разговаривать с ботом, используя стандартные методы взаимодействия) - Скорость загрузки и взаимодействия (пользователю нужно совершать меньше кликов и тратить меньше времени) - Простота разработки (нет необходимости в разработке UI\UX, единый API для общения с мессенджером или ассистентом) ## Системы с поддержкой чат-ботов * Telegram * Яндекс.Диалоги * Facebook Messenger * ВКонтакте * Viber * Skype * Slack * и другие... # Telegram боты Мессенджер Telegram имеет свою платформу для диалоговых ботов. Бот представляет из себя обычный Telegram профиль, но со следующими отличиями и ограничениями: * Бот не может сам начать общение с пользователем. Для начала работы с ботом, пользователь должен добавить его в группу или отправить команду `/start` в ЛС * Юзернейм бота имеет окончание **bot** (например, @TriviaBot, @GitHub_bot) * Боты не могут общаться между собой * Бота, как и обычного пользователя, можно добавить в группу. Бот будет видеть все сообщения в группе, кроме сообщений других ботов ## Регистрация бота Для взаимодействия с ботом, сначала нужно его зарегестрировать. Для этого в Telegram есть специальный бот - [@BotFather](https://tlgg.ru/BotFather). С помощью BotFather можно регистрировать ботов, смотреть информацию о своих ботах, изменять настройки (inline mode, webhook url, privacy settings, и т.д.) ![](https://i.imgur.com/SHjQG5R.png) Для того, чтобы зарегистрировать бота: 1) Введите команду `/newbot` 2) Введите название бота (например: My First Bot) 3) Введите **@алиас** бота, заканчивающийся на bot или Bot (без @, например: MyFirstBot) 4) Бот создан! BotFather отправит сообщение, где указан токен бота. ![](https://i.imgur.com/x0sslqK.png) :::warning Никогда не передавайте токен третьим лицам! Зная токен бота, злоумышленник может нарушить работу бота! ::: ## Задание №1: Регистрация и настройка бота с помощью BotFather Откройте диалог с [@BotFather](https://tlgg.ru/BotFather) и создайте нового бота. 1) Добавьте несколько слов в поле "Description" 2) Добавьте несколько слов в поле "About" 3) Добавьте аватар бота (если не хочется искать фото, возьмите его [здесь](https://images.freeimages.com/images/large-previews/2fb/50-s-robot-1-1181573.jpg)) 4) Зайдите в диалог с вашим ботом и посмотрите, как он выглядит # Telegram Bot API Telegram Bot API - это основанный на HTTP интерфейс взаимодействия ботов и серверов Telegram. Документация - https://core.telegram.org/bots/api. Любой запрос к серверам Telegram представляет из себя GET или POST HTTP запрос по адресу следующей формы: [https://api.telegram.org/bot<token>/METHOD_NAME](https://api.telegram.org/bot<token>/METHOD_NAME). Например, получить информацию о боте можно по ссылке: [https://api.telegram.org/bot<token>/getMe](https://api.telegram.org/bot<token>/getMe). ## Задание №2: Взаимодействие с Telegram Bot API из браузера 1) Используя браузер, перейдите по ссылке: [https://api.telegram.org/bot<token>/getMe](https://api.telegram.org/bot<token>/getMe), вставив токен своего бота вместо `<token>` 2) Изучите JSON ответ сервера Telegram 3) Напишите любое сообщение боту 4) Используя браузер, перейдите по ссылке: [https://api.telegram.org/bot<token>/getUpdates](https://api.telegram.org/bot<token>/getUpdates), вставив токен своего бота вместо `<token>`. В JSON ответе сервера найдите сообщение, которое вы написали ранее, и изучите какие свойства оно имеет 5) Используя браузер, перейдите по ссылке: [https://api.telegram.org/bot<token>/sendMessage?chat_id=**<chat_id>**&text=**<text>**](https://api.telegram.org/bot<token>/sendMessage?chat_id=<chat_id>&text=<text>), вставив токен своего бота вместо `<token>`, идентификатор своего чата (поле `message['chat']['id']` в JSON) вместо `<chat_id>` и текст ответного сообщения вместо `<text>` Пришло ли вам сообщение от бота в Telegram? ## Polling и Webhook Есть два метода взаимодействия бота c серверами Telegram: **polling** и **webhook**. **Polling** - метод, при котором ваш **бот периодически опрашивает** сервера Telegram о наличии новых изменений. При наличии новых сообщений, коллбэков или inline запросов, бот формирует ответы и отправляет их на сервера Telegram. ![](https://i.imgur.com/DDS9wPO.png) Очевидный минус такого способа - огромное количество запросов к серверу, даже если новых изменений нет. --- **Webhook** - метод, при котором **сервера Telegram сами отправляют** изменения вашему боту. Таким образом решается главная проблема polling'а - огромное количество бессмысленных запросов. ![](https://i.imgur.com/otJfole.png) Минусы данного подхода: - нужно сгенерировать и настроить ssl сертификаты - нужно поднять веб-сервер, умеющий принимать запросы из внешнего мира # Подготовка среды для разработки и запуска Telegram бота Инструменты, которые понадобятся нам для написания Telegram бота: 1. Python 3 + pip 2. Текстовый редактор или IDE с поддержкой Python (Sublime Text, PyCharm, Vim, и т.д.) 3. Модуль **pyTelegramBotAPI** ## Установка pyTelegramBotAPI 1) Перейдите в командную строку 2) Запустите команду `python3 -m pip install pyTelegramBotAPI` 3) Дождитесь завершения установки Библиотека теперь доступна для импорта через “import telebot”. Документация и исходный код библиотеки расположены здесь: https://github.com/eternnoir/pyTelegramBotAPI # Эхо бот с использованием pyTelegramBotAPI **pyTelegramBotAPI** - это обертка Telegram Bot API для Python. С помощью этого модуля вы можете взаимодействовать с ботом, не думая о том, куда и с какими параметрами отправлять запросы. Например, для отправки сообщения вы можете использовать простую команду `bot.sendMessage(chat_id=142561490, text='Hi there!')` :::success А теперь вспомним, как мы отправляли сообщение пользователю в [задании №2](#Задание-№2-Взаимодействие-с-Telegram-Bot-API-из-браузера). Использовать pyTelegramBotAPI намного удобнее! ::: Давайте взглянем на простейшего бота, который будет отвечать пользователю его же сообщением. Код простого эхо бота: ```python= import telebot API_TOKEN = '<api_token>' bot = telebot.TeleBot(API_TOKEN) @bot.message_handler(commands=['help', 'start']) def send_welcome(message): bot.send_message(chat_id=message.chat.id, text='Привет! Отправь мне любой текст!') @bot.message_handler(func=lambda message: True) def echo_message(message): bot.reply_to(message, message.text) bot.polling() ``` Разберем программу по частям: ```python= import telebot ``` Здесь мы импортируем библиотеку pyTelegramBotAPI, которая содержит в себе необходимый нам функционал для разработки Telegram ботов. ```python=3 API_TOKEN = '<api_token>' bot = telebot.TeleBot(API_TOKEN) ``` В переменную `API_TOKEN` нужно записать токен вашего бота (<> убрать). Таким образом мы указываем, с каким ботом мы взаимодействуем. Теперь нам доступен объект `bot`, с помощью которого мы можем отправлять, удалять и изменять сообщения. ```python=8 @bot.message_handler(commands=['start', 'help']) def send_welcome(message): bot.send_message(chat_id=message.chat.id, text='Привет! Отправь мне любой текст!') ``` C помощью декоратора `@bot.message_handler` мы указываем, что метод `send_welcome` будет вызываться только тогда, когда пользователь отправляет команду `/start` или `/help`. При этом, бот отправляет приветственное сообщение. ```python=13 @bot.message_handler(func=lambda message: True) def echo_message(message): bot.reply_to(message, message.text) ``` Также с помощью декоратора мы указываем, что любое другое сообщение будет просто отправлено обратно. ```python=18 bot.polling() ``` Запуск **polling**'a. Теперь бот будет переодически опрашивать сервера Telegram о наличии новых сообщений, и при наличии таковых, обрабатывать их. ## Задание №3: Счётчик слов в сообщении Модифицируйте простой эхо бот таким образом, чтобы при отправке команды `/start` он приветствовал пользователя по имени. При отправке любого другого сообщения, бот должен отправить число - количество слов в сообщении пользователя. Запустите и протестируйте бота. # Клавиатуры Иногда ответ пользователя можно представить в виде выбора между несколькими вариантами, которые известны боту заранее. Тогда, для удобства пользователя, мы можем создавать собственные клавиатуры, дающие пользователю этот выбор. Есть два типа клавиатур: * Пользовательские (кастомные) – клавиатуры, заменяющие пользователю основную клавиатуру * Встроенные (in-line) – клавиатуры, встроенные в сообщение. Могут обновлять сообщение и себя после нажатия ## Пользовательские клавиатуры Клавиатуры, заменяющие пользователю основную клавиатуру ![](https://i.imgur.com/xYlmDKT.jpg) :::info Как обрабатывать сообщение пользователя, посланное с помощью такой клавиатуры? Как обычное сообщение, ведь клавиатура просто посылает сообщение с текстом кнопки боту! ::: Импортируем фрагмент библиотеки: ```python= from telebot import types ``` Создаем клавиатуру и отправляем её: ```python=2 markup = types.ReplyKeyboardMarkup(row_width=3) btn3 = types.KeyboardButton('3') btn4 = types.KeyboardButton('4') btn5 = types.KeyboardButton('5') markup.add(btn3, btn4, btn5) bot.send_message(chat_id=chat_id, text='2 + 2 = ?', reply_markup=markup) ``` ## In-line клавиатуры Пользовательские клавиатуры, которые мы рассмотрели, чаще используются в случае, когда необходимо оставлять ее неизменной на протяжении многих сообщений. Встроенные (**in-line**) клавиатуры гораздо более удобны в случае необходимости адаптации выбора ответа под сообщение. ![](https://i.imgur.com/jgkAylN.png) Создаем клавиатуру и отправляем ее вместе с сообщением: ```python= markup = types.InlineKeyboardMarkup(row_width=3) btn1 = types.InlineKeyboardButton(text='Да!', callback_data='0') markup.add(btn1) bot.send_message(chat_id=chat_id, text='Ты человек?', reply_markup=markup) ``` При нажатии на in-line кнопку, сервера Telegram сообщат о новом событии - callback query. Посмотрим, как обрабатывать его: ```python= @bot.callback_query_handler(func=lambda call: call.data == '0') def reply(call): bot.edit_message_text(text="Ты человек!", chat_id=call.message.chat.id, message_id=call.message.message_id) ``` # Многопользовательские боты Большинство ботов не ограничиваются одним пользователем. При этом каждый пользователь в сложных ботах может находиться в разном состоянии – у каждого пользователя может быть разное сообщение на экране и разная клавиатура для ответа. Как подружить бота с несколькими пользователями сразу? ## Конечный автомат [Конечный автомат](https://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D0%BD%D0%B5%D1%87%D0%BD%D1%8B%D0%B9_%D0%B0%D0%B2%D1%82%D0%BE%D0%BC%D0%B0%D1%82) (Finite State Machine) - абстрактная машина, имеющая конечное количество **состояний**. В каждый момент времени конечный автомат может находиться только в одном состоянии. Существует множество **переходов**, изменяющих состояние конечного автомата в ответ на **внешние данные**. Также конечный автомат имеет **начальное состояние**. :::info Состояние -> Внешние данные -> Переход (Действие) -> Новое состояние ::: ## Простейший конечный автомат на примере пропускной системы (турникетов): ![](https://i.imgur.com/2R5UkMQ.png) Белыми кругами обозначены состояния, а стрелками обозначены переходы между состояниями. Черным кругом обозначено начальное состояние. ## Чат-бот как конечный автомат Рассмотрим пример бота, реализующего простую викторину: ![](https://i.imgur.com/qJTUlRz.png) Как мы видим, диалоговых ботов также можно рассматривать как конечные автоматы: * Действия бота определяются текущим состоянием пользователя, с которым бот ведет диалог * Переходы между состояниями происходят в следствии действий пользователя ## Telegram chat id **chat_id** – уникальный идентификатор диалога с пользователем. Хранить информацию о пользователе можно привязывая ее к **chat_id**. Для того чтобы бот корректно работал с несколькими пользователями сразу, можно хранить состояния каждого пользователя следующим образом: ```python states = {chat_id: state} ``` ## Пример использования **chat_id**: ```python= import telebot API_TOKEN = '<api_token>' bot = telebot.TeleBot(API_TOKEN) states = {} @bot.message_handler(commands=['start']) def send_welcome(message): chat_id = message.chat.id bot.send_message(chat_id=chat_id, text='Привет! Как тебя зовут?') if states.get(chat_id) is None: states.update({chat_id: 0}) else: states[chat_id] = 0 @bot.message_handler(func=lambda message: states.get(message.chat.id) == 0) def ask_age(message): chat_id = message.chat.id bot.send_message(chat_id=chat_id, text='А сколько тебе лет, %s?' % message.text) states[chat_id] = 1 @bot.message_handler(func=lambda message: states.get(message.chat.id) == 1) def finalize(message): chat_id = message.chat.id bot.send_message(chat_id=chat_id, text='Круто! Мне тоже %s лет!' % message.text) states[chat_id] = 2 @bot.message_handler(func=lambda message: states.get(message.chat.id) == 2) def propose_restart(message): chat_id = message.chat.id bot.send_message(chat_id=chat_id, text='Я тебя запомнил! Нажми /start чтобы познакомиться заново!') bot.polling() ``` Очевидная проблема такого хранения состояний - при выключении бота, все данные сотрутся. Поэтому, часто для хранения состояния пользователей и другой информации о пользователях используют базы данных (MongoDB, PostgreSQL, Sqlite и т.д.). Рассмотрим добавление состояния пользователей в БД. Для этого воспользуемся sqlite и [peewee](https://github.com/coleifer/peewee). ```python= # db.py import peewee db = peewee.SqliteDatabase('users.db') class User(peewee.Model): chat_id = peewee.IntegerField(unique=True) state = peewee.IntegerField(default=0) class Meta: database = db def init(): db.connect() db.create_tables([User], safe = True) db.close() def get_state(chat_id): user = User.get_or_none(chat_id=chat_id) if user is None: return None return user.state def set_state(chat_id, state): user, created = User.get_or_create(chat_id=chat_id) user.state = state user.save() init() ``` ```python= # bot.py import telebot from db import get_state, set_state API_TOKEN = '<api_token>' bot = telebot.TeleBot(API_TOKEN) @bot.message_handler(commands=['start']) def send_welcome(message): chat_id = message.chat.id bot.send_message(chat_id=chat_id, text='Привет! Как тебя зовут?') set_state(chat_id=chat_id, state=0) @bot.message_handler(func=lambda message: get_state(message.chat.id) == 0) def ask_age(message): chat_id = message.chat.id bot.send_message(chat_id=chat_id, text='А сколько тебе лет, %s?' % message.text) set_state(chat_id=chat_id, state=1) @bot.message_handler(func=lambda message: get_state(message.chat.id) == 1) def finalize(message): chat_id = message.chat.id bot.send_message(chat_id=chat_id, text='Круто! Мне тоже %s лет!' % message.text) set_state(chat_id=chat_id, state=2) @bot.message_handler(func=lambda message: get_state(message.chat.id) == 2) def propose_restart(message): chat_id = message.chat.id bot.send_message(chat_id=chat_id, text='Я тебя запомнил! Нажми /start чтобы познакомиться заново!') bot.polling() ``` ## Задание №4: Многопользовательский бот-викторина Реализуйте многопользовательского бота-викторину с 3-4 простыми вопросами. * По команде `/start` бот должен поприветствовать пользователя и сразу же задать первый вопрос. * При неправильном ответе пользователя, бот должен ответить, что пользователь ошибся и перезадать ему тот же вопрос * При правильном ответе от пользователя, бот должен сказать, что пользователь ответил на вопрос правильно и задать следующий вопрос * Когда пользователь ответил на все вопросы, бот должен поздравить пользователя и предложить ему пройти викторину заново, нажав на `/restart` --- Поделитесь с коллегами **@алиасами** ботов и проведите тестирование в режиме работы с несколькими пользователями одновременно. Добились ли мы желаемого результата? # Telegram бот в Docker контейнере Для того, чтобы упростить запуск и разработку Telegram бота, можно использовать Docker. Запуск бота в изолированном Docker контейнере гарантирует стабильное и предсказуемое поведения бота вне зависимости от окружения компьютера, на котором запущен бот. Пример Dockerfile для эхо-бота: ```dockerfile= FROM python:3.7-alpine COPY . echo-bot WORKDIR echo-bot RUN python3 -m pip install -r requirements.txt CMD python3 bot.py ``` Для создания docker образа, воспользуемся командой: ```bash docker build -t echo-bot . ``` Запуск docker контейнера: ```bash docker run -it -e TG_API_TOKEN=<api_token> echo-bot ``` Заметьте, что токен бота передается как переменная окружения, не забудьте добавить соответсвующий функционал в код бота: ```python= import os API_TOKEN = os.getenv('TG_API_TOKEN') ``` ## Задание №5: Бот-викторина в Docker контейнере Модифицируйте многопользовательского бота-викторину из [задания №4](#Задание-№4-Многопользовательский-бот-викторина) таким образом, чтобы бот запускался в Docker контейнере. Токен бота не должен фигурировать в коде вашего бота, а передаваться как переменная окружения. Запустите и удостоверьтесь, что бот работает корректно. # Webhook + Heroku Вспомним, чем отличается [polling](#Polling) и [webhook](#Webhook). ![](https://i.imgur.com/ABNSmaq.png) ## Поддерджка webhook Все наши боты до этого момента использовали polling, но в реальном мире чаще используют webhook. Посмотрим, как реализовать поддержку webhook в нашем простом эхо-боте: ```python= import flask import telebot import os TOKEN = os.getenv('TG_API_TOKEN') WEBHOOK_HOST = os.getenv('WEBHOOK_HOST') WEBHOOK_PORT = os.getenv('WEBHOOK_PORT') WEBHOOK_SSL_CERT = './cert/cert.pem' WEBHOOK_SSL_PRIV = './cert/key.pem' WEBHOOK_URL_BASE = "https://%s:%s" % (WEBHOOK_HOST, WEBHOOK_PORT) WEBHOOK_URL_PATH = "/%s/" % (API_TOKEN) bot = telebot.TeleBot(API_TOKEN) server = flask.Flask(__name__) @server.route(WEBHOOK_URL_PATH, methods=['POST']) def webhook(): if flask.request.headers.get('content-type') == 'application/json': json_string = flask.request.get_data().decode('utf-8') update = telebot.types.Update.de_json(json_string) bot.process_new_updates([update]) return '' else: flask.abort(403) @bot.message_handler(func=lambda message: True) def echo_message(message): bot.reply_to(message, message.text) if __name__ == '__main__': bot.remove_webhook() bot.set_webhook(url=WEBHOOK_URL_BASE + WEBHOOK_URL_PATH, certificate=open(WEBHOOK_SSL_CERT, 'r')) server.run(host='0.0.0.0', port=WEBHOOK_PORT, ssl_context=(WEBHOOK_SSL_CERT, WEBHOOK_SSL_PRIV)) ``` Для генерации сертификата и приватного ключа: ```bash openssl req -nodes -x509 -newkey rsa:4096 -keyout cert/key.pem -out cert/cert.pem -days 365 -subj '/CN=<host>' ``` ## Heroku Прблема наших предыдущих ботов в том, что они работали локально с компьютера, и при его выключении, бот переставал работать. Для решения этой проблемы используют удаленные сервера или схожие сервисы. Мы будем использовать сервис [Heroku](https://heroku.com), предоставляющий из себя облачную платформу, на которой можно бесплатно захостить бота. :::success Heroku использует https по умолчанию, поэтому можно не настраивать сертификаты! ::: ```python= import os from flask import Flask, request import telebot TOKEN = os.getenv('TG_API_TOKEN') bot = telebot.TeleBot(TOKEN) server = Flask(__name__) @bot.message_handler(commands=['start']) def start(message): bot.reply_to(message, 'Привет, %s!' % message.from_user.first_name) @bot.message_handler(func=lambda message: True, content_types=['text']) def echo_message(message): bot.reply_to(message, message.text) @server.route('/' + TOKEN, methods=['POST']) def get_message(): bot.process_new_updates([telebot.types.Update.de_json(request.stream.read().decode('utf-8'))]) return '', 200 if __name__ == '__main__': bot.remove_webhook() bot.set_webhook(url=os.getenv('WEBHOOK_URL') + TOKEN) server.run(host="0.0.0.0", port=int(os.getenv('PORT', 8443))) ``` Попробуем задеплоить нашего бота на Heroku. Для этого нам нужно: 1) Зарегистрировать аккаунт на heroku.com 2) Создать репозиторий на github.com со следующим содержимым: * Python скрипт с кодом бота * Файл с названием **Procfile**. Он нужен Heroku для того, чтобы понять какой именно скрипт нужно запустить. Содержимое Procfile: `web: python3 bot.py` * **requirements.txt** (Flask, pyTelegramBotAPI) 3) Создать новое приложение на Heroku 4) Во вкладке Deployment выбрать репозиторий на GitHub 5) Во вкладке Settings, в Config Vars указать токен бота и ссылку на приложение 6) Задеплоить бота в секции Manual Deploy во вкладке Deployment ## Задание №6: Бот с webhook на Heroku Разработайте Telegram бота, показывающего информацию о пользователе, отправившем сообщение. Для справки: [@userinfobot](https://tlgg.ru/userinfobot), который делает то же самое. # Inline боты Inline боты - это боты, которых не нужно добавлять в чат или писать им в ЛС. Чтобы вызвать его, можно в **любом** диалоге ввести его алиас в окне для сообщения, и он предложит вам варианты действий. Например, бот [**@youtube**](https://tlgg.ru/youtube) можно использовать в inline моде. При вводе **@алиаса** бота и поисковом запросе, бот предложит самые релевантные видео. ![](https://i.imgur.com/MrrbLSF.png) А при нажатии на одно из предложений, пользователь отправит сообщение от своего имени с пометкой "через **@youtube**". К такому сообщению также могут быть прикреплены кнопки. ![](https://i.imgur.com/sPwBFn2.png) Давайте разберемся, как реализовать похожий функционал. Для начала, в [@BotFather](https://tlgg.ru/BotFather) нужно активировать Inline mode для своего бота. Данная настройка находится в секции Bot Settings. ## Inline query Когда пользователь вводит текст после алиаса вашего бота, сервера Telegram оповещают вас о новом запросе - **inline query**. Чтобы обработать его с помощью Telebot, есть специальный декоратор `inline_handler`: ```python= @bot.inline_handler(func=lambda query: True) def answer_query(inline_query): text = inline_query.query # переменная text содержит в себе введенный текст ``` Далее, мы можем предложить пользователю возможные действия, они представляют из себя статьи - объекты класса `InlineQueryResultArticle` (доступны из telebot.types) Рассмотрим простой пример использования `InlineQueryResultArticle`: ```python= @bot.inline_handler(func=lambda query: 'alias' in query.query) def answer_alias_query(inline_query): alias_article = telebot.types.InlineQueryResultArticle( id='0', title='Отправить мой алиас', description='Нажав сюда, вы отправите в чат свой алиас!', input_message_content=telebot.types.InputTextMessageContent( message_text='Мой алиас: @%s' % inline_query.from_user.username ) ) bot.answer_inline_query( inline_query_id=inline_query.id, results=[alias_article], cache_time=0 ) ``` Параметр `results` в методе `answer_inline_query` хранит в себе список статей, которыми бот ответит пользователю. Что будет, если пользователь напишет **"send my username"**: ![](https://i.imgur.com/4oguiD8.png) ## Cache time Параметр `cache_time` в `answer_inline_query` отвечает за время, на которое сервера Telegram закэшируют ответ на запрос. Т.е. если пользователь ввёл "Send my alias", и бот ответил ему статьями, при следующем таком же запросе, сервера Telegram не отправят вашему боту новый inline query, а воспользуются ответом из кэша. Кэширование позволяет работать вашему боту быстрее и меньше нагружать сервер, но при разработке бота удобно использовать `cache_time=0`. Дэфолтное значение cache_time = 300 секунд. ## Switch to PM Иногда нужно сказать пользователю, чтобы он перешел в диалог с ботом. Например, чтобы он уточнил какую-нибудь информацию о себе и т.д. Для этого, в методе `answer_inline_query` есть параметр `switch_pm_text` и `switch_pm_parameter`. ![](https://i.imgur.com/oH8W2UI.png) Параметр `switch_pm_text` отвечает за текст кнопки. При нажатии на эту кнопку, пользователь перейдет в диалог с ботом, и вызовет команду `/start` с параметром, указанным в `switch_pm_parameter`. :::info Пользователь увидит только сообщение с текстом `/start`, а параметр будет доступен для обработки ботом. ::: ## Задание №7: Информация о пользователе в Inline mode Модифицируйте вашего бота из [задания №6](#Задание-№6-Бот-с-webhook-на-Heroku) таким образом, чтобы при упоминании его в диалоге, он предлагал вывести информацию о вас. При этом от лица пользователя (с пометкой *via @bot*) отправлялось сообщение с информацией о пользователе в том же формате, что и в [задании №6](#Задание-№6-Бот-с-webhook-на-Heroku). # Telegram speech-to-text bot Часто в группах люди записывают голосовые сообщения, но не всегда есть возможность послушать их (например, на совещании). Поэтому в Telegram существуют боты, которые переводят голосовые сообщения в текстовые. Один из таких ботов - [@voicybot](https://tlgg.ru/BotFather). После добавления в группу он будет транслировать любое голосовое сообщение в текстовое. ## Обработка медиафайлов Как получить голосовое сообщение пользователя: 1) Получить доступ к информации о голосовом сообщении, прикрепленном сообщении, используя `voice_info = bot.get_file(message.voice.file_id)` 2) Скачать файл (в виде байтов) с серверов Telegram: `voice_bytes = bot.download_file(voice_info.file_path)` 3) Записать байты в файл: ```python= with open('voice.ogg', 'wb') as file: file.write(voice_bytes) ``` ## Задание №8: Бот-облако Разработайте бота, который сохраняет голосовые сообщения, картинки и документы на диск компьтера в разные папки (voice, pics, docs). Протестируйте бота и проверьте, правильно ли он сохраняет файлы. ## SpeechRecognition Для того, чтобы распознавать речь, воспользуемся библиотекой [SpeechRecognition](https://pypi.org/project/SpeechRecognition/). Она поддерживает несколько API: - Google Speech Recognition - Microsoft Azure Speech - Wit.ai - IBM Speech to Text - и еще несколько других... Мы будем пользоваться Google Speech Recognition, т.к. его можно использовать бесплатно. ```python= import speech_recognition as sr recognizer = sr.Recognizer() with sr.AudioFile('file.wav') as source: audio = recognizer.record(source) text = recognizer.recognize_google(audio, language='ru-RU') ``` ## Задание №9: Распознавание голоса Напишите простую программу, которая умеет распознавать голос из аудиофайлов. Пример использования программы: ```bash recognize.py file1.wav file2.wav ... fileN.wav # output: Text1 Text2 Text3 ... TextN ``` ## Задание №10: Многопользовательский STT бот Разработайте бота, транслирующего голосовое сообщение в текстовое после добавления в группу. Проверьте работоспособность бота вместе с коллегами. 1) Бот должен использовать webhook 2) Бот должен быть задеплоен на Heroku 3) Для распознавания речи используйте модуль `SpeechRecognition` 4) При отправке голосового сообщения, бот сразу должен дать понять пользователю, что он работает (например, сообщением типа "Обрабатываю..."). После того, как голосовое сообщение распознано, бот должен изменить это сообщение на распознанный текст. Подсказки: 1) Чтобы бот видел сообщения в группе, разрешите ему делать это в @BotFather в секции "**Allow groups?**" 2) Для перевода .ogg файла в .wav файл используйте модуль [pydub](http://pydub.com/) ```python # convert .ogg voice file to .wav pydub.AudioSegment.from_ogg(voice_path_ogg).export(voice_path_wav, format='wav') ```

    Import from clipboard

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lost their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template is not available.
    All
    • All
    • Team
    No template found.

    Create a template

    Delete template

    Do you really want to delete this template?

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in via Google

    New to HackMD? Sign up

    Help

    Documents

    Tutorials
    YAML Metadata
    Slide Example
    Book Example

    Contacts

    Talk to us
    Report an issue
    Send us email

    Cheatsheet

    Example Syntax
    Header # Header
    • Unordered List
    - Unordered List
    1. Ordered List
    1. Ordered List
    • Todo List
    - [ ] Todo List
    Blockquote
    > Blockquote
    Bold font **Bold font**
    Italics font *Italics font*
    Strikethrough ~~Strikethrough~~
    19th 19^th^
    H2O H~2~O
    Inserted text ++Inserted text++
    Marked text ==Marked text==
    Link [link text](https:// "title")
    Image ![image alt](https:// "title")
    Code `Code`
    var i = 0;
    ```javascript
    var i = 0;
    ```
    :smile: :smile:
    Externals {%youtube youtube_id %}
    LaTeX $L^aT_eX$

    This is a alert area.

    :::info
    This is a alert area.
    :::

    Versions

    Versions and GitHub Sync

    Sign in to link this note to GitHub Learn more
    This note is not linked with GitHub Learn more
     
    Add badge Pull Push GitHub Link Settings

    Version named by    

    More Less
    • Edit
    • Delete

    Note content is identical to the latest version.
    Compare with
      Choose a version
      No search result
      Version not found

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub

        Please sign in to GitHub and install the HackMD app on your GitHub repo. Learn more

         Sign in to GitHub

        HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully