Как ждать ответ от пользователя Aiogram Telegram Bot
У меня есть Телеграм бот на Python на библиотеке Aiogram, которому мне надо написать команду, после которой он будет ждать следующее сообщение от меня. То есть к примеру я пишу боту «Привет», он пишет мне «Введите имя» и после чего бот должен ждать сообщение от меня. Соответственно, я не могу через if проверять сообщение, ведь я не знаю, что напишет пользователь. Так, человек должен попасть в некий цикл, внутри которого не будут реагировать другие команды, он выйдет из него только когда напишет имя. Я пытался создать другой обработчик сообщений внутри основного, но всё тщетно.
Отслеживать
задан 4 авг 2020 в 14:34
Данил Переверзев Данил Переверзев
51 1 1 золотой знак 1 1 серебряный знак 4 4 бронзовых знака
2 ответа 2
Сортировка: Сброс на вариант по умолчанию
У aiogram есть готовая машина состояний и заполняемые формы
import aiogram.utils.markdown as md from aiogram import Bot, Dispatcher, types from aiogram.contrib.fsm_storage.memory import MemoryStorage from aiogram.dispatcher import FSMContext from aiogram.dispatcher.filters import Text from aiogram.dispatcher.filters.state import State, StatesGroup from aiogram.types import ParseMode from aiogram.utils import executor bot = Bot(token=API_TOKEN) storage = MemoryStorage() dp = Dispatcher(bot, storage=storage) # создаём форму и указываем поля class Form(StatesGroup): name = State() age = State() gender = State() # Начинаем наш диалог @dp.message_handler(commands=['start']) async def cmd_start(message: types.Message): await Form.name.set() await message.reply("Привет! Как тебя зовут?") # Добавляем возможность отмены, если пользователь передумал заполнять @dp.message_handler(state='*', commands='cancel') @dp.message_handler(Text(equals='отмена', ignore_case=True), state='*') async def cancel_handler(message: types.Message, state: FSMContext): current_state = await state.get_state() if current_state is None: return await state.finish() await message.reply('ОК') # Сюда приходит ответ с именем @dp.message_handler(state=Form.name) async def process_name(message: types.Message, state: FSMContext): async with state.proxy() as data: data['name'] = message.text await Form.next() await message.reply("Сколько тебе лет?") # Проверяем возраст @dp.message_handler(lambda message: not message.text.isdigit(), state=Form.age) async def process_age_invalid(message: types.Message): return await message.reply("Напиши возраст или напиши /cancel") # Принимаем возраст и узнаём пол @dp.message_handler(lambda message: message.text.isdigit(), state=Form.age) async def process_age(message: types.Message, state: FSMContext): await Form.next() await state.update_data(age=int(message.text)) markup = types.ReplyKeyboardMarkup(resize_keyboard=True, selective=True) markup.add("М", "Ж") markup.add("Другое") await message.reply("Укажи пол (кнопкой)", reply_markup=markup) # Проверяем пол @dp.message_handler(lambda message: message.text not in ["М", "Ж", "Другое"], state=Form.gender) async def process_gender_invalid(message: types.Message): return await message.reply("Не знаю такой пол. Укажи пол кнопкой на клавиатуре") # Сохраняем пол, выводим анкету @dp.message_handler(state=Form.gender) async def process_gender(message: types.Message, state: FSMContext): async with state.proxy() as data: data['gender'] = message.text markup = types.ReplyKeyboardRemove() await bot.send_message( message.chat.id, md.text( md.text('Hi! Nice to meet you,', md.bold(data['name'])), md.text('Age:', md.code(data['age'])), md.text('Gender:', data['gender']), sep='\n', ), reply_markup=markup, parse_mode=ParseMode.MARKDOWN, ) await state.finish() if __name__ == '__main__': executor.start_polling(dp, skip_updates=True)
Пишем бота для принятия решений
Представьте, вы работаете на очень ответственной должности в банке: вы принимаете самые важные стратегические решения в компании. Но вы так давно не были в отпуске, ведь каждый день нужно отвечать стольким сотрудникам… Вот бы вас кто-нибудь подменил…
В этом туториале вы напишете телеграм-бота для принятия особо важных стратегических решений:
Перед тем как начать…
Очень советуем не просто читать этот туториал, а проходить его вместе с нами. Копируйте кусочки кода из него и запускайте их по мере прочтения текста. Так вы поймёте происходящее гораздо лучше.
1. Создайте бота
Пользоваться будем библиотекой ptbot . Перейдите в его документацию и следуйте инструкциям в разделе “Как начать пользоваться”.
Если вы всё сделали правильно, то от бота в Telegram прилетит сообщение:
2. Заставьте бота говорить
Сейчас бот сообщает о своём запуске, а после этого замолкает. Какой-то он не слишком разговорчивый, надо его разговорить! Разберёмся что заставляет его отправить сообщение о запуске. Похоже дело в этой строчке:
bot.send_message(TG_CHAT_ID, "Бот запущен")
Давайте её менять:
import ptbot TG_TOKEN = '958423683:AAEAtJ5Lde5YYfu8GldVhSGCAsxAYbzUIYg' # подставьте свой ключ API TG_CHAT_ID = '228593533' # подставьте свой ID bot = ptbot.Bot(TG_TOKEN) bot.send_message(TG_CHAT_ID, "Привет!") bot.send_message(TG_CHAT_ID, "Как дела?")
Круто, похоже, она и правда отвечает за отправку сообщений:
3. Научите бота принимать решения
Бот умеет присылать заготовленный текст. А как сделать так, чтобы он отвечал пользователям разными сообщениями? Для этого пригодится библиотека random . Её устанавливать не нужно, она встроена в Python:
import random answers = ("да", "нет", "это возможно") choice = random.choice(answers) print("Думаю, ", choice)
В переменной answers лежит сразу 3 строки. Такой приём называется упаковка. Она передаётся в функцию random.choice , и та случайным образом выбирает одну из них. Случайный ответ складывается в переменную choice . Попробуйте запустить этот код несколько раз и вы будете получать разные результаты:
Хорошо, выбор есть. Теперь его можно отправить пользователю:
import random answers = ("да", "нет", "это возможно") choice = random.choice(answers) bot.send_message(TG_CHAT_ID, "Думаю, ", choice)
Похоже, этот код с ошибкой! Вот что он выводит:
TypeError: send_message() takes 3 positional arguments but 4 were given
Это ошибка, на которую часто натыкаются начинающие питонисты. Дело в том, что команда print() принимает любое количество аргументов: сколько вы ей передадите, столько она и выведет. Пример:
print("Один аргумент") print("Два", "аргумента") print("Т", "р", "и")
А вот функция send_message принимает строго два: chat_id и текст сообщения.
Чтобы исправить ошибку, “склейте” свои кусочки сообщения в одну переменную перед отправкой:
message = "Думаю, <>".format(choice) bot.send_message(TG_CHAT_ID, message)
Вот что получилось:
4. Соберите код принятия решения в функцию
Теперь ваш бот умеет принимать решения. Но чтобы он это делал, его нужно запускать несколько раз. Жать на зелёную кнопку в Repl – занятие не очень интересное, хорошо бы его автоматизировать. Поможет функция:
def choose(): answers = ("да", "нет", "это возможно") choice = random.choice(answers) message = "Думаю, <>".format(choice) bot.send_message(TG_CHAT_ID, message)
choose() – это функция. Такая же, как print() или random.choice() , только здесь вы создаёте её самостоятельно.
После того, как вы объявили функцию, вы можете её вызывать. Так же, как вы делали это с print() , т.е. просто напишите её название и поставьте скобки:
def choose(): answers = ("да", "нет", "это возможно") choice = random.choice(answers) message = "Думаю, <>".format(choice) bot.send_message(TG_CHAT_ID, message) choose() choose()
Бот пришлёт сразу 2 сообщения:
Чтобы создать новую функцию, нужно не так-то много действий. Каждая функция начинается со слова def . Это сокращение от define , т.е. “определить”. Так код понимает разницу между созданием функции и её использованием:
def choose(): # Это создание ... choose() # Это использование
Второе правило: весь код функции пишется с отступом в 4 пробела:
Конечно, не обязательно жать 4 раза на клавишу “пробел”. То же самое делает клавиша TAB на левом крае клавиатуры.
Последний нюанс. Всё, что происходит в функции, остаётся внутри неё. Например, все переменные, которые мы создали во время работы функции, окажутся недоступны извне:
def choose(): answers = ("да", "нет", "это возможно") choice = random.choice(answers) message = "Думаю, <>".format(choice) bot.send_message(TG_CHAT_ID, message) print(answers) # NameError: name 'answers' is not defined
То есть всё, что внутри функции, остальному коду недоступно.
Функция – это отдельный кусочек кода, который можно запустить попозже
5. Сделайте выбор неслучайным
Сейчас бот случайным образом выбирает между “да”, “нет” и “возможно”. Позже вы сможете прокачать бота, чтобы он выдавал более осознанные ответы. Но всё равно по коже пробегают мурашки: что он там сейчас наотвечает? И ведь вы даже не можете понять какие из ответов ваши, а какие принадлежат боту! Надо что-то придумать, чтобы бот записывал что и кому он ответил.
Ну давайте поступим по-простому: пусть функция выводит в консоль кто и что спрашивал у бота:
def choose(): answers = ("да", "нет", "это возможно") choice = random.choice(answers) message = "Думаю, <>".format(choice) bot.send_message(TG_CHAT_ID, message) print("Что сюда писать. ") # choose()
print() понятно, а дальше что. Что в него писать. В функции нет таких переменных… Чтобы они появились, нужно добавить функции аргументы:
def choose(chat_id, question): answers = ("да", "нет", "это возможно") choice = random.choice(answers) message = "Думаю, <>".format(choice) bot.send_message(TG_CHAT_ID, message) print("Мне написал пользователь с ID:", chat_id) print("Он спрашивал:", question) print("Я ответил:", message)
Теперь функция получает 2 аргумента: ID пользователя в Telegram и его вопрос. А вот как функцией теперь пользоваться:
choose(TG_CHAT_ID, "Привет! Можно мне повысить зарплату?")
Если сделать вот так, по-старинке:
choose()
Вы получите ошибку:
TypeError: choose() takes 2 positional arguments but 0 was given
Знакомая ошибка! Вы сталкивались с ней в пункте 3 этого туториала. Только теперь вы недодаёте функции аргументов, ведь теперь она принимает сразу 2!
Последний тюнинг: разрешим боту общаться с другими пользователями. Для этого в методе send_message заменим TG_CHAT_ID на chat_id. TG_CHAT_ID – это ваш id, поэтому бот мог писать только вам. Вместо него мы передадим методу chat_id , это id любого, кто пишет боту. Этот id мы передаем при обращении к функции choose:
def choose(chat_id, question): answers = ("да", "нет", "это возможно") choice = random.choice(answers) message = "Думаю, <>".format(choice) bot.send_message(chat_id, message) # print("Мне написал пользователь с ID:", chat_id) print("Он спрашивал:", question) print("Я ответил:", message) choose(TG_CHAT_ID, "Привет! Можно мне повысить зарплату?")
6. Подключите функцию к боту
У нас есть функция, которая принимает решения. Но бот всё ещё не реагирует на сообщения в чате и отвечает только вам. Просто пишет всякие сообщения, когда вы жмёте на кропку Run . Как заставить его ждать сообщений, а уже после этого отвечать?
Такая функция есть в ptbot :
def choose(chat_id, question): answers = ("да", "нет", "это возможно") choice = random.choice(answers) message = "Думаю, <>".format(choice) bot.send_message(chat_id, message) print("Мне написал пользователь с ID:", chat_id) print("Он спрашивал:", question) print("Я ответил:", message) bot = ptbot.Bot(TG_TOKEN) bot.reply_on_message(choose) # bot.run_bot() #
Это новый способ пользоваться функциями, вы наверняка так ещё не делали. Вы передали функцию как аргумент другой функции:
bot.reply_on_message(choose)
Чтобы лучше понимать что произошло, давайте посмотрим на такой код:
print(print)
Выведется что-то новенькое: . Вы вывели саму функцию принт! Так можно делать с любыми функциями:
print(random.choice)
Теперь выведется вот такое колдунство:
bound method Random.choice of random.Random object at 0x008D71E8>>
Название функции – это переменная, в которой лежит функция
Этой переменной можно орудовать как любой другой. Передавать как аргумент в другие функции, или класть в другие переменные:
a = print a("Привет, мир!")
Теперь в переменной a лежит функция print и она может делать то же самое, что и print , стоит лишь подставить круглые скобки справа от неё.
Так вот, вернёмся к нашему коду:
def choose(chat_id, question): answers = ("да", "нет", "это возможно") choice = random.choice(answers) message = "Думаю, <>".format(choice) bot.send_message(chat_id, message) print("Мне написал пользователь с ID:", chat_id) print("Он спрашивал:", question) print("Я ответил:", message) bot = ptbot.Bot(TG_TOKEN) bot.reply_on_message(choose) # bot.run_bot()
Здесь вы передаёте в метод bot.reply_on_message вашу функцию choose :
bot.reply_on_message(choose)
Метод reply_on_message будет ждать до тех пор, пока боту кто-нибудь не напишет. Как только боту напишет человек, reply_on_message запустит вашу функцию choose , вот так: choose(chat_id, question) . То есть теперь функцию запустят за вас, вам самим её вызывать не нужно.
На гифке ниже можно посмотреть как теперь работает бот. Теперь он отвечает только после того, как вы ему написали:
7. Создайте иллюзию раздумий
Бот отвечает мгновенно, не задумываясь. Люди так не делают, все сразу поймут, что отвечает бот. Пусть бот немного ждёт перед ответом, чтобы все думали, что это вы
В ptbot есть метод, отвечающий за таймеры, с помощью таймера можно сделать эту задержку:
def notify(): print("Прошло 5 секунд!") bot = ptbot.Bot(TG_TOKEN) bot.create_timer(5, notify) bot.run_bot()
Если запустить такой код, то таймер запустится сразу же, как только вы нажали Run . Когда пройдёт 5 секунд, запустится функция notify . Она выведет в консоль, что 5 секунд прошло.
Вам нужен другой алгоритм:
- В самом начале бот ждёт сообщений от пользователя.
- Если боту написали, он ставит таймер на 5 секунд
- Когда таймер прошёл, бот запустит функцию choose
А вот как это сделать:
def wait(): bot.create_timer(5, choose) def choose(chat_id, question): answers = ("да", "нет", "это возможно") choice = random.choice(answers) message = "Думаю, <>".format(choice) bot.send_message(chat_id, message) print("Мне написал пользователь с ID:", chat_id) print("Он спрашивал:", question) print("Я ответил:", message) bot = ptbot.Bot(TG_TOKEN) bot.reply_on_message(wait) bot.run_bot()
Когда боту пишут, он запускает функцию wait . Она ничего никому не пишет, а ставит таймер на 5 секунд.
Когда таймер пройдёт, запустится функция choose . Она запустится потому что мы передали её в метод bot.create_timer(5, choose) .
Эта функция сделает то же самое, что и раньше: выберет случайный вариант ответа и отправит сообщение пользователю.
Запускаем… Опа, ошибка!
TypeError: wait() takes 0 positional arguments but 2 were given
Знакомый TypeError , снова функция хотела меньше аргументов, чем получила.
Когда вы передали функцию wait в reply_on_message , он подсунул в неё 2 аргумента: chat_id, question . Нужно указать их при объявлении def wait(): :
def wait(chat_id, question): bot.create_timer(5, choose)
Запускаем ещё раз… Теперь бот подождал 5 секунд и снова выдал TypeError :
TypeError: choose() missing 2 required positional arguments: 'chat_id' and 'question'
На этот раз ситуация обратная, у choose есть 2 аргумента, chat_id и question , но их ему никто не передал.
Для этого нужно воспользоваться фичей библиотеки и заставить функцию wait “поделиться” аргументами:
def wait(chat_id, question): bot.create_timer(5, choose, chat_id=chat_id, question=question)
Теперь бот наконец работает!
Напоследок стоит навести красоту, чтобы вас на смущало question=question . Эти аргументы можно переименовать как угодно, вот например:
def wait(chat_id, question): bot.create_timer(5, choose, author_id=chat_id, message=question) def choose(author_id, message): answers = ("да", "нет", "это возможно") choice = random.choice(answers) answer = "Думаю, <>".format(choice) bot.send_message(author_id, answer) print("Мне написал пользователь с ID:", author_id) print("Он спрашивал:", message) print("Я ответил:", answer) bot = ptbot.Bot(TG_TOKEN) bot.reply_on_message(wait) bot.run_bot()
Называйте аргументы как хотите. Главное, чтобы было понятно
Это было интересно… а что дальше?
Теперь вы можете писать простых ботов. Вы можете реализовать ещё несколько идей с использованием ptbot . Для них нужно знать конструкции if и for , но в остальном они очень простые и потребуют не больше сотни строк:
- Бот, который каждый год поздравляет вас с Новым годом/Днём рождения или напоминает о годовщине свадьбы.
- Бот для крестиков-ноликов
- Бот для игры виселицу
Когда вы перерастёте возможности библиотеки ptbot , можно осваивать её старшего брата: python-telegram-bot. Эта библиотека умеет более крутые фичи и на ней можно писать более сложных ботов. Обращению с ней мы учим в курсе Чат-боты на Python.
Попробуйте бесплатные уроки по Python
Получите крутое код-ревью от практикующих программистов с разбором ошибок и рекомендациями, на что обратить внимание — бесплатно.
Переходите на страницу учебных модулей «Девмана» и выбирайте тему.
Как сделать так чтобы бот в телеграме отвечал на все сообщения в том числе на видео и фото? Aiogram
Но он отвечает только на текстовые сообщения. Если скинуть ему видео или фото ответа нету. Что делать?
Отслеживать
задан 13 апр 2022 в 14:54
1 1 1 бронзовый знак
@dp.message_handler()
13 апр 2022 в 15:24
1 ответ 1
Сортировка: Сброс на вариант по умолчанию
Примерно вот так @dp.message_handler(content_type=[types.ContentType.ANY]) Я правда не уверен в регистре.
Отслеживать
ответ дан 14 апр 2022 в 4:34
oleksandrigo oleksandrigo
5,318 2 2 золотых знака 10 10 серебряных знаков 25 25 бронзовых знаков
- python
- telegram
- aiogram
-
Важное на Мете
Похожие
Подписаться на ленту
Лента вопроса
Для подписки на ленту скопируйте и вставьте эту ссылку в вашу программу для чтения RSS.
Дизайн сайта / логотип © 2023 Stack Exchange Inc; пользовательские материалы лицензированы в соответствии с CC BY-SA . rev 2023.11.15.1019
Нажимая «Принять все файлы cookie» вы соглашаетесь, что Stack Exchange может хранить файлы cookie на вашем устройстве и раскрывать информацию в соответствии с нашей Политикой в отношении файлов cookie.
Как сделать так чтобы телеграм бот записывал сообщение пользователя и отправлял его как запрос на сайт wiki через библиотеку wikipedia?
Итак сегодня я столкнулся с проблемой при написании телеграм бота.Идея заключалась в том чтобы ты писал боту запрос а он тебе кидал в текстовом формате краткую статью вики(как я уже написал в заголовке через библиотеку wikipedia) но получилось так что бот отправлял не последнее сообщение как запрос а саму команду которая вызывала всю эту радость(search).
import telebot from telebot import types, TeleBot import wikipedia bot: TeleBot = telebot.TeleBot('BOT TOKEN') def short(mess): features = "html.parser" wikipedia.set_lang("ru") c = wikipedia.summary(mess, sentences=100) return c def search_2(message): getin = message.text bot.send_message(message.chat.id, short(getin), parse_mode="html") @bot.message_handler(commands=['search']) def search(message): bot.send_message(message.chat.id, "Что тебя интересует?") search_2(message)
вот так выглядел код. Я пробовал: Обнулять переменную(Возможно я делал это неправильно)
def search(message): bot.send_message(message.chat.id, "Что тебя интересует?") getin = message.text getin = "" getin = message.text bot.send_message(message.chat.id, short(getin), parse_mode="html")
Также пробовал выполнять команду search через 2 функции(то есть в функции search я использовал функцию search_2) это последний мой вариант кода который я представил ранее. И да это первый мой вопрос на этом сайте поэтому не судите строго за корявость оформления этого вопроса)
