Создание бота Telegram может значительно расширить функциональность вашего обмена сообщениями, позволяя использовать все, от автоматических ответов до сложных взаимодействий. Это подробное руководство проведет вас через процесс создания базового бота Telegram с помощью Python, включая пользовательские команды, обработку сообщений и пользовательские клавиатуры. Мы будем использовать библиотеку python-telegram-bot
из-за ее простоты и мощных функций.
Необходимые условия
- Python 3.x установлен в вашей системе.
- Аккаунт в Telegram.
- Менеджер пакетов
pip
для установки библиотек Python.
Введение в бот для объявления о продаже автомобилей
В этой статье я продемонстрирую очень простой бот для объявления о продаже автомобилей, который предназначен для упрощения процесса сбора некоторой необходимой информации от пользователей, желающих выставить свои автомобили на продажу. Участвуя в структурированном разговоре, бот собирает такие данные, как тип, цвет и пробег автомобиля, и даже позволяет загружать фотографии, в результате чего создается резюме, подтверждающее детали объявления.
Ключевые особенности:
- Интерактивный поток разговоров для сбора информации об автомобиле.
- Встроенная клавиатура для удобного выбора параметров.
- Возможность загрузить фото автомобиля.
- Краткое изложение деталей объявления для подтверждения.
Шаг 1: Настройте своего бота Telegram
- Создайте своего бота: откройте Telegram и найдите аккаунт «BotFather». Начните беседу и используйте команду
/newbot
для создания нового бота. Следуйте инструкциям, чтобы настроить имя и имя пользователя бота. Затем BotFather предоставит вам токен, который имеет решающее значение для доступа к Telegram Bot API. Храните этот токен в надежном месте и не делитесь им.

2. Установите необходимые библиотеки: Установите python-telegram-bot
с помощью pip:
python3 -m pip install python-telegram-bot
Шаг 2: Создание бота с помощью Python
Теперь давайте углубимся в программирование вашего бота. Пожалуйста, создайте новый файл Python, например, my_telegram_bot
.py
и откройте его в вашем любимом текстовом редакторе. Затем выполните следующие действия, чтобы написать бота.
Импорт библиотек:
Начните с импорта необходимых модулей и настройки логирования, чтобы помочь в отладке:
import logging
from telegram import (ReplyKeyboardMarkup, ReplyKeyboardRemove, Update, InlineKeyboardButton, InlineKeyboardMarkup)
from telegram.ext import (Application, CallbackQueryHandler, CommandHandler, ContextTypes, ConversationHandler, MessageHandler, filters)
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO)
logger = logging.getLogger(__name__)
Определите состояния беседы:
Состояния в боте Telegram, особенно при использовании обработчика разговора, служат фреймворком для управления потоком взаимодействия между ботом и пользователем. По сути, они являются маркерами или контрольными точками, которые определяют, в какой части разговора пользователь участвует в данный момент, и определяют, что бот должен делать дальше, основываясь на вводе данных пользователем. Ниже приведен более общий обзор роли и функциональных возможностей состояний в управлении беседами ботов. Назначение и функционал состояний в Telegram-боте заключаются в следующем:
- Последовательное управление потоком: Состояния позволяют боту управлять последовательным потоком разговора. Переходя из одного состояния в другое, бот может провести пользователя через ряд шагов, вопросов или вариантов в логическом порядке.
- Контекстная осведомленность: Они помогают боту поддерживать контекст в разговоре. Зная текущее состояние, бот понимает, какая информация была предоставлена пользователем и какая информация еще необходима, что позволяет ему реагировать соответствующим образом.
- Обработка пользовательского ввода: В зависимости от текущего состояния бот может по-разному обрабатывать вводимые пользователем данные. Например, входные данные в состоянии «CAR_TYPE» будут пониматься как указание пользователем типа продаваемого автомобиля, в то время как те же входные данные в состоянии «CAR_COLOR» будут интерпретироваться как цвет автомобиля.
- Реализация условной логики: Состояния позволяют реализовать условную логику в диалоге. В зависимости от реакции или выбора пользователя бот может пропустить определенные состояния, повторить их или направить пользователя по другому пути общения.
- Обработка и повторение ошибок: Они облегчают обработку ошибок и повторение вопросов, если пользователь дает неожиданные или недействительные ответы. Отслеживая текущее состояние, бот может повторно запросить у пользователя информацию правильно.
- Постоянство состояния: В более сложных ботах состояния могут сохраняться и сохраняться между сеансами, что позволяет пользователям продолжить разговор с того места, на котором они остановились, даже если они временно покидают чат или если бот перезапускается.
Перечислим состояния, в которых наш бот будет управлять потоком:
CAR_TYPE, CAR_COLOR, CAR_MILEAGE_DECISION, CAR_MILEAGE, PHOTO, SUMMARY = range(6)
Реализуйте обработчики бесед:
Обработчики разговоров в ботах Telegram, особенно при использовании таких библиотек, как python-telegram-bot
, являются мощными инструментами, которые управляют потоком разговоров на основе вводимых пользователем данных и предопределенных состояний. Они имеют решающее значение для разработки ботов, требующих последовательности взаимодействий, таких как сбор информации, навигация пользователей по меню или выполнение команд в определенном порядке. Вот более подробный обзор того, как работают обработчики разговоров и их роль в разработке ботов:
Назначение и функциональные возможности:
- Управление состояниями разговора: Обработчики бесед отслеживают текущее состояние диалога с каждым пользователем. Они определяют, что бот должен делать дальше, на основе вводимых пользователем данных и текущего состояния, обеспечивая плавное и логичное прохождение различных этапов взаимодействия.
- Маршрутизация вводимых пользователем данных: Они направляют вводимые пользователем данные в различные функции обратного вызова в зависимости от текущего состояния. Это означает, что один и тот же ввод может привести к разным результатам в зависимости от того, на каком этапе потока разговора находится пользователь.
- Работа с командами и текстом: Обработчики бесед могут различать команды (например,
/start
или/help
) и обычные текстовые сообщения, что позволяет разработчикам указывать различные ответы или действия для каждого типа ввода. - Интеграция с клавиатурами и кнопками: Они безупречно работают с пользовательскими клавиатурами и встроенными кнопками, позволяя разработчикам создавать интерактивные и удобные интерфейсы в рамках разговора. Пользователи могут выбирать параметры или перемещаться по функциям бота с помощью этих элементов пользовательского интерфейса.
- Резервные варианты и тайм-ауты: Обработчики диалогов поддерживают резервные функции, которые могут срабатывать, когда пользователь отправляет неожиданные данные или когда разговор необходимо сбросить. Они также могут обрабатывать тайм-ауты, автоматически завершая разговор после периода бездействия.
Реализация обработчиков диалогов:
Реализация обработчика беседы обычно включает в себя определение точек входа, состояний и резервных вариантов:
- Точки входа: Это триггеры, с которых начинается разговор. Как правило, в качестве точки входа используется команда
/start
, но можно определить несколько точек входа для разных потоков беседы. - Государств: Как уже говорилось, состояния представляют разные точки разговора. Каждое состояние связано с одной или несколькими функциями обратного вызова, которые определяют поведение бота на этом этапе. Разработчики сопоставляют состояния с этими обратными вызовами, диктуя ход диалога.
- Запасные варианты: Резервные функции определяются для обработки непредвиденных ситуаций или для обеспечения способа выхода из диалога или его сброса. Распространенным резервным вариантом является команда
/cancel
, которая позволяет пользователям остановить разговор в любой момент.
Далее следует функция обработчика запуска
, которая инициирует диалог (точку входа), представляя пользователю выбор типов автомобилей:
def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
"""Starts the conversation and asks the user about their preferred car type."""
reply_keyboard = [['Sedan', 'SUV', 'Sports', 'Electric']]
await update.message.reply_text(
'<b>Welcome to the Car Sales Listing Bot!\n'
'Let\'s get some details about the car you\'re selling.\n'
'What is your car type?</b>',
parse_mode='HTML',
reply_markup=ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True, resize_keyboard=True),
)
return CAR_TYPE
Здесь вы можете ознакомиться с остальными обработчиками:
async def car_type(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
"""Stores the user's car type."""
user = update.message.from_user
context.user_data['car_type'] = update.message.text
cars = {"Sedan": "🚗", "SUV": "🚙", "Sport": "🏎️", "Electric": "⚡"}
logger.info('Car type of %s: %s', user.first_name, update.message.text)
await update.message.reply_text(
f'<b>You selected {update.message.text} car {cars[update.message.text]}.\n'
f'What color your car is?</b>',
parse_mode='HTML',
reply_markup=ReplyKeyboardRemove(),
)
# Define inline buttons for car color selection
keyboard = [
[InlineKeyboardButton('Red', callback_data='Red')],
[InlineKeyboardButton('Blue', callback_data='Blue')],
[InlineKeyboardButton('Black', callback_data='Black')],
[InlineKeyboardButton('White', callback_data='White')],
]
reply_markup = InlineKeyboardMarkup(keyboard)
await update.message.reply_text('<b>Please choose:</b>', parse_mode='HTML', reply_markup=reply_markup)
return CAR_COLOR
async def car_color(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
"""Stores the user's car color."""
query = update.callback_query
await query.answer()
context.user_data['car_color'] = query.data
await query.edit_message_text(
text=f'<b>You selected {query.data} color.\n'
f'Would you like to fill in the mileage for your car?</b>',
parse_mode='HTML'
)
# Define inline buttons for mileage decision
keyboard = [
[InlineKeyboardButton('Fill', callback_data='Fill')],
[InlineKeyboardButton('Skip', callback_data='Skip')],
]
reply_markup = InlineKeyboardMarkup(keyboard)
await query.message.reply_text('<b>Choose an option:</b>', parse_mode='HTML', reply_markup=reply_markup)
return CAR_MILEAGE_DECISION
async def car_mileage_decision(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
"""Asks the user to fill in the mileage or skip."""
query = update.callback_query
await query.answer()
decision = query.data
if decision == 'Fill':
await query.edit_message_text(text='<b>Please type in the mileage (e.g., 50000):</b>', parse_mode='HTML')
return CAR_MILEAGE
else:
await query.edit_message_text(text='<b>Mileage step skipped.</b>', parse_mode='HTML')
return await skip_mileage(update, context)
async def car_mileage(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
"""Stores the car mileage."""
context.user_data['car_mileage'] = update.message.text
await update.message.reply_text('<b>Mileage noted.\n'
'Please upload a photo of your car 📷, or send /skip.</b>',
parse_mode='HTML')
return PHOTO
async def skip_mileage(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
"""Skips the mileage input."""
context.user_data['car_mileage'] = 'Not provided'
text = '<b>Please upload a photo of your car 📷, or send /skip.</b>'
# Determine the correct way to send a reply based on the update type
if update.callback_query:
# If called from a callback query, use the callback_query's message
chat_id = update.callback_query.message.chat_id
await context.bot.send_message(chat_id=chat_id, text=text, parse_mode='HTML')
# Optionally, you might want to acknowledge the callback query
await update.callback_query.answer()
elif update.message:
# If called from a direct message
await update.message.reply_text(text)
else:
# Handle other cases or log an error/warning
logger.warning('skip_mileage was called without a message or callback_query context.')
return PHOTO
async def photo(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
"""Stores the photo."""
photo_file = await update.message.photo[-1].get_file()
# Correctly store the file_id of the uploaded photo for later use
context.user_data['car_photo'] = photo_file.file_id # Preserve this line
# Inform user and transition to summary
await update.message.reply_text('<b>Photo uploaded successfully.\n'
'Let\'s summarize your selections.</b>',
parse_mode='HTML'
)
await summary(update, context) # Proceed to summary
async def skip_photo(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
"""Skips the photo upload."""
await update.message.reply_text('<b>No photo uploaded.\n'
'Let\'s summarize your selections.</b>',
parse_mode='HTML')
await summary(update, context)
async def summary(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
"""Summarizes the user's selections and ends the conversation, including the uploaded image."""
selections = context.user_data
# Construct the summary text
summary_text = (f"<b>Here's what you told me about your car:\n</b>"
f"<b>Car Type:</b> {selections.get('car_type')}\n"
f"<b>Color:</b> {selections.get('car_color')}\n"
f"<b>Mileage:</b> {selections.get('car_mileage')}\n"
f"<b>Photo:</b> {'Uploaded' if 'car_photo' in selections else 'Not provided'}")
chat_id = update.effective_chat.id
# If a photo was uploaded, send it back with the summary as the caption
if 'car_photo' in selections and selections['car_photo'] != 'Not provided':
await context.bot.send_photo(chat_id=chat_id, photo=selections['car_photo'], caption=summary_text, parse_mode='HTML')
else:
# If no photo was uploaded, just send the summary text
await context.bot.send_message(chat_id=chat_id, text=summary_text, parse_mode='HTML')
return ConversationHandler.END
async def cancel(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
"""Cancels and ends the conversation."""
await update.message.reply_text('Bye! Hope to talk to you again soon.', reply_markup=ReplyKeyboardRemove())
return ConversationHandler.END
Основная функция и опрос ботов
В функции main
настройте Application
и ConversationHandler
, включая точки входа, состояния и резервные варианты. Запустите бота с опросом, чтобы прослушивать обновления:
def main() -> None:
"""Run the bot."""
application = Application.builder().token("YOUR TOKEN HERE").build()
conv_handler = ConversationHandler(
entry_points=[CommandHandler('start', start)],
states={
CAR_TYPE: [MessageHandler(filters.TEXT & ~filters.COMMAND, car_type)],
CAR_COLOR: [CallbackQueryHandler(car_color)],
CAR_MILEAGE_DECISION: [CallbackQueryHandler(car_mileage_decision)],
CAR_MILEAGE: [MessageHandler(filters.TEXT & ~filters.COMMAND, car_mileage)],
PHOTO: [
MessageHandler(filters.PHOTO, photo),
CommandHandler('skip', skip_photo)
],
SUMMARY: [MessageHandler(filters.ALL, summary)]
},
fallbacks=[CommandHandler('cancel', cancel)],
)
application.add_handler(conv_handler)
# Handle the case when a user sends /start but they're not in a conversation
application.add_handler(CommandHandler('start', start))
application.run_polling()
Запустите бота:
Завершите свой скрипт вызовом функции main
. Запустите бота, выполнив скрипт Python в терминале.
Здесь вы можете найти весь код:
import logging
from telegram import (ReplyKeyboardMarkup, ReplyKeyboardRemove, Update,
InlineKeyboardButton, InlineKeyboardMarkup)
from telegram.ext import (Application, CallbackQueryHandler, CommandHandler,
ContextTypes, ConversationHandler, MessageHandler, filters)
# Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logger = logging.getLogger(__name__)
# Define states
CAR_TYPE, CAR_COLOR, CAR_MILEAGE_DECISION, CAR_MILEAGE, PHOTO, SUMMARY = range(6)
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
"""Starts the conversation and asks the user about their preferred car type."""
reply_keyboard = [['Sedan', 'SUV', 'Sports', 'Electric']]
await update.message.reply_text(
'<b>Welcome to the Car Sales Listing Bot!\n'
'Let\'s get some details about the car you\'re selling.\n'
'What is your car type?</b>',
parse_mode='HTML',
reply_markup=ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True, resize_keyboard=True),
)
return CAR_TYPE
async def car_type(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
"""Stores the user's car type."""
user = update.message.from_user
context.user_data['car_type'] = update.message.text
cars = {"Sedan": "🚗", "SUV": "🚙", "Sports": "🏎️", "Electric": "⚡"}
logger.info('Car type of %s: %s', user.first_name, update.message.text)
await update.message.reply_text(
f'<b>You selected {update.message.text} car {cars[update.message.text]}.\n'
f'What color your car is?</b>',
parse_mode='HTML',
reply_markup=ReplyKeyboardRemove(),
)
# Define inline buttons for car color selection
keyboard = [
[InlineKeyboardButton('Red', callback_data='Red')],
[InlineKeyboardButton('Blue', callback_data='Blue')],
[InlineKeyboardButton('Black', callback_data='Black')],
[InlineKeyboardButton('White', callback_data='White')],
]
reply_markup = InlineKeyboardMarkup(keyboard)
await update.message.reply_text('<b>Please choose:</b>', parse_mode='HTML', reply_markup=reply_markup)
return CAR_COLOR
async def car_color(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
"""Stores the user's car color."""
query = update.callback_query
await query.answer()
context.user_data['car_color'] = query.data
await query.edit_message_text(
text=f'<b>You selected {query.data} color.\n'
f'Would you like to fill in the mileage for your car?</b>',
parse_mode='HTML'
)
# Define inline buttons for mileage decision
keyboard = [
[InlineKeyboardButton('Fill', callback_data='Fill')],
[InlineKeyboardButton('Skip', callback_data='Skip')],
]
reply_markup = InlineKeyboardMarkup(keyboard)
await query.message.reply_text('<b>Choose an option:</b>', parse_mode='HTML', reply_markup=reply_markup)
return CAR_MILEAGE_DECISION
async def car_mileage_decision(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
"""Asks the user to fill in the mileage or skip."""
query = update.callback_query
await query.answer()
decision = query.data
if decision == 'Fill':
await query.edit_message_text(text='<b>Please type in the mileage (e.g., 50000):</b>', parse_mode='HTML')
return CAR_MILEAGE
else:
await query.edit_message_text(text='<b>Mileage step skipped.</b>', parse_mode='HTML')
return await skip_mileage(update, context)
async def car_mileage(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
"""Stores the car mileage."""
context.user_data['car_mileage'] = update.message.text
await update.message.reply_text('<b>Mileage noted.\n'
'Please upload a photo of your car 📷, or send /skip.</b>',
parse_mode='HTML')
return PHOTO
async def skip_mileage(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
"""Skips the mileage input."""
context.user_data['car_mileage'] = 'Not provided'
text = '<b>Please upload a photo of your car 📷, or send /skip.</b>'
# Determine the correct way to send a reply based on the update type
if update.callback_query:
# If called from a callback query, use the callback_query's message
chat_id = update.callback_query.message.chat_id
await context.bot.send_message(chat_id=chat_id, text=text, parse_mode='HTML')
# Optionally, you might want to acknowledge the callback query
await update.callback_query.answer()
elif update.message:
# If called from a direct message
await update.message.reply_text(text)
else:
# Handle other cases or log an error/warning
logger.warning('skip_mileage was called without a message or callback_query context.')
return PHOTO
async def photo(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
"""Stores the photo."""
photo_file = await update.message.photo[-1].get_file()
# Correctly store the file_id of the uploaded photo for later use
context.user_data['car_photo'] = photo_file.file_id # Preserve this line
# Inform user and transition to summary
await update.message.reply_text('<b>Photo uploaded successfully.\n'
'Let\'s summarize your selections.</b>',
parse_mode='HTML'
)
await summary(update, context) # Proceed to summary
async def skip_photo(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
"""Skips the photo upload."""
await update.message.reply_text('<b>No photo uploaded.\n'
'Let\'s summarize your selections.</b>',
parse_mode='HTML')
await summary(update, context)
async def summary(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
"""Summarizes the user's selections and ends the conversation, including the uploaded image."""
selections = context.user_data
# Construct the summary text
summary_text = (f"<b>Here's what you told me about your car:\n</b>"
f"<b>Car Type:</b> {selections.get('car_type')}\n"
f"<b>Color:</b> {selections.get('car_color')}\n"
f"<b>Mileage:</b> {selections.get('car_mileage')}\n"
f"<b>Photo:</b> {'Uploaded' if 'car_photo' in selections else 'Not provided'}")
chat_id = update.effective_chat.id
# If a photo was uploaded, send it back with the summary as the caption
if 'car_photo' in selections and selections['car_photo'] != 'Not provided':
await context.bot.send_photo(chat_id=chat_id, photo=selections['car_photo'], caption=summary_text, parse_mode='HTML')
else:
# If no photo was uploaded, just send the summary text
await context.bot.send_message(chat_id=chat_id, text=summary_text, parse_mode='HTML')
return ConversationHandler.END
async def cancel(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
"""Cancels and ends the conversation."""
await update.message.reply_text('Bye! Hope to talk to you again soon.', reply_markup=ReplyKeyboardRemove())
return ConversationHandler.END
def main() -> None:
"""Run the bot."""
application = Application.builder().token("YOUR TOKEN HERE").build()
conv_handler = ConversationHandler(
entry_points=[CommandHandler('start', start)],
states={
CAR_TYPE: [MessageHandler(filters.TEXT & ~filters.COMMAND, car_type)],
CAR_COLOR: [CallbackQueryHandler(car_color)],
CAR_MILEAGE_DECISION: [CallbackQueryHandler(car_mileage_decision)],
CAR_MILEAGE: [MessageHandler(filters.TEXT & ~filters.COMMAND, car_mileage)],
PHOTO: [
MessageHandler(filters.PHOTO, photo),
CommandHandler('skip', skip_photo)
],
SUMMARY: [MessageHandler(filters.ALL, summary)]
},
fallbacks=[CommandHandler('cancel', cancel)],
)
application.add_handler(conv_handler)
# Handle the case when a user sends /start but they're not in a conversation
application.add_handler(CommandHandler('start', start))
application.run_polling()
if __name__ == '__main__':
main()
Шаг 3: Тестирование и взаимодействие с ботом
После запуска скрипта найдите своего бота в Telegram и начните взаимодействовать с ним. Теперь вы должны иметь возможность использовать команду /start
для начала разговора, который поможет вам выставить автомобиль на продажу.








Заключение:
Вы только что расширили свой Telegram-бот, добавив в него обработку текстовых сообщений и интерактивные кнопки, что сделало его гораздо более привлекательным. Это лишь малая часть того, что возможно с библиотекой python-telegram-bot
. По мере дальнейшего изучения вы найдете варианты обработки различных типов контента, интеграции с внешними API и многое другое. Погрузитесь в документацию библиотеки, чтобы узнать обо всех возможностях вашего нового Telegram-бота.
Удачного программирования и приятного воплощения в жизнь вашего Telegram-бота!
Ваша поддержка значит очень многое! 🙌
Если вам понравилась эта статья и вы нашли ее ценной, пожалуйста, подумайте о том, чтобы похлопать в ладоши, чтобы выразить свою поддержку. Не стесняйтесь изучать другие мои статьи, где я освещаю широкий спектр тем, связанных с программированием на Python и другими. Подписавшись на меня, вы будете в курсе моего последнего контента и идей. Я с нетерпением жду возможности поделиться с вами новыми знаниями и связаться с вами в будущих статьях. А пока продолжайте программировать, продолжайте учиться и, самое главное, наслаждайтесь путешествием!
Удачного программирования!