Простой Telegram-бот на Python за 30 минут

На Хабре, да и не только, про ботов рассказано уже так много, что даже слишком. Но заинтересовавшись пару недель назад данной темой, найти нормальный материал у меня так и не вышло: все статьи были либо для совсем чайников и ограничивались отправкой сообщения в ответ на сообщение пользователя, либо были неактуальны. Это и подтолкнуло меня на написание статьи, которая бы объяснила такому же новичку, как я, как написать и запустить более-менее осмысленного бота (с возможностью расширения функциональности).

Часть 1: Регистрация бота


Самая простая и описанная часть. Очень коротко: нужно найти бота @BotFather, написать ему /start, или /newbot, заполнить поля, которые он спросит (название бота и его короткое имя), и получить сообщение с токеном бота и ссылкой на документацию. Токен нужно сохранить, желательно надёжно, так как это единственный ключ для авторизации бота и взаимодействия с ним.

Часть 2: Подготовка к написанию кода


Как уже было сказано в заголовке, писать бота мы будем на Python'е. В данной статье будет описана работа с библиотекой PyTelegramBotAPI (Telebot). Если у вас не установлен Python, то сперва нужно сделать это: в терминале Linux нужно ввести

sudo apt-get install python python-pip

Если же вы пользуетесь Windows, то нужно скачать Python с официального сайта .

После, в терминале Linux, или командной строке Windows вводим

pip install pytelegrambotapi

Теперь все готово для написания кода.

Часть 3: Получаем сообщения и говорим «Привет»


Небольшое отступление. Телеграмм умеет сообщать боту о действиях пользователя двумя способами: через ответ на запрос сервера (Long Poll), и через Webhook, когда сервер Телеграмма сам присылает сообщение о том, что кто-то написал боту. Второй способ явно выглядит лучше, но требует выделенного IP-адреса, и установленного SSL на сервере. В этой статье я хочу рассказать о написании бота, а не настройке сервера, поэтому пользоваться мы будем Long Poll'ом.

Открывайте ваш любимый текстовый редактор, и давайте писать код бота!

Первое, что нужно сделать это импортировать нашу библиотеку и подключить токен бота:

import telebot;
bot = telebot.TeleBot('%ваш токен%');

Теперь объявим метод для получения текстовых сообщений:

@bot.message_handler(content_types=['text'])
def get_text_messages(message):

В этом участке кода мы объявили слушателя для текстовых сообщений и метод их обработки. Поле content_types может принимать разные значения, и не только одно, например

@bot.message_handler(content_types=['text', 'document', 'audio'])

Будет реагировать на текстовые сообщения, документы и аудио. Более подробно можно почитать в официальной документации

Теперь добавим в наш метод немного функционала: если пользователь напишет нам «Привет», то скажем ему «Привет, чем я могу помочь?», а если нам напишут команду «/help», то скажем пользователю написать «Привет»:

if message.text == "Привет":
    bot.send_message(message.from_user.id, "Привет, чем я могу тебе помочь?")
elif message.text == "/help":
    bot.send_message(message.from_user.id, "Напиши привет")
else:
    bot.send_message(message.from_user.id, "Я тебя не понимаю. Напиши /help.")

Данный участок кода не требует комментариев, как мне кажется. Теперь нужно добавить в наш код только одну строчку (вне всех методов).

bot.polling(none_stop=True, interval=0)

Теперь наш бот будет постоянно спрашивать у сервера Телеграмма «Мне кто-нибудь написал?», и если мы напишем нашему боту, то Телеграмм передаст ему наше сообщение. Сохраняем весь файл, и пишем в консоли

python bot.py

Где bot.py – имя нашего файла.

Теперь можно написать боту и посмотреть на результат:

image

Часть 4: Кнопки и ветки сообщений


Отправлять сообщения это несомненно весело, но ещё веселее вести с пользователем диалог: задавать ему вопросы и получать на них ответы. Допустим, теперь наш бот будет спрашивать у пользователя по очереди его имя, фамилию и возраст. Для этого мы будем использовать метод register_next_step_handler бота:

name = '';
surname = '';
age = 0;
@bot.message_handler(content_types=['text'])
def start(message):
    if message.text == '/reg':
        bot.send_message(message.from_user.id, "Как тебя зовут?");
        bot.register_next_step_handler(message, get_name); #следующий шаг – функция get_name
    else:
        bot.send_message(message.from_user.id, 'Напиши /reg');

def get_name(message): #получаем фамилию
    global name;
    name = message.text;
    bot.send_message(message.from_user.id, 'Какая у тебя фамилия?');
    bot.register_next_step_handler(message, get_surnme);

def get_surname(message):
    global surname;
    surname = message.text;
    bot.send_message('Сколько тебе лет?');
    bot.register_next_step_handler(message, get_age);

def get_age(message):
    global age;
    while age == 0: #проверяем что возраст изменился
        try:
             age = int(message.text) #проверяем, что возраст введен корректно
        except Exception:
             bot.send_message(message.from_user.id, 'Цифрами, пожалуйста');
      bot.send_message(message.from_user.id, 'Тебе '+str(age)+' лет, тебя зовут '+name+' '+surname+'?')

И так, данные пользователя мы записали. В этом примере показан очень упрощённый пример, по хорошему, хранить промежуточные данные и состояния пользователя нужно в БД, но мы сегодня работаем с ботом, а не с базами данных. Последний штрих – запросим у пользователей подтверждение того, что все введено верно, да не просто так, а с кнопками! Для этого немного отредактируем код метода get_age

def get_age(message):
    global age;
    while age == 0: #проверяем что возраст изменился
        try:
             age = int(message.text) #проверяем, что возраст введен корректно
        except Exception:
             bot.send_message(message.from_user.id, 'Цифрами, пожалуйста');
      keyboard = types.InlineKeyboardMarkup(); #наша клавиатура
      key_yes = types.InlineKeyboardButton(text='Да', callback_data='yes'); #кнопка «Да»
      keyboard.add(key_yes); #добавляем кнопку в клавиатуру
      key_no= types.InlineKeyboardButton(text='Нет', callback_data='no');
      keyboard.add(key_no);
      question = 'Тебе '+str(age)+' лет, тебя зовут '+name+' '+surname+'?';
      bot.send_message(message.from_user.id, text=question, reply_markup=keyboard)

И теперь наш бот отправляет клавиатуру, но если на нее нажать, то ничего не произойдёт. Потому что мы не написали метод-обработчик. Давайте напишем:

@bot.callback_query_handler(func=lambda call: True)
def callback_worker(call):
    if call.data == "yes": #call.data это callback_data, которую мы указали при объявлении кнопки
        .... #код сохранения данных, или их обработки
        bot.send_message(call.message.chat.id, 'Запомню : )');
    elif call.data == "no":
         ... #переспрашиваем

Остаётся только дописать в начало файла одну строку:

from telebot import types

Вот и всё, сохраняем и запускаем нашего бота:

image

Средняя зарплата в IT

120 000 ₽/мес.
Средняя зарплата по всем IT-специализациям на основании 8 965 анкет, за 1-ое пол. 2021 года Узнать свою зарплату
Реклама
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее

Комментарии 21

    +4
    После, в терминале Linux, или командной строке Windows вводим
    pip install pytelegrambotapi


    ИМХО было бы не плохо, не устанавливать пакет(ы) глобально, а рассказать, что для таких целей лучше использовать virtualenv или pypenv
      –1
      А ещё лучше docker
        +2
        Прекратите, пожалуйста, пихать доскер по делу и без.
          0
          Почему? Докер же беспрецедентно прекрасен. Если у вас пачка специфических зависимостей, то спрятать их в контейнер — это лучшее, что можно придумать для тех, кому нужно «ехать», а не «шашечки». Те, кому «шашечки», всё и так поставят да развернут, пропишут и запустят, но докер даёт унификацию и изоляцию.
          Вообще, у меня, простите, бомбануло. Обоснуйте, пожалуйста, подробнее почему вы думаете, что контейнеризация в этом проекте не уместна?
            +1
            Потому что это проект на пять условных строчек кода. Потому что это проект на Python, который изо всех сил старается стать платформонезависимым и гарантировать одинаковое поведение на всех системах, для которых есть интерпретатор. Потому что, в конце концов, это стрельба из пушки по воробьям. Контейнеризация уместна, например, если Вы написали большое приложение с кучей зависимостей, вплоть до таких, которые нужно собирать руками, как, например, это было со многим ПО для кодирования MP3, для многого приходилось собирать libmp3lame0 ручками. Туда же идёт nginx, который по умолчанию собирается статически. Тут да, каждый раз собирать утомительно.
            А здесь нет никакого выигрыша, кроме времени установки. Контейнер будет отжирать всяко больше, чем голый интерпретатор, которому хватит и virtualenv. И всё ради чего? Чтобы модным «девопсам» было проще запустить пятистрочник?
              –1
              все зависит от того — что у вас в избытке, а чего в недостатке.
              если у вас есть деньги и время на время девопса, вы будете смотреть в виртуаленв, а если у вас нет времени, а есть деньги — то вы (не сразу, пару раз протормозите, но жизнь намекнет) вы потратите на докер\вируалку\что угодно, что масштабируется быстрее чем люди.
                0
                Дело в том, что маленький докер-файл, размещенный в репозитории, не отменит возможности поставить и запускать проект через virtualenv или вовсе глобально. Просто будет дополнительный способ развернуть приложение быстро и просто, возможно с маленькими накладными расходами. Почему бы не предоставить право выбирать способ установки конечному пользователю? Кроме того, докер позволит без танцев с бубном развернуть вашего бота на каком-нибудь NAS вроде synology или на современном медиа-плеере. Да, контейнер будет прилично весить по сравнению с «чистой» установкой, но такие ресурсы нынче дешевы.
                Коллега vassabi в соседнем комментарии прав, не знаю кто влепил ему минус за вполне разумную точку зрения.

                С другой стороны, не очень понятен ваш экспрессивно негативный тон отзыва о докере и его применимости в данном случае. Невольно читается какое-то предвзятое отношение.

                Питон, конечно, старается быть на всех платформах одинаковым, и у него немножко это даже местами получается, но унификация, прсотота и изоляция докера перевешивает. Кстати, запускать какой-то чужой код, пусть даже бегло просмотренный ввиду своих невеликих размеров, спокойнее в изолированной среде отдельного контейнера, а не на сервере, где грутится помимо всего прочего много полезных и нужных вещей вроде сайта (тоже в контейнерах) и прочих более важных сервисов, чем какой-то чат-бот.
        +2
        в документации на библиотеку детально написано все — по ссылке github.com/eternnoir/pyTelegramBotAPI от авторов подробный и понятный пример.

        interval=0 — крутовато, конечно. Тогда уже надо реализовавать вебхук
          0
          Чем обоснован выбор обработчика по типу с if/elif/else, когда существует regexp для текста и commands для команд?
            +7
            Ох, уж этот telebot. Как раз ситуация, когда простота хуже {подставить по желанию}.

            Каждый первый вопрос на профильных форумах, у меня бот на питон (telebot), как мне «изменить/добавить» и дальше простенький вопрос не выходящий за рамки документации Bot-API телеги. А человек уже в тупике.

            API простейшее, зачем хаутушки делать с определенным враппером? По-правильному ботовод должен знать API, а как POST-запросы слать или какую обёртку использовать, дело совсем уже десятое.

            Очередное описание телебота получилось, а не подход к построению бот-фермы. Где сохранение состояния, где подход к расширению функциональности?
              +1
              Вопросы возникают потому что люди читают статьи «как сделать бота за 5 минут не умея программировать», а документацию не читают.
              0

              Частично по теме — что сейчас с ботами в свете блокировок? Есть бесплатный и безболезненный способ обойти РКН?

                +2
                Держать серверы в другой стране.
                  0
                  А если это невозможно?
                    +1
                    Самый простой вариант — подключать бота через прокси. Многие библиотеки это умеют из коробки. Не знаю насчет mtproto прокси, но http и socks поддерживаются.
                +3
                Конечно, как же без global. А что будет, если этим ботом больше одного человека начнет пользоваться? Потом еще куча новичков на Тостер побежит, спрашивать, а чего это не работает.
                  0
                  как только кто-то попробует с двумя адресатами одновременно общаться — тут же и споткнется об него и перепишет.
                  Зато обратная ситуация (забытый global, когда он нужен) — одна из самых коварных фишек питона для новичков.
                    +1
                    Нет, во-первых, новички не поймут проблему, во-вторых, не нужно новичкам вообще использовать global, он нужен очень редко и только тогда, когда понимают для чего он и почему без него нельзя обойтись.
                  +1

                  Для текущего проекта с ботом выбрал aiogram: там и асинхронщина, и конечные автоматы, и ещё много чего

                    +3
                    На Хабре, да и не только, про ботов рассказано уже так много, что даже слишком. Но заинтересовавшись пару недель назад данной темой, найти нормальный материал у меня так и не вышло. Поэтому я решил добавить еще один такой же
                      0

                      Вижу: глобальные переменные, ловим Exception, форматирование через конкатенацию. Это же читают новички, не надо так!


                      Кстати, вы делаете register_next_step_handler, стоит отметить что при перезапуске бота по дефолту все такие хэндлеры слетают. А сохранять эта штука умеет только в pickle, что как бы тоже не очень.

                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                      Самое читаемое