7702

Как создать телеграм-бота на Python

Боты в Telegram предоставляют бизнесу еще один эффективный канал коммуникации с аудиторией и автоматизируют большой объем однообразной работы, освобождая время наемного персонала для выполнения более важных задач. Доступность и высокая скорость разработки ботов сделала их чрезвычайно популярными в разных отраслях бизнеса.

В этой статье поговорим про то, как создать телеграм-бота на Python, а именно — с помощью библиотеки python-telegram-bot. Это официальная оболочка для API от мессенджера Телеграм, полностью совместимая с Python 3.6+, с помощью которой можно удобно и достаточно быстро написать нужного бота.

 

Создание бота

Первым делом для начала работы потребуется установить python-telegram-bot. Для этого можно воспользоваться бесплатной документацией библиотеки.

Bot

$ pip install python-telegram-bot upgrade

После этого можно приступать к непосредственному созданию бота. Для этого выполняем несколько простых действий:

  • Перейти в Telegram.
  • Найти бота @BotFather, отвечающего за регистрацию новых ботов.
  • Начать общаться с ботом, использовав команду /newbot.
  • Ввести имя и юзернейм создаваемого бота.

С помощью @BotFather можно также выполнять множество других важных действий, например задать изображение нового бота.

Дальше нужно определиться с тем, какую именно задачу будет решать проект. В качестве примера мы создадим бота, предоставляющего информацию о биоритмах человека. Однако, используя эту инструкцию, вы сможете разрабатывать ботов любой направленности, ориентированных на потребности конкретного бизнеса.

 

Программирование бота с помощью Python

Как уже говорилось, пакет python-telegram-bot состоит из оболочки API Telegram, однако кроме этого он оснащен модулем telegram.ext, который существенно облегчит программирование бота.

Сам модуль telegram.ext включает множество классов, из которых можно выделить два основных

  • Telegram.ext.Updater — получает обновления от Telegram и передает их в Dispatcher.
  • Telegram.ext.Dispatcher — выполняет отправку всех видов обновлений зарегистрированным обработчикам.

Теперь можно приступить непосредственно к написанию кода:

# mastrobot_example.py
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters
# function to handle the /start command
def start(update, context):
    update.message.reply_text('start command received')
# function to handle the /help command
def help(update, context):
    update.message.reply_text('help command received')
# function to handle errors occured in the dispatcher 
def error(update, context):
    update.message.reply_text('an error occured')
# function to handle normal text 
def text(update, context):
    text_received = update.message.text
    update.message.reply_text(f'did you said "{text_received}" ?')
def main():
    TOKEN = "insert here your token and don't share it with anyone!"
    # create the updater, that will automatically create also a dispatcher and a queue to 
    # make them dialoge
    updater = Updater(TOKEN, use_context=True)
    dispatcher = updater.dispatcher
    # add handlers for start and help commands
    dispatcher.add_handler(CommandHandler("start", start))
    dispatcher.add_handler(CommandHandler("help", help))
    # add an handler for normal text (not commands)
    dispatcher.add_handler(MessageHandler(Filters.text, text))
    # add an handler for errors
    dispatcher.add_error_handler(error)
    # start your shiny new bot
    updater.start_polling()
    # run the bot until Ctrl-C
    updater.idle()
if __name__ == '__main__':
    main()

Обратите внимание, что для функции main() мы создали класс Updater. Впоследствии он сгенерировал новый объект Dispatcher, который стал доступен через  .dispatcher-свойства Updater.

Теперь добавим несколько обработчиков:

  • Пользовательская команда /start вызовет функцию start(), которая отправит человеку информационное сообщение по дальнейшей работе с ботом.
  • Пользовательская команда /help вызовет функцию help(), которая отправит сообщение-инструкцию с ответами на популярные вопросы.
  • В случае возникновения ошибки при отправке сообщения вызывается функция error().
  • При введении некорректной команды или символов, которые сами по себе не являются командой, будет вызвана функция text(). Она сгенерирует ответное сообщение с тем же текстом, который ввел пользователь.

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

Тестирование бота

На этом этапе уже можно приступить к тестированию проекта, чтобы удостовериться что все описанные обработчики работают корректно. Для этого запускаем бот и отправляем команду /start.

 

Запускаем

$ python mastrobot_example.py

Несмотря на то, что бот работает корректно, это еще не конец. Теперь, чтобы он выполнял поставленную задачу — предоставлял человеку информацию о биоритмах, нужно изменить функцию команды /start таким образом, чтобы она запрашивала личные данные пользователя, например: год, месяц и день рождения. Кроме того, создадим новую функцию с командой /biorhythm, которая на основании полученной информации будет генерировать ответ с биоритмом.

В первую очередь изменим функцию start(), для этого пишем такой код:

def start(update, context):
    first_name = update.message.chat.first_name
    update.message.reply_text(f"Hi {first_name}, nice to meet you!")
    start_getting_birthday_info(update, context)

В параметре update будет содержаться полезная информация о пользователе, например его имя или логин в Telegram. В начале скрипта создадим новую переменную STATE, и пока оставим ее пустой. Она будет нужна для того, чтобы понять, на какой вопрос отвечает пользователь:

STATE = None
BIRTH_YEAR = 1
BIRTH_MONTH = 2
BIRTH_DAY = 3

Дальше реализуем функцию start_getting:_birthday_info(), которая будет вызываться с помощью /start. Таким образом после запуска мы сможем получить данные о дне рождения пользователя.

def start_getting_birthday_info(update, context):
    global STATE
    STATE = BIRTH_YEAR
    update.message.reply_text(f"I would need to know your birthday, so tell me what year were you born in...")

Также для переменной STATE мы установили значение BIRTH_YEAR. Это поможет пользователю понять, что вопрос будет касаться года его рождения. После этого ему отправляется сообщение с соответствующим запросом. 

Здесь мы подразумеваем, что пользователь даст ответ текстовым значением, которое не будет являться командой.

Поэтому, для корректной работы бота нам также нужно изменить функцию text().

def text(update, context):
    global STATE
    if STATE == BIRTH_YEAR:
        return received_birth_year(update, context)
    if STATE == BIRTH_MONTH:
        return received_birth_month(update, context)
    if STATE == BIRTH_DAY:
        return received_birth_day(update, context)

Используя переменную STATE мы позволяем боту понять, на какой именно вопрос ответил пользователь. 

Дальше нам остается только вызвать функцию, которая будет выполнять обработку каждого ответа пользователя.

В коде это можно записать следующим образом:

def received_birth_year(update, context):
    global STATE
    try:
        today = datetime.date.today()
        year = int(update.message.text)
        
        if year > today.year:
            raise ValueError("invalid value")
        context.user_data['birth_year'] = year
        update.message.reply_text(f"ok, now I need to know the month (in numerical form)...")
        STATE = BIRTH_MONTH
    except:
        update.message.reply_text("it's funny but it doesn't seem to be correct...")
def received_birth_month(update, context):
    global STATE
    try:
        today = datetime.date.today()
        month = int(update.message.text)
        if month > 12 or month < 1:
            raise ValueError("invalid value")
        context.user_data['birth_month'] = month
        update.message.reply_text(f"great! And now, the day...")
        STATE = BIRTH_DAY
    except:
        update.message.reply_text("it's funny but it doesn't seem to be correct...")
def received_birth_day(update, context):
    global STATE
    try:
        today = datetime.date.today()
        dd = int(update.message.text)
        yyyy = context.user_data['birth_year']
        mm = context.user_data['birth_month']
        birthday = datetime.date(year=yyyy, month=mm, day=dd)
        if today - birthday < datetime.timedelta(days=0):
            raise ValueError("invalid value")
        context.user_data['birthday'] = birthday
        STATE = None
        update.message.reply_text(f'ok, you born on {birthday}')
    except:
        update.message.reply_text("it's funny but it doesn't seem to be correct...")

При получении ответа от пользователя нужно проверить допустимость введенного значения. Если данные корректны, они записываются в массив context.user_data[]. 

Теперь остается аналогичным образом получить от пользователя остальные данные. Для этого мы можем просто менять значение переменной STATE и задавать человеку соответствующие вопросы. 

После получения последнего ответа добавляем переменную даты, которая будет получаться автоматически, и сохраняем ее в словарь context.user_data[].

При этом, благодаря функции text() мы защищаем данные от некорректного ввода. Если будет получен недопустимый ответ, система автоматически вернет пользователю его же сообщение, и он фактически «застрянет» на этом вопросе пока не предоставит правильную информацию.

Создаем логику основной команды

Получив от пользователя всю нужную информацию, бот должен ее обработать, и предоставить человеку данные о его биоритмах. Для этого настроим работу команды /biorhythm.

В первую очередь добавим в функцию main() новый обработчик команд:

dispatcher.add_handler(CommandHandler("biorhythm", biorhythm))

Затем опишем логику расчета биоритма. Для этого создадим две отдельные функции: первая будет отвечать за обработку команды /biorhythm, а вторая выполнять непосредственные математические вычисления.

В коде это будет выглядеть так:

# This function is called when the /biorhythm command is issued
def biorhythm(update, context):
    user_biorhythm = calculate_biorhythm(
        context.user_data['birthday'])
    update.message.reply_text(f"Phisical: {user_biorhythm['phisical']}")
    update.message.reply_text(f"Emotional: {user_biorhythm['emotional']}")
    update.message.reply_text(f"Intellectual: {user_biorhythm['intellectual']}")
def calculate_biorhythm(birthdate):
    today = datetime.date.today()
    delta = today - birthdate
    days = delta.days
    phisical = math.sin(2*math.pi*(days/23))
    emotional = math.sin(2*math.pi*(days/28))
    intellectual = math.sin(2*math.pi*(days/33))
    biorhythm = {}
    biorhythm['phisical'] = int(phisical * 10000)/100
    biorhythm['emotional'] = int(emotional * 10000)/100
    biorhythm['intellectual'] = int(intellectual * 10000)/100
    biorhythm['phisical_critical_day'] = (phisical == 0)
    biorhythm['emotional_critical_day'] = (emotional == 0)
    biorhythm['intellectual_critical_day'] = (intellectual == 0)
    return biorhythm

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

Изучение Python в SpaceLAB

Python — это один из самых широко используемых языков программирования, который поддерживается компанией Google. С его помощью можно создавать не только телеграм-ботов, но и веб-сценарии, искусственный интеллект, Data Science-проекты и многое другое. 

В лаборатории SpaceLAB вы можете изучить язык Python полностью бесплатно, с перспективой дальнейшего трудоустройства. Курс содержит мощную теоретическую часть, но в большей степени ориентирован на выполнение практических задач под кураторством опытных менторов.