Бот для Telegram на Python: от первой строчки кода до запуска на Heroku

X-DMIN

КИДАЛА

X-DMIN

КИДАЛА
Регистрация
2 Июл 2018
Сообщения
1,325
Реакции
940
Репутация
0
Вступление
На первый взгляд, Telegram — это просто очередной мессенджер. Реклама гласит, что он быстр, защищён, в нём нет рекламы и всё такое. Но у него есть важная фича — боты!

Бот — это не просто «автоответчик». Его правильнее считать автоматизированным помощником. Представим, что вы хотите поделиться в групповом чате видео с YouTube, но ссылки у вас нет.

Вот стандартный алгоритм действий без бота:

  • открываем YouTube в браузере;
  • находим видео;
  • нажимаем на «Поделиться» (и надеемся, что в списке будет наш мессенджер);
  • если нашего мессенджера в списке не оказалось, копируем ссылку на видео;
  • возвращаемся в мессенджер и делимся ссылкой.
Да, многие привыкли делать именно так. Однако бот позволит действовать следующим образом:

  • переписываемся в мессенджере;
  • вводим @vid и название видео, которое вы хотите найти и скинуть в беседу;
  • отправляем его.
Удобно, не правда ли? И это лишь одна из возможностей

Telegram позволяет пользователям создавать своих ботов. Ответим на вопрос «Почему мне стоит написать своего?» — это, пожалуй, самый простой способ понять, что такое API.

Круто, начинаем писать бота?
Обо всём по порядку. Сперва нужно зарегистрироваться в Telegram. Наиболее удобно использовать

Пожалуйста Авторизуйтесь или Зарегистрируйтесь для просмотра скрытого текста.

для знакомства с основными принципами работы ботов и API.

Откройте приложение, найдите @BotFather и начните беседу. Отправьте команду /newbot и следуйте инструкциям. После выполнения первых шагов вы получите:

Собственно, вот и всё. На данном этапе ваш бот полностью пассивен.

Вам нужно начать беседу с ботом. Введите в поисковой строке его имя и нажмите на кнопку /start. Отправьте сообщение, например, «Привет». Это первое сообщение очень важно, поскольку оно станет первым обновлением, которое получит ваш бот.

Если вы в первый раз работаете с API, то разобраться вам поможет браузер. Откройте новую вкладку и воспользуйтесь Telegram API, перейдя по ссылке:

Код:
https://api.telegram.org/bot<ваш_токен>/getUpdates

Открыв этот адрес в браузере, вы отправите запрос на сервер Telegram, и он ответит вам в формате JSON. Ответ чем-то похож на словарь в Python. Вы увидите что-то вроде такого:

Код:
{
  "ok":true,
  "result":[{
    "update_id":523349956,
    "message":{
      "message_id":51,
      "from":{
        "id":303262877,
        "first_name":"YourName"
      },
      "chat":{
        "id":303262877,
        "first_name":"YourName",
        "type":"private"
      },
      "date":1486829360,
      "text":"Hello"
    }
  }]
}

Если вы изучите раздел

Пожалуйста Авторизуйтесь или Зарегистрируйтесь для просмотра скрытого текста.

/sendMessage в документации, то увидите, что он принимает два параметра: chat_id и text. Вы можете создавать цепочки параметров в адресной строке браузера, используя ? для первого и & для всех последующих. Команда для отправки сообщения будет выглядеть так:

Код:
/sendMessage?chat_id=303262877&text=test

И когда же будет код?
Если вы пользуетесь Windows и у вас не установлен Python, скачать его можно

Пожалуйста Авторизуйтесь или Зарегистрируйтесь для просмотра скрытого текста.

. Вообще версия не важна, но в этой статье будет использоваться Python 3.x. Если же у вас Linux или macOS, то обе версии (или, как минимум, Python 2.x) уже установлены.

Затем нужно установить pip. В версиях выше Python 2.7.9 и Python 3.4, а также на macOS/Linux он уже есть. Проверить это можно командой pip --version в терминале. Если же по каким-то причинам он отсутствует, установить его можно при помощи команды:
Код:
$ sudo apt-get install python-pip
Загвоздка в том, что разные версии Python используют разные pip. Если у вас macOS, вы можете попробовать следовать

Пожалуйста Авторизуйтесь или Зарегистрируйтесь для просмотра скрытого текста.

со Stack Overflow. В случае с Windows вам нужно скачать

Пожалуйста Авторизуйтесь или Зарегистрируйтесь для просмотра скрытого текста.

, открыть командную строку, перейти в директорию со скачанным файлом и выполнить команду:
Код:
$ python get-pip.py
Самое сложное позади. Установим пакет requests при помощи pip:
Код:
$ pip install requests
А теперь — код
Теперь, когда вы поняли, что такое API, и установили все необходимые инструменты, напишем скрипт, который будет проверять обновления и отвечать на новые сообщения.

Сперва бот должен проверить обновления. Первое сообщение можно расценивать как самое свежее, но getUpdates возвращает все обновления за последние 24 часа. Напишем небольшой скрипт, чтобы получить самое последнее обновление:

Код:
import requests

url = "https://api.telegram.org/bot<ваш_токен>/"


def get_updates_json(request): 
    response = requests.get(request + 'getUpdates')
    return response.json()


def last_update(data): 
    results = data['result']
    total_updates = len(results) - 1
    return results[total_updates]

Словарь обновлений состоит из двух элементов: ok и results. Нас интересует вторая часть — список всех обновлений, полученных ботом за последние 24 часа.

Узнать больше о библиотеке requests можно, прочитав

Пожалуйста Авторизуйтесь или Зарегистрируйтесь для просмотра скрытого текста.

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

Теперь нужно добавить ещё 2 функции. Первая будет доставать chat_id из обновления, а вторая — отправлять сообщение.

Код:
def get_chat_id(update): 
    chat_id = update['message']['chat']['id']
    return chat_id

def send_mess(chat, text): 
    params = {'chat_id': chat, 'text': text}
    response = requests.post(url + 'sendMessage', data=params)
    return response

chat_id = get_chat_id(last_update(get_updates_json(url)))
send_mess(chat_id, 'Your message goes here')

Помните, как мы объединяли параметры при помощи ? и &? Вы можете сделать то же самое, добавив словарь в качестве второго дополнительного параметра в функциях get/post из пакета requests.

Скрипт готов, но он не идеален. Главным минусом является необходимость запускать его каждый раз, когда мы хотим, чтобы бот отправил сообщение. Исправим это. Чтобы бот слушал сервер и получал обновления, нам нужно запустить основной цикл. На новой строке, после import requests, добавьте from time import sleep. После этого замените две последние строки на следующий код:

Код:
def main(): 
    update_id = last_update(get_updates_json(url))['update_id']
    while True:
        if update_id == last_update(get_updates_json(url))['update_id']:
           send_mess(get_chat_id(last_update(get_updates_json(url))), 'test')
           update_id += 1
        sleep(1)       

if __name__ == '__main__': 
    main()

Хотя мы и добавили таймаут в 1 секунду, пример выше можно использовать только в обучающих целях, поскольку он использует частые опросы (short polling). Это плохо влияет на сервера Telegram, поэтому их нужно избегать. Есть ещё два способа получения обновлений через API — длинные опросы (long polling) и вебхуки (webhooks). Если мы будем использовать способ получения обновлений через getUpdates без параметров, то запросы будут происходить слишком часто.

Поскольку мы начали использовать в скрипте основной цикл, мы должны переключиться на длинные опросы. Сперва изменим первую функцию, добавив в неё параметр timeout. Сам по себе он не уменьшит частоту проверки обновлений и будет работать только в том случае, когда обновлений нет. Чтобы помечать уже просмотренные обновления, нужно добавить параметр сдвига offset:

Код:
def get_updates_json(request): 
    params = {'timeout': 100, 'offset': None}
    response = requests.get(request + 'getUpdates', data=params)
    return response.json()

Теперь бот должен работать нормально, но его всё ещё можно улучшить. Давайте инкапсулируем все функции в один класс. Должно получиться что-то вроде этого:

Код:
import requests 
import datetime

class BotHandler:

    def __init__(self, token):
        self.token = token
        self.api_url = "https://api.telegram.org/bot{}/".format(token)

    def get_updates(self, offset=None, timeout=30):
        method = 'getUpdates'
        params = {'timeout': timeout, 'offset': offset}
        resp = requests.get(self.api_url + method, params)
        result_json = resp.json()['result']
        return result_json

    def send_message(self, chat_id, text):
        params = {'chat_id': chat_id, 'text': text}
        method = 'sendMessage'
        resp = requests.post(self.api_url + method, params)
        return resp

    def get_last_update(self):
        get_result = self.get_updates()

        if len(get_result) > 0:
            last_update = get_result[-1]
        else:
            last_update = get_result[len(get_result)]

        return last_update

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

Код:
greet_bot = BotHandler(token) 
greetings = ('здравствуй', 'привет', 'ку', 'здорово') 
now = datetime.datetime.now()


def main(): 
    new_offset = None
    today = now.day
    hour = now.hour

    while True:
        greet_bot.get_updates(new_offset)

        last_update = greet_bot.get_last_update()

        last_update_id = last_update['update_id']
        last_chat_text = last_update['message']['text']
        last_chat_id = last_update['message']['chat']['id']
        last_chat_name = last_update['message']['chat']['first_name']

        if last_chat_text.lower() in greetings and today == now.day and 6 <= hour < 12:
            greet_bot.send_message(last_chat_id, 'Доброе утро, {}'.format(last_chat_name))
            today += 1

        elif last_chat_text.lower() in greetings and today == now.day and 12 <= hour < 17:
            greet_bot.send_message(last_chat_id, 'Добрый день, {}'.format(last_chat_name))
            today += 1

        elif last_chat_text.lower() in greetings and today == now.day and 17 <= hour < 23:
            greet_bot.send_message(last_chat_id, 'Добрый вечер, {}'.format(last_chat_name))
            today += 1

        new_offset = last_update_id + 1

if __name__ == '__main__': 
    try:
        main()
    except KeyboardInterrupt:
        exit()

еперь вы можете улучшать бота так, как захотите. Можно, например, настроить отправку медиафайлов или добавить собственные кнопки.

Устрой деплой
Последним шагом будет развёртывание вашего бота на сервере. Наверняка у вас его нет, да и покупать не хочется — но это необязательно. Сейчас существует множество бесплатных облачных решений, из которых мы выберем Heroku.

Сперва зарегистрируйтесь на GitHub. Создайте аккаунт (рано или поздно он вам точно пригодится) и установите Git. На Linux выполните следующую команду:

Код:
$ sudo apt-get install git-all
На

Пожалуйста Авторизуйтесь или Зарегистрируйтесь для просмотра скрытого текста.

и

Пожалуйста Авторизуйтесь или Зарегистрируйтесь для просмотра скрытого текста.

его нужно скачать и установить вручную. И не забудьте

Пожалуйста Авторизуйтесь или Зарегистрируйтесь для просмотра скрытого текста.

на Heroku.

Установите virtualenv:

Код:
$ pip install virtualenv
Теперь вам нужно навести порядок в файлах. Создайте новую папку и перейдите в неё в терминале или командной строке. Инициализируйте в ней virtualenv:

Код:
$ virtualenv my_env
Имя не имеет значения, но лучше сделать его интуитивно понятным. Перейдите в папку my_env.

Теперь вам нужно склонировать git-репозиторий. Введите следующую команду:

Код:
$ git clone https://github.com/имя_вашего_профиля/имя_вашего_репозитория
Поместите скрипт в папку, полученную в результате выполнения команды git clone.

Вернитесь в папку my_env и запустите virtualenv:

  • На Windows:
Код:
$ scripts\activate.bat
  • На Linux/macOS:
Код:
$ source bin/activate
Если вы успешно запустили virtualenv, приглашение командной строки должно начинаться с (my_env).

Перейдите в папку репозитория и ещё раз установите модуль requests:

Код:
$ pip install requests
Теперь нужно создать список зависимостей Heroku. Это несложно. Введите:

Код:
$ pip freeze > requirements.txt
Создайте Procfile. В этом файле вам нужно разместить инструкции по работе с вашим скриптом. Имя файла обязательно должно быть Procfile (Procfile.windows в случае с Windows). У него не должно быть других расширений. Содержимое файла должно быть таким (замените my_bot на имя вашего скрипта):

Код:
web: python my_bot.py
Добавьте файл __init__.py в вашу папку. Он может быть пустым, но он должен там быть.

Отправьте коммит с изменениями в репозиторий:

Код:
$ git init
$ git add .
$ git commit -m ‘короткое сообщение, описывающее изменения в коммите’
$ git push -u https://github.com/имя_вашего_профиля/имя_вашего_репозитория
Теперь развернём бота на Heroku. Можно использовать и панель управления на сайте, но мы потренируемся делать всё через консоль. Если вы столкнётесь с какими-то проблемами, обратитесь

Пожалуйста Авторизуйтесь или Зарегистрируйтесь для просмотра скрытого текста.

.

Рассмотрим лишь те действия, которые нужны для развёртывания приложения. Если вы пользуетесь macOS или Windows, установите интерфейс командной строки, следуя

Пожалуйста Авторизуйтесь или Зарегистрируйтесь для просмотра скрытого текста.

.

Если вы пользуетесь Ubuntu, используйте следующие команды:

Код:
$ sudo add-apt-repository "deb https://cliassets.heroku.com/branches/stable/apt ./"
$ curl -L https://cli-assets.heroku.com/apt/release.key |
$ sudo apt-key add -
$ sudo apt-get update
$ sudo apt-get install heroku
Если возникнут ошибки, проверьте, присутствуют ли все зависимости.

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

Код:
$ heroku login
$ heroku create
$ git push heroku master
$ heroku ps:scale web=1
$ heroku open
С этого момента ваше приложение должно работать на сервере Heroku. Если что-то пойдёт не так, проверить логи можно следующим образом:

Код:
$ heroku logs --tail
Коды ошибок можно найти

Пожалуйста Авторизуйтесь или Зарегистрируйтесь для просмотра скрытого текста.

.

Бесплатный аккаунт накладывает некоторые ограничения. Тем не менее теперь у вас есть полностью рабочий бот — поздравляем! Продолжить изучение возможностей ботов для Telegram вам поможет

Пожалуйста Авторизуйтесь или Зарегистрируйтесь для просмотра скрытого текста.

— в нём используют PHP, но суть остаётся той же.
 
Сверху