Введение
На Хабре уже полно статей-туториалов с заголовками «Создание бота на Python», но многие из них используют готовые обертки над HTTP-интерфейсом Bot API Телеграма. Я же использую стандартную библиотеку для отправки и получения GET- и POST-запросов — requests. И так, рассмотрим создание примитивного Телеграм бота, который будет отвечать на все наши текстовые сообщения. Это будет заготовка для дальнейшего расширения.
Шаг 1. Создание бота
Для работы с ботом нужно сначала его создать внутри приложения Телеграм. Для этого напишем первородному боту телеграма @BotFather.
Пишем ему /newbot:
Выполняем его просьбу ввести имя вашего бот. Оно обязательно должно иметь на конце слово bot.
Если имя ввели правильно БотОтец любезно предоставит вам токен вашего бота для доступа к нему через API.
Вот теперь можно приступать к написанию самого бота.
Шаг 2. Авторизация бота
Так как API телеграма для работы с ботами представляет из себя HTTP-интерфейс, нам понадобится модуль для работы с HTTP — requests.
import requests
Принцип работы с ботом заключается получении обновлений, то есть получения действий, которые были совершены с вашим ботом. Для этого отправляем POST запрос в форме "URL + TOKEN + необязательная информация о частоте запросов, о количестве хранимых апдейтов и т.д.", который по умолчанию выдаст 100 последний не подтвержденных обновлений. Даже если бот не активен его обновления будут храниться на сервере до тех пор, пока вы их не обработаете, но не более 24 часов. Обработка обновления происходит посредством его подтверждения. Для подтверждения обновления в запросе /getUpdates передается параметр offset. Все обновления с id меньшим или равным переданному offset будут отмечены как подтверждённые и не будут больше возвращаться сервером. В ответ на запрос вы получите объект Update, который уже сериализован в JSON. Обработку JSON можно посмотреть в документации.
Инициализируем переменные для последующих запросов:
offset = 0 # параметр необходим для подтверждения обновления
URL = 'https://api.telegram.org/bot' # URL на который отправляется запрос
TOKEN = 'token' # токен вашего бота, полученный от @BotFather
data = {'offset': offset 1, 'limit': 0, 'timeout': 0}
Дальше отправляем запрос обновлений:
try: # обрабатываем исключения
request = requests.post(URL TOKEN '/getUpdates', data=data) # собственно сам запрос
except:
print('Error getting updates')
return False
if not request.status_code == 200: return False # проверяем пришедший статус ответа
if not request.json()['ok']: return False
Обработка исключений обязательна, так как бот чувствителен к ошибкам и любой сбой приведет к «пробке» в очереди обновлений.
Шаг 3. Обработка обновлений и отправка сообщений
И так, проходимся по накопившимся обновлениям:
for update in request.json()['result']:
offset = update['update_id'] # подтверждаем текущее обновление
if 'message' not in update or 'text' not in update['message']: # это просто текст или какая-нибудь картиночка?
print('Unknown message')
continue
message_data = { # формируем информацию для отправки сообщения
'chat_id': update['message']['chat']['id'], # куда отправляем сообщение
'text': "I'm <b>bot</b>", # само сообщение для отправки
'reply_to_message_id': update['message']['message_id'], # если параметр указан, то бот отправит сообщение в reply
'parse_mode': 'HTML' # про форматирование текста ниже
}
try:
request = requests.post(URL TOKEN '/sendMessage', data=message_data) # запрос на отправку сообщения
except:
print('Send message error')
return False
if not request.status_code == 200: # проверим статус пришедшего ответа
return False
Немного о форматировании отправляемых сообщений.
Телеграм позволяет выделять некоторый текст в сообщении жирным шрифтом, курсивом, выделять ссылки и код. Существует два способа выделения текста: Markdown или HTML разметкой, что как раз и определяется в передаваемом /sendMessage параметре _parsemode.
Шаг 4. Постоянная прослушка
Осталось только организовать постоянную прослушку обновлений с бота и дело в шляпе. Запрос обновлений и их обработку можно оформить в функцию _checkupdates, а затем просто вызывать ее в бесконечном цикле с обработкой прерывании бота пользователем:
while True:
try:
check_updates()
except KeyboardInterrupt: # порождается, если бота остановил пользователь
print('Interrupted by the user')
break
Вот и готов самый простой бот для Телеграма. Конечно, можно использовать уже готовые оболочки над Bot API, но зачем, если обращение напрямую к HTTP-интерфейсу и так не занимает много места. Пожалуй, на этом остановимся. В будущем можно будет реализовать отправку стикеров, обработку пришедших сообщений, и определенные ответы на определенные слова. Так же с ботами можно общаться посредством команд формата /команда, что очень удобно для каких-либо конкретных, шаблонных запросов к боту.
Комментарии (16)
tmnhy
17.02.2017 10:58И, у вас «ус отклеился», при отправке сообщения проверка на код 200 в блок except попала, она не сработает никогда. Если надо через exception и возвращаемый код обрабатывать, то assert вам в помощь:
try: resp = request.post() assert resp.status_code == 200 except: print('Send message error') return
Неаккуратно как-то, а так да, «Студент лентяй» соответствует ;)
tmnhy
17.02.2017 11:23+5Ну, и по существу…
Начали хорошо:На Хабре уже полно статей-туториалов с заголовками «Создание бота на Python»
А вот дальше… Половина статьи — описание @BotFather, зачем?
Вторая половина статьи — описание двух POST-запросов, а бот — это несколько больше чем пара запросов из API.
И питон тут причем? Разве на других ЯП нет библиотек, аналогичных requests?
CrocodileRed
18.02.2017 10:00Отличный пример того, как не нужно делать.
Эхо-бота так писать еще куда ни шло, но при написании более сложной логики все будет выглядеть не так радужно и сам придется реализовать тот же api, что уже написан до вас, а зачем?
Кроме того, получать апдейты в цикле — не круто по определению.
mizhgun
18.02.2017 20:34И с какой версии Python requests стала стандартной? По вашей же логике, requests — это обертка над urllib3 (и той, в общем случае, нестандартной). Не то, чтобы я придираюсь, просто не наблюдаю в данном случае никакой добавленной ценности от использования requests по сравнению с готовыми обертками, кроме «смотрите, как могу в GET и POST на Pythone».
AlexMt
19.02.2017 01:40while True: try: check_updates() except KeyboardInterrupt: # порождается, если бота остановил пользователь print('Interrupted by the user') break
Вы серьёзно хотите заваливать сервера telegram сообщениями на всю возможную ширину канала, процессорного времени и памяти, которые есть на сервере?
Или всё-таки в этой петле не хватает хотя бы time.sleep(1)?
tmnhy
Почему long polling, а не хуки?
leoismyname
В случае ботов webhook не видится мне каким-то удобным и оптимальным решением.
Быстрый старт для кода выше – укажите токен, запустите python bot.py (предполагаю, что как-то так), для решения на основе webhook, как минимум, помимо перечисленного – описание регистрации webhook в Telegram, наличие домена с сертификатом (теперь уже без разницы, самоподписанный или нет), дополнительный код, который будет обрабатывать входящие запросы.
tmnhy
> В случае ботов webhook не видится мне каким-то удобным и оптимальным решением.
Для «wow я умею в bot api», согласен. А если что-то сложнее?
Тогда, вам и ТСу вопрос. Сколько диалогов, одновременно входящих сообщений такой код выдержит? Какие задержки будут для клиентов бота, когда их станет больше 10?
Вам не кажется, что через хуки, всем этим это рулить будет много проще. Да, доп. затраты есть (домен, сертификат, роутинг до бота, код сервера), но они на самом деле совсем мизерные, по сравнению с кодом мало-мальски полезного бота.
leoismyname
Что-то я не понял, о каких соединениях идет речь? Ответом на LP-запрос будет массив сообщений. Не вижу никаких проблем при работе с 10 клиентами.
tmnhy
Я к тому, что боту могут написать одновременно несколько человек. Пока пример кода ТСа будет обрабатывать массив LP, отвечая запросом на каждый апдейт, кто-то написав боту в это время будет ждать.
leoismyname
Хоть 10, хоть 100 – для текущего кода разницы нет, так как нет и никакой обработки. Даже если он в ответе на LP получит 500 сообщений, так или иначе он их получит и будет отвечать, пусть не одним, а несколькими запросами, но обновления будут.
А что с webhook? Написали 500 человек боту, это инициирует отправку 500 запросов к коду? Что если на 100 запросе код перестал отвечать (упала база или просто не смог)? Как получить те сообщения, на которые код не успел ответить? Сколько времени потребуется, чтобы найти все такие сообщения? Или Телеграм сам их будут повторно рассылать спустя какой-то интервал времени? По-моему тут больше проблем и вопросов появляется, чем преимуществ.
tmnhy
Если HTTP 200 не получит, то да, будет рассылать какое-то время.
Реальность, она несколько отличается от хеллоуворлда, там как раз обработка есть и не всегда это миллисекунды. Если это интерактивный бот, а не спамер, то вполне себе есть разница, получить ответ в течение секунды или минуты.
И я не против LP, но не так как у ТСа.
leoismyname
Вот ты мне и скажи, а почему хуки?
tmnhy
Ну, помимо «мне лень делать сервер» я не вижу препятствий для них.