Телеграм-бот на Python

В пер­вой части мы сде­ла­ли горо­скоп на Python, кото­рый выда­ёт нам про­гноз на день по зна­ку зоди­а­ка. Сего­дня пой­дём даль­ше: теперь этот же гене­ра­тор горо­ско­пов будет встро­ен в Теле­грам в виде бота.

Да. То, что обыч­но на кур­сах про­да­ют за 50 тысяч руб­лей, мы вам сей­час рас­ска­жем за 15 минут бесплатно.

Как всё будет работать

В этом про­ек­те три зве­на: наш ком­пью­тер с Python, сер­вер Теле­гра­ма и Телеграм-клиент.

На ком­пью­те­ре рабо­та­ет интер­пре­та­тор Python, а внут­ри интер­пре­та­то­ра кру­тит­ся наша про­грам­ма на Python. Она отве­ча­ет за весь кон­тент: в неё зало­же­ны все шаб­ло­ны тек­ста, вся логи­ка, всё поведение.

Внут­ри про­грам­мы на Python рабо­та­ет биб­лио­те­ка, кото­рая отве­ча­ет за обще­ние с сер­ве­ром Теле­гра­ма. В биб­лио­те­ку мы вши­ли сек­рет­ный ключ, что­бы сер­вер Теле­гра­ма пони­мал, что наша про­грам­ма свя­за­на с опре­де­лён­ным ботом.

Когда кли­ент с Теле­гра­мом запра­ши­ва­ет у бота горо­скоп, запрос при­хо­дит на сер­вер, а сер­вер отправ­ля­ет его на наш ком­пью­тер. Запрос обра­ба­ты­ва­ет­ся про­грам­мой на Python, ответ идёт на сер­вер Теле­гра­ма, сер­вер отда­ёт ответ кли­ен­ту. Изи:

Обра­ти­те вни­ма­ние, что рабо­тать наш бот будет толь­ко тогда, когда вклю­чён ком­пью­тер и на нём запу­ще­на про­грам­ма на Python. Если ком­пью­тер выклю­чит­ся, про­па­дёт интер­нет или вы отклю­чи­те интер­пре­та­тор, то бот рабо­тать пере­ста­нет: запро­сы будут при­хо­дить, но никто на них не отве­тит. В одной из сле­ду­ю­щих частей мы сде­ла­ем так, что­бы это всё рабо­та­ло на уда­лён­ном сер­ве­ре и было все­гда доступно.

Что будем делать

Если запи­сать поша­го­во наш план, то он будет выгля­деть так:

  1. Реги­стри­ру­ем бота в Телеграме.
  2. Уста­нав­ли­ва­ем Python-библиотеку для рабо­ты с Телеграмом.
  3. Добав­ля­ем биб­лио­те­ку в про­грам­му с горо­ско­пом и учим про­грам­му реа­ги­ро­вать на сооб­ще­ния в чате.
  4. Пишем там же код, кото­рый пока­жет кноп­ки для выбо­ра зна­ков зодиака.
  5. Сде­ла­ем так, что­бы по кноп­кам появ­лял­ся горо­скоп для это­го знака.

Теперь по оче­ре­ди раз­бе­рём каж­дый пункт.

1. Регистрация нового бота

В Теле­гра­ме нахо­дим канал @BotFather — он отве­ча­ет за реги­стра­цию новых ботов:

Пер­вый в спис­ке со спе­ци­аль­ным знач­ком под­твер­жде­ния — это он. 

Нажи­ма­ем Start и пишем коман­ду /newbot. Нас по оче­ре­ди спро­сят про назва­ние бота и его ник­нейм (мы при­ду­ма­ли толь­ко с тре­тьей попыт­ки, пото­му что осталь­ные были заняты):

С тре­тьей попыт­ки нам дали ново­го бота и токен для управ­ле­ния. Токен нужен для управ­ле­ния ботом, поэто­му на экране его нет. 

2. Установка библиотеки

Есть два основ­ных спо­со­ба рабо­тать с теле­гра­мом в Python: через биб­лио­те­ку telebot и с помо­щью Webhook. Мы будем исполь­зо­вать биб­лио­те­ку — так про­ще и быстрее.

Что­бы её уста­но­вить, запус­ка­ем команд­ную стро­ку от име­ни адми­ни­стра­то­ра (если у вас Windows) и пишем коман­ду pip install pytelegrambotapi

В кон­це видим сооб­ще­ние об успеш­ной уста­нов­ке, зна­чит всё сде­ла­ли правильно. 

Подключаем библиотеку и получаем сообщения

Что­бы про­грам­ма на Python уме­ла управ­лять Телеграм-ботами, нуж­но в самое нача­ло кода доба­вить строки:

import telebot; bot = telebot.TeleBot('токен');

Един­ствен­ное, о чём нуж­но не забыть — заме­нить сло­во «токен» на насто­я­щий токен, кото­рый дал нам @BotFather. Откры­ва­ем про­грам­му горо­ско­па и добавляем.

Программа-гороскоп

# Подключаем модуль случайных чисел 
import random
# Заготовка для первого предложения
first = ["Сегодня — идеальный день для новых начинаний.","Оптимальный день для того, чтобы решиться на смелый поступок!","Будьте осторожны, сегодня звёзды могут повлиять на ваше финансовое состояние.","Лучшее время для того, чтобы начать новые отношения или разобраться со старыми.","Плодотворный день для того, чтобы разобраться с накопившимися делами."]
second = ["Но помните, что даже в этом случае нужно не забывать про","Если поедете за город, заранее подумайте про","Те, кто сегодня нацелен выполнить множество дел, должны помнить про","Если у вас упадок сил, обратите внимание на","Помните, что мысли материальны, а значит вам в течение дня нужно постоянно думать про"]
second_add = ["отношения с друзьями и близкими.","работу и деловые вопросы, которые могут так некстати помешать планам.","себя и своё здоровье, иначе к вечеру возможен полный раздрай.","бытовые вопросы — особенно те, которые вы не доделали вчера.","отдых, чтобы не превратить себя в загнанную лошадь в конце месяца."]
third = ["Злые языки могут говорить вам обратное, но сегодня их слушать не нужно.","Знайте, что успех благоволит только настойчивым, поэтому посвятите этот день воспитанию духа.","Даже если вы не сможете уменьшить влияние ретроградного Меркурия, то хотя бы доведите дела до конца.","Не нужно бояться одиноких встреч — сегодня то самое время, когда они значат многое.","Если встретите незнакомца на пути — проявите участие, и тогда эта встреча посулит вам приятные хлопоты."]
# выводим знаки зодиака
print("1 — Овен")
print("2 — Телец")
print("3 — Близнецы")
print("4 — Рак")
print("5 — Лев")
print("6 — Дева")
print("7 — Весы")
print("8 — Скорпион")
print("9 — Стрелец")
print("10 — Козерог")
print("11 — Водолей")
print("12 — Рыбы")
# Спрашиваем у пользователя про его знак
zodiac = int(input("{blue}Введите число с номером знака зодиака: {endcolor}".format(blue="\033[96m", endcolor="\033[0m")))
# Если число введено верно — выдаём гороскоп
if 0 < zodiac < 13:
    print(random.choice(first), random.choice(second), random.choice(second_add), random.choice(third))
else:
    print("Вы ошиблись с числом, запустите программу ещё раз")
    

Теперь научим бота реа­ги­ро­вать на сло­во «При­вет». Для это­го доба­вим после стро­чек с импор­том новый метод и сра­зу про­пи­шем в нём реак­цию на нуж­ное сло­во. Если не зна­е­те, что такое метод и зачем он нужен, — читай­те ста­тью про ООП.

@bot.message_handler(content_types=['text'])
def get_text_messages(message):
  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)

Она ска­жет про­грам­ме, что­бы она непре­рыв­но спра­ши­ва­ла у бота, не при­шли ли ему какие-то новые сооб­ще­ния. Запус­ка­ем про­грам­му и про­ве­ря­ем, как рабо­та­ет наш бот.

Бот отве­ча­ет имен­но так, как мы запро­грам­ми­ро­ва­ли. Класс. 
Такая ошиб­ка во вре­мя запус­ка про­грам­мы озна­ча­ет, что ком­пью­тер не может соеди­нить­ся с сер­ве­ром telegram.org, пото­му что его бло­ки­ру­ет Рос­ком­над­зор. Что делать? Слож­но ска­зать. Если бы вы жили в дру­гой стране, этой про­бле­мы бы не было. Ещё мож­но исполь­зо­вать какие-то сред­ства, кото­рые направ­ля­ют ваш тра­фик через дру­гую стра­ну, но рас­сказ об этих сред­ствах явля­ет­ся в Рос­сии пре­ступ­ле­ни­ем, поэто­му тут мы вам ниче­го не можем подсказать. 

Добавляем кнопки

Что­бы поль­зо­ва­те­лям наше­го бота было удоб­но, пока­жем им сра­зу все зна­ки зоди­а­ка в виде кно­пок. А потом сде­ла­ем так, что когда на них нажи­ма­ешь — появ­ля­ет­ся горо­скоп для это­го зна­ка на сегодня.

Добав­ля­ем код с кноп­ка­ми в раз­дел, кото­рый реа­ги­ру­ет на «При­вет»:

# Готовим кнопки
keyboard = types.InlineKeyboardMarkup()
# По очереди готовим текст и обработчик для каждого знака зодиака
key_oven = types.InlineKeyboardButton(text='Овен', callback_data='zodiac')
# И добавляем кнопку на экран
keyboard.add(key_oven)
key_telec = types.InlineKeyboardButton(text='Телец', callback_data='zodiac')
keyboard.add(key_telec)
key_bliznecy = types.InlineKeyboardButton(text='Близнецы', callback_data='zodiac')
keyboard.add(key_bliznecy)
key_rak = types.InlineKeyboardButton(text='Рак', callback_data='zodiac')
keyboard.add(key_rak)
key_lev = types.InlineKeyboardButton(text='Лев', callback_data='zodiac')
keyboard.add(key_lev)
key_deva = types.InlineKeyboardButton(text='Дева', callback_data='zodiac')
keyboard.add(key_deva)
key_vesy = types.InlineKeyboardButton(text='Весы', callback_data='zodiac')
keyboard.add(key_vesy)
key_scorpion = types.InlineKeyboardButton(text='Скорпион', callback_data='zodiac')
keyboard.add(key_scorpion)
key_strelec = types.InlineKeyboardButton(text='Стрелец', callback_data='zodiac')
keyboard.add(key_strelec)
key_kozerog = types.InlineKeyboardButton(text='Козерог', callback_data='zodiac')
keyboard.add(key_kozerog)
key_vodoley = types.InlineKeyboardButton(text='Водолей', callback_data='zodiac')
keyboard.add(key_vodoley)
key_ryby = types.InlineKeyboardButton(text='Рыбы', callback_data='zodiac')
keyboard.add(key_ryby)
# Показываем все кнопки сразу и пишем сообщение о выборе
bot.send_message(message.from_user.id, text='Выбери свой знак зодиака', reply_markup=keyboard)
Кноп­ки есть, но пока не рабо­та­ют. Сей­час исправим. 

Добавляем обработчик кнопок

Ско­рее все­го, вы заме­ти­ли, что в каж­дой кноп­ке у нас напи­са­но callback_data='zodiac'. Это зна­чит, что при нажа­тии на любую кноп­ку у нас будет вызы­вать­ся один и тот же метод, кото­рый отве­ча­ет за горо­скоп. Если вы хоти­те сде­лать чест­ный горо­скоп, при­дёт­ся в каж­дой кноп­ке про­пи­сать своё назва­ние обра­бот­чи­ка, а потом задать его пове­де­ние, тоже для каж­дой кнопки.

Давай­те сде­ла­ем обра­бот­чик кно­пок, кото­рый будет реа­ги­ро­вать на 'zodiac' и выда­вать слу­чай­ный текст, как в исход­ной про­грам­ме. Для это­го доба­вим новый метод в программу:

# Обработчик нажатий на кнопки
@bot.callback_query_handler(func=lambda call: True)
def callback_worker(call):
  # Если нажали на одну из 12 кнопок — выводим гороскоп
  if call.data == "zodiac": 
    # Формируем гороскоп
    msg = random.choice(first) + ' ' + random.choice(second) + ' ' + random.choice(second_add) + ' ' + random.choice(third)
    # Отправляем текст в Телеграм
    bot.send_message(call.message.chat.id, msg)
Нажи­ма­ем на кноп­ку — полу­ча­ем текст гороскопа. 

Убираем лишнее

Теперь у нас есть гото­вый бот, и нам оста­лось толь­ко убрать лиш­ний код, кото­рый рань­ше отве­чал за вывод зна­ков зоди­а­ка в кон­со­ли. После чист­ки полу­ча­ем гото­вую программу:

# Подключаем модуль случайных чисел 
import random
# Подключаем модуль для Телеграма
import telebot
# Указываем токен
bot = telebot.TeleBot('токен')
# Импортируем типы из модуля, чтобы создавать кнопки
from telebot import types
# Заготовки для трёх предложений
first = ["Сегодня — идеальный день для новых начинаний.","Оптимальный день для того, чтобы решиться на смелый поступок!","Будьте осторожны, сегодня звёзды могут повлиять на ваше финансовое состояние.","Лучшее время для того, чтобы начать новые отношения или разобраться со старыми.","Плодотворный день для того, чтобы разобраться с накопившимися делами."]
second = ["Но помните, что даже в этом случае нужно не забывать про","Если поедете за город, заранее подумайте про","Те, кто сегодня нацелен выполнить множество дел, должны помнить про","Если у вас упадок сил, обратите внимание на","Помните, что мысли материальны, а значит вам в течение дня нужно постоянно думать про"]
second_add = ["отношения с друзьями и близкими.","работу и деловые вопросы, которые могут так некстати помешать планам.","себя и своё здоровье, иначе к вечеру возможен полный раздрай.","бытовые вопросы — особенно те, которые вы не доделали вчера.","отдых, чтобы не превратить себя в загнанную лошадь в конце месяца."]
third = ["Злые языки могут говорить вам обратное, но сегодня их слушать не нужно.","Знайте, что успех благоволит только настойчивым, поэтому посвятите этот день воспитанию духа.","Даже если вы не сможете уменьшить влияние ретроградного Меркурия, то хотя бы доведите дела до конца.","Не нужно бояться одиноких встреч — сегодня то самое время, когда они значат многое.","Если встретите незнакомца на пути — проявите участие, и тогда эта встреча посулит вам приятные хлопоты."]
# Метод, который получает сообщения и обрабатывает их
@bot.message_handler(content_types=['text'])
def get_text_messages(message):
    # Если написали «Привет»
    if message.text == "Привет":
        # Пишем приветствие
        bot.send_message(message.from_user.id, "Привет, сейчас я расскажу тебе гороскоп на сегодня.")
        # Готовим кнопки
        keyboard = types.InlineKeyboardMarkup()
        # По очереди готовим текст и обработчик для каждого знака зодиака
        key_oven = types.InlineKeyboardButton(text='Овен', callback_data='zodiac')
        # И добавляем кнопку на экран
        keyboard.add(key_oven)
        key_telec = types.InlineKeyboardButton(text='Телец', callback_data='zodiac')
        keyboard.add(key_telec)
        key_bliznecy = types.InlineKeyboardButton(text='Близнецы', callback_data='zodiac')
        keyboard.add(key_bliznecy)
        key_rak = types.InlineKeyboardButton(text='Рак', callback_data='zodiac')
        keyboard.add(key_rak)
        key_lev = types.InlineKeyboardButton(text='Лев', callback_data='zodiac')
        keyboard.add(key_lev)
        key_deva = types.InlineKeyboardButton(text='Дева', callback_data='zodiac')
        keyboard.add(key_deva)
        key_vesy = types.InlineKeyboardButton(text='Весы', callback_data='zodiac')
        keyboard.add(key_vesy)
        key_scorpion = types.InlineKeyboardButton(text='Скорпион', callback_data='zodiac')
        keyboard.add(key_scorpion)
        key_strelec = types.InlineKeyboardButton(text='Стрелец', callback_data='zodiac')
        keyboard.add(key_strelec)
        key_kozerog = types.InlineKeyboardButton(text='Козерог', callback_data='zodiac')
        keyboard.add(key_kozerog)
        key_vodoley = types.InlineKeyboardButton(text='Водолей', callback_data='zodiac')
        keyboard.add(key_vodoley)
        key_ryby = types.InlineKeyboardButton(text='Рыбы', callback_data='zodiac')
        keyboard.add(key_ryby)
        # Показываем все кнопки сразу и пишем сообщение о выборе
        bot.send_message(message.from_user.id, text='Выбери свой знак зодиака', reply_markup=keyboard)
    elif message.text == "/help":
        bot.send_message(message.from_user.id, "Напиши Привет")
    else:
        bot.send_message(message.from_user.id, "Я тебя не понимаю. Напиши /help.")
# Обработчик нажатий на кнопки
@bot.callback_query_handler(func=lambda call: True)
def callback_worker(call):
    # Если нажали на одну из 12 кнопок — выводим гороскоп
    if call.data == "zodiac": 
        # Формируем гороскоп
        msg = random.choice(first) + ' ' + random.choice(second) + ' ' + random.choice(second_add) + ' ' + random.choice(third)
        # Отправляем текст в Телеграм
        bot.send_message(call.message.chat.id, msg)
# Запускаем постоянный опрос бота в Телеграме
bot.polling(none_stop=True, interval=0)

Как вид­но, боль­шую часть кода зани­ма­ет тупое пере­чис­ле­ние всех зна­ков зоди­а­ка. Мы мог­ли бы авто­ма­ти­зи­ро­вать это через цик­лы, но на ули­це такая хоро­шая пого­да, что мы это отложим.

Что дальше

Впе­ре­ди — без­гра­нич­ные возможности:

  • мож­но сде­лать инди­ви­ду­аль­ный горо­скоп для каж­до­го знака;
  • научить бота новым командам;
  • при­сы­лать све­жий горо­скоп каж­дое утро;
  • нала­дить непре­рыв­ную рабо­ту на веб-сервере.

Напи­ши­те в ком­мен­та­ри­ях, что бы вы хоте­ли от тако­го бота? Что дол­жен уметь иде­аль­ный бот с гороскопом?