Реализация telegram bot через setWebhook

Jul 21, 2015 22:17 · 565 words · 3 minute read telegram python

Не так давно Telegram открыл API для написание ботов. Примеры уже существующих можно посмотреть на их сайте. Всё общение сводится к отправке команды, её параметров и получение ответа, вот и меня попросили разобраться с этим и написать пример бота. Ну ok, почему бы и нет? :)

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

Начнём, как полагается, с документации. В ней сказано, что для регистрации своего бота надо обратиться к @BotFather с оной просьбой (/newbot). Открываем чат, выполняем команду, получаем токен вида “110201543:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw”. Это мегасуперсекретная последовательность, с помощью которой можно управлять созданным ботом. Для начала посмотрим что нам создали с помощью GET-запроса по адресу https://api.telegram.org/bot110201543:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw/getMe. Ответом будет json, в котором указаны id и название нашего бота. Пообщавшись с @BotFather, можно задать описание, список команд, иконку и пр.

Готовимся писать код

Настроив внешний вид бота в мессенджере, пришло время определиться как наш код будет получать сообщения. Доступны 2 варианта:

  1. Раз в n секунд опрашивать https://api.telegram.org/bot110201543:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw/getUpdates и получать список сообщений, пришедший за это время;
  2. Установить обработчик с помощью https://api.telegram.org/bot110201543:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw/setWebhook, который будет дёргаться при каждом новом сообщении.

Первый путь мне показался примитивным и уж более не безопасным - секретный ключ будет видно прямо в коде, поэтому я выбрал второй. И тут начинается ад! Для его работы обязателен веб-сервер с белым IP и действительным не самоподписанным сертификатом. Ну что ж, попробуем раздобыть сертификат… Я пошёл на сайт регистратора и - о чудо! - он раздавал его бесплатно :) Будем считать, что повезло. Спустя пару дней после запроса мне пришло 4 ключа: приватный, публичный, промежуточный, корневой и запросный. Зачем нужен последний я так и не понял, но! Аккуратно устанавливаем всё это хозяйство согласно статьи. Тут меня ждали 2 засады. Первое - обращаем внимание на какой домен выдан сертификат. В моём случае это tyvik.ru и www.tyvik.ru. Т.е. домен, допустим, telegram.tyvik.ru уже будет не защищён => повесить на него бота нельзя. Второе - в файле сертификата должен быть полный путь до корневого, включая все промежуточные. В противном случае сообщения от telegram в наш веб-хук просто не будут приходить. Да-да - ни ошибок, ни мусора - ни-че-го. Сволочи! Я 3 часа потратил пытаясь найти что же ко мне приходит! То, что telegram.tyvik.ru не защищён https'ом я понял просто обратившись по адресу вебхука (на тот момент https://telegram.tyvik.ru/bot/) и увидев перечёркнутый значёк протокола. А вот про промежуточный нигде в документации не сказано. Решение я нашёл где-то на реддите и сейчас уже не повторю сей подвиг.

Кодим

Для реализации задуманного я решил взять flask. Почему? А потому что я его не знаю :) В боте много бизнес-логики быть не должно, так что микрофреймворк - самое то. Создание виртуального окружения и непосредственно кодинг описывать не буду, приведу сразу весь код:

#!/usr/bin/env python
# coding: utf-8
from __future__ import unicode_literals
import json

from twx.botapi import TelegramBot, ReplyKeyboardMarkup
from flask import Flask, request
app = Flask(__name__)

bot = TelegramBot('bot110201543:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw')
@app.route("/telegram/", methods=['POST'])
def hello():
    message = json.loads(request.data)
    if message['message']['text'] == '/ping':
        bot.send_message(message['message']['chat']['id'], 'Pong!').wait()
    return 'ok'

Итак, поехали. Во-первых, я нашёл библиотеку для работы с сообщениями telegram - twx.botapi. С помощью неё можно отправить сообщение, содержащее картинку, геопозицию, стикер, а также установить клавиатуру (варианты ответов). Во-вторых, строка декоратора говорит, что мы будем принимать сообщения по адресу /telegram/. В-третьих, само сравнение с командой и ответ на него. Замечу, что вызов wait() обязателен - он закрывает сообщение (делает flush), отправляя его собеседнику. Вот такой вот простенький бот, с которым можно поиграть в ping-pong :) Настройку nginx+uwsgi описывать не буду, т.к. она не отличается от таковой для Django.