Разработка Telegram-бота для распознавания текста на изображениях, синтеза и распознавания аудио
Важно
Часть ресурсов, необходимых для прохождения практического руководства, доступны только в регионе Россия.
В этом руководстве вы создадите бота для Telegram, который умеет:
- синтезировать речь из текста сообщения с помощью API v1 сервиса Yandex SpeechKit;
- распознавать речь в голосовых сообщениях и преобразовывать ее в текст с помощью с помощью API синхронного распознавания сервиса Yandex SpeechKit;
- распознавать текст на изображениях с помощью сервиса Yandex Vision OCR.
Запросы от бота будет принимать API-шлюз Yandex API Gateway и перенаправлять их функции Yandex Cloud Functions для обработки.
Чтобы создать бота:
- Подготовьте облако к работе.
- Подготовьте ресурсы.
- Зарегистрируйте Telegram-бота.
- Создайте функцию.
- Создайте API-шлюз.
- Свяжите функцию и бота.
- Протестируйте бота.
Если созданные ресурсы вам больше не нужны, удалите их.
Перед началом работы
Зарегистрируйтесь в Yandex Cloud и создайте платежный аккаунт:
- Перейдите в консоль управления
, затем войдите в Yandex Cloud или зарегистрируйтесь. - На странице Yandex Cloud Billing
убедитесь, что у вас подключен платежный аккаунт, и он находится в статусеACTIVE
илиTRIAL_ACTIVE
. Если платежного аккаунта нет, создайте его и привяжите к нему облако.
Если у вас есть активный платежный аккаунт, вы можете создать или выбрать каталог, в котором будет работать ваша инфраструктура, на странице облака
Подробнее об облаках и каталогах.
Необходимые платные ресурсы
В стоимость поддержки Telegram-бота входят:
- плата за использование SpeechKit (см. тарифы для SpeechKit);
- плата за использование Vision OCR (см. тарифы для Vision OCR);
- плата за количество вызовов функции, вычислительные ресурсы, выделенные для выполнения функции, и исходящий трафик (см. тарифы для Cloud Functions);
- плата за количество запросов к созданному API-шлюзу и исходящий трафик (см. тарифы API Gateway).
Подготовьте ресурсы
-
Создайте сервисный аккаунт с именем
recognizer-bot-sa
и назначьте ему ролиai.editor
,functions.editor
на ваш каталог. -
Подготовьте ZIP-архив с кодом функции:
-
Создайте файл
index.py
и добавьте в него указанный ниже код.index.py
import logging import requests import telebot import json import os import base64 # Эндпоинты сервисов и данные для аутентификации API_TOKEN = os.environ['TELEGRAM_TOKEN'] vision_url = 'https://ocr.api.cloud.yandex.net/ocr/v1/recognizeText' speechkit_url = 'https://stt.api.cloud.yandex.net/speech/v1/stt:recognize' speechkit_synthesis_url = 'https://tts.api.cloud.yandex.net/speech/v1/tts:synthesize' folder_id = "" iam_token = '' logger = telebot.logger telebot.logger.setLevel(logging.INFO) bot = telebot.TeleBot(API_TOKEN, threaded=False) # Получение идентификатора каталога def get_folder_id(iam_token, version_id): headers = {'Authorization': f'Bearer {iam_token}'} function_id_req = requests.get(f'https://serverless-functions.api.cloud.yandex.net/functions/v1/versions/{version_id}', headers=headers) function_id_data = function_id_req.json() function_id = function_id_data['functionId'] folder_id_req = requests.get(f'https://serverless-functions.api.cloud.yandex.net/functions/v1/functions/{function_id}', headers=headers) folder_id_data = folder_id_req.json() folder_id = folder_id_data['folderId'] return folder_id def process_event(event): request_body_dict = json.loads(event['body']) update = telebot.types.Update.de_json(request_body_dict) bot.process_new_updates([update]) def handler(event, context): global iam_token, folder_id iam_token = context.token["access_token"] version_id = context.function_version folder_id = get_folder_id(iam_token, version_id) process_event(event) return { 'statusCode': 200 } # Обработчики команд и сообщений @bot.message_handler(commands=['help', 'start']) def send_welcome(message): bot.reply_to(message, "Бот умеет:\n*распознавать текст с картинок;\n* генерировать голосовые сообщения из текста;\n* переводить голосовые сообщения в текст.") @bot.message_handler(func=lambda message: True, content_types=['text']) def echo_message(message): global iam_token, folder_id with open('/tmp/audio.ogg', "wb") as f: for audio_content in synthesize(folder_id, iam_token, message.text): f.write(audio_content) voice = open('/tmp/audio.ogg', 'rb') bot.send_voice(message.chat.id, voice) @bot.message_handler(func=lambda message: True, content_types=['voice']) def echo_audio(message): file_id = message.voice.file_id file_info = bot.get_file(file_id) downloaded_file = bot.download_file(file_info.file_path) response_text = audio_analyze(speechkit_url, iam_token, folder_id, downloaded_file) bot.reply_to(message, response_text) @bot.message_handler(func=lambda message: True, content_types=['photo']) def echo_photo(message): file_id = message.photo[-1].file_id file_info = bot.get_file(file_id) downloaded_file = bot.download_file(file_info.file_path) image_data = base64.b64encode(downloaded_file).decode('utf-8') response_text = image_analyze(vision_url, iam_token, folder_id, image_data) bot.reply_to(message, response_text) # Распознавание изображения def image_analyze(vision_url, iam_token, folder_id, image_data): response = requests.post(vision_url, headers={'Authorization': 'Bearer '+iam_token, 'x-folder-id': folder_id}, json={ "mimeType": "image", "languageCodes": ["en", "ru"], "model": "page", "content": image_data }) blocks = response.json()['result']['textAnnotation']['blocks'] text = '' for block in blocks: for line in block['lines']: for word in line['words']: text += word['text'] + ' ' text += '\n' return text # Распознавание речи def audio_analyze(speechkit_url, iam_token, folder_id, audio_data): headers = {'Authorization': f'Bearer {iam_token}'} params = { "topic": "general", "folderId": f"{folder_id}", "lang": "ru-RU"} audio_request = requests.post(speechkit_url, params=params, headers=headers, data=audio_data) responseData = audio_request.json() response = 'error' if responseData.get("error_code") is None: response = (responseData.get("result")) return response # Синтез речи def synthesize(folder_id, iam_token, text): headers = { 'Authorization': 'Bearer ' + iam_token, } data = { 'text': text, 'lang': 'ru-RU', 'voice': 'filipp', 'folderId': folder_id } with requests.post(speechkit_synthesis_url, headers=headers, data=data, stream=True) as resp: if resp.status_code != 200: raise RuntimeError("Invalid response received: code: %d, message: %s" % (resp.status_code, resp.text)) for chunk in resp.iter_content(chunk_size=None): yield chunk
-
Создайте файл
requirements.txt
и укажите в нем библиотеку для работы с ботом.telebot
-
Добавьте оба файла в ZIP-архив
index.zip
.
-
Зарегистрируйте Telegram-бота
Зарегистрируйте бота в Telegram и получите токен.
-
Запустите бота BotFather
и отправьте ему команду:/newbot
-
В поле
name
укажите имя создаваемого бота. Это имя увидят пользователи при общении с ботом. -
В поле
username
укажите имя пользователя создаваемого бота. По имени пользователя можно будет найти бота в Telegram. Имя пользователя должно оканчиваться на...Bot
или..._bot
.В результате вы получите токен. Сохраните его — он потребуется в дальнейшем.
Создайте функцию
Создайте функцию, которая будет обрабатывать действия пользователей в чате.
-
В консоли управления
перейдите в каталог, в котором хотите создать функцию. -
В списке сервисов выберите Cloud Functions
-
Создайте функцию:
- Нажмите кнопку Создать функцию.
- Введите имя функции —
for-recognizer-bot
. - Нажмите кнопку Создать.
-
Создайте версию функции:
-
Выберите среду выполнения
Python
, отключите опцию Добавить файлы с примерами кода и нажмите кнопку Продолжить. -
Укажите способ загрузки
ZIP-архив
и выберите архивindex.zip
, который подготовили раньше. -
Укажите точку входа
index.handler
. -
В блоке Параметры укажите:
-
Таймаут, c —
30
. -
Память —
128 МБ
. -
Сервисный аккаунт —
recognizer-bot-sa
. -
Переменные окружения:
TELEGRAM_TOKEN
— токен вашего бота в Telegram.
-
-
Нажмите кнопку Сохранить изменения.
-
-
Создайте функцию
for-recognizer-bot
:yc serverless function create --name=for-recognizer-bot
Результат:
id: b09bhaokchn9******** folder_id: aoek49ghmknn******** created_at: "2023-03-21T10:03:37.475Z" name: for-recognizer-bot log_group_id: eolm8aoq9vcp******** http_invoke_url: https://functions.yandexcloud.net/b09bhaokchn9******** status: ACTIVE
-
Создайте версию функции
for-recognizer-bot
:yc serverless function version create \ --function-name for-recognizer-bot \ --memory=128m \ --execution-timeout=30s \ --runtime=python312 \ --entrypoint=index.handler \ --service-account-id=<идентификатор_сервисного_аккаунта> \ --environment TELEGRAM_TOKEN=<токен_бота> \ --source-path=./index.zip
Где:
--function-name
— имя функции, версия которой создается.--memory
— объем RAM.--execution-timeout
— максимальное время выполнения функции до таймаута.--runtime
— среда выполнения.--entrypoint
— точка входа.--service-account-id
— идентификатор сервисного аккаунтаrecognizer-bot-sa
.--environment
— переменные окружения.--source-path
— путь до ZIP-архиваindex.zip
.
Результат:
done (1s) id: d4e6qqlh53nu******** function_id: d4emc80mnp5n******** created_at: "2023-03-22T16:49:41.800Z" runtime: python312 entrypoint: index.handler resources: memory: "134217728" execution_timeout: 30s service_account_id: aje20nhregkc******** image_size: "4096" status: ACTIVE tags: - $latest log_group_id: ckgmc3l93cl0******** environment: TELEGRAM_TOKEN: <токен_бота> log_options: folder_id: b1g86q4m5vej********
-
Опишите в конфигурационном файле параметры функции:
resource "yandex_function" "for-recognizer-bot-function" { name = "for-recognizer-bot" user_hash = "first function" runtime = "python312" entrypoint = "index.handler" memory = "128" execution_timeout = "30" service_account_id = "aje20nhregkcvu******" environment = { TELEGRAM_TOKEN = <токен_бота> } content { zip_filename = "./index.zip" } }
Где:
name
— имя функции.user_hash
— произвольная строка, определяющая версию функции.runtime
— среда выполнения функции.entrypoint
— точка входа.memory
— объем памяти в мегабайтах, отведенный для выполнения функции.execution_timeout
— таймаут выполнения функции.service_account_id
— идентификатор сервисного аккаунтаrecognizer-bot-sa
.environment
— переменные окружения.content
— путь до ZIP-архиваindex.zip
с исходным кодом функции.
Более подробную информацию о параметрах ресурса
yandex_function
см. в документации провайдера . -
Проверьте корректность конфигурационных файлов.
-
В командной строке перейдите в папку, где вы создали конфигурационный файл.
-
Выполните проверку с помощью команды:
terraform plan
Если конфигурация описана верно, в терминале отобразится список создаваемых ресурсов и их параметров. Если в конфигурации есть ошибки, Terraform на них укажет.
-
-
Разверните облачные ресурсы.
-
Если в конфигурации нет ошибок, выполните команду:
terraform apply
-
Подтвердите создание функции: введите в терминал слово
yes
и нажмите Enter.
-
Чтобы создать функцию, воспользуйтесь методом create для ресурса Function или вызовом gRPC API FunctionService/Create.
Чтобы создать версию функцию, воспользуйтесь методом createVersion для ресурса Function или вызовом gRPC API FunctionService/CreateVersion.
Создайте API-шлюз
Сервер Telegram будет оповещать ваш бот о поступлении новых сообщений с помощью веб-хукаfor-recognizer-bot
для обработки.
-
В консоли управления
выберите каталог, в котором хотите создать API-шлюз. -
В списке сервисов выберите API Gateway.
-
Нажмите кнопку Создать API-шлюз.
-
В поле Имя введите
recognizer-bot-api-gw
. -
В блок Спецификация добавьте спецификацию:
openapi: 3.0.0 info: title: Sample API version: 1.0.0 paths: /for-recognizer-bot-function: post: x-yc-apigateway-integration: type: cloud_functions function_id: <идентификатор_функции> service_account_id: <идентификатор_сервисного_аккаунта> operationId: for-recognizer-bot-function
Где:
function_id
— идентификатор функцииfor-recognizer-bot
;service_account_id
— идентификатор сервисного аккаунтаrecognizer-bot-sa
.
-
Нажмите кнопку Создать.
-
Выберите созданный API-шлюз. Сохраните значение поля Служебный домен — оно потребуется в дальнейшем.
-
Сохраните следующую спецификацию в файл
spec.yaml
:openapi: 3.0.0 info: title: Sample API version: 1.0.0 paths: /for-recognizer-bot-function: post: x-yc-apigateway-integration: type: cloud_functions function_id: <идентификатор_функции> service_account_id: <идентификатор_сервисного_аккаунта> operationId: for-recognizer-bot-function
Где:
function_id
— идентификатор функцииfor-recognizer-bot
.service_account_id
— идентификатор сервисного аккаунтаrecognizer-bot-sa
.
-
Выполните команду:
yc serverless api-gateway create --name recognizer-bot-api-gw --spec=spec.yaml
Где:
--name
— имя API-шлюза.--spec
— файл со спецификацией.
Результат:
done (5s) id: d5d1ud9bli1e******** folder_id: b1gc1t4cb638******** created_at: "2023-09-25T16:01:48.926Z" name: recognizer-bot-api-gw status: ACTIVE domain: d5d1ud9bli1e********.apigw.yandexcloud.net log_group_id: ckgefpleo5eg******** connectivity: {} log_options: folder_id: b1gc1t4cb638********
Чтобы создать API-шлюз:
-
Опишите в конфигурационном файле параметры ресурса
yandex_api_gateway
:resource "yandex_api_gateway" "recognizer-bot-api-gw" { name = "recognizer-bot-api-gw" spec = <<-EOT openapi: 3.0.0 info: title: Sample API version: 1.0.0 paths: /for-recognizer-bot-function: post: x-yc-apigateway-integration: type: cloud_functions function_id: <идентификатор_функции> service_account_id: <идентификатор_сервисного_аккаунта> operationId: for-recognizer-bot-function EOT }
Где:
name
— имя API-шлюза.spec
— спецификация API-шлюза.
Более подробную информацию о параметрах ресурсов в Terraform см. в документации провайдера
. -
Проверьте корректность конфигурационных файлов.
-
В командной строке перейдите в папку, где вы создали конфигурационный файл.
-
Выполните проверку с помощью команды:
terraform plan
Если конфигурация описана верно, в терминале отобразится список создаваемых ресурсов и их параметров. Если в конфигурации есть ошибки, Terraform на них укажет.
-
-
Разверните облачные ресурсы.
-
Если в конфигурации нет ошибок, выполните команду:
terraform apply
-
Подтвердите создание ресурсов: введите в терминал слово
yes
и нажмите Enter.
-
Чтобы создать API-шлюз, воспользуйтесь методом REST API create для ресурса ApiGateway или вызовом gRPC API ApiGatewayService/Create.
Настройте связь между функцией и Telegram-ботом
Установите веб-хук для вашего Telegram-бота:
curl --request POST \
--url https://api.telegram.org/bot<токен_бота>/setWebhook \
--header 'content-type: application/json' \
--data '{"url": "<домен_API-шлюза>/for-recognizer-bot-function"}'
Где:
<токен_бота>
— токен Telegram-бота.<домен_API-шлюза>
— служебный домен API-шлюзаrecognizer-bot-api-gw
.
Результат:
{"ok":true,"result":true,"description":"Webhook was set"}
Протестируйте бота
Поговорите с ботом:
-
Откройте Telegram и найдите бота по указанному имени пользователя
username
. -
Отправьте в чат сообщение
/start
.Бот должен ответить:
Бот умеет: * распознавать текст с картинок; * генерировать голосовые сообщения из текста; * переводить голосовые сообщения в текст.
-
Отправьте в чат текстовое сообщение. Бот ответит голосовым сообщением, синтезированным из вашего текста.
-
Отправьте в чат голосовое сообщение. Бот ответит сообщением с текстом, распознанным из вашей речи.
-
Отправьте в чат изображение с текстом. Бот ответит сообщением с распознанным текстом.
Примечание
Изображение должно соответствовать требованиям.
Как удалить созданные ресурсы
Чтобы перестать платить за созданные ресурсы: