Всем привет, в этой статье я расскажу как сделать простейшего телеграмм бота на Python для отправки текущей погоды в Москве.


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


Технологии и API:


  • Python — язык программирования,
  • Flask — фреймворк для создания веб-приложений,
  • Telegram Bot API,
  • Weatherstack API,
  • Ngrok — сервис для создания туннеля к localhost.

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


  1. Пользователь пишет сообщение тел��грамм боту.
  2. Telegram пересылает сообщение пользователя на сервер.
  3. Сервер запрашивает информацию о погоде у Weatherstack.
  4. Сервер отсылает информацию о погоде в Telegram.
  5. Пользователь получает информацию о погоде.

Регистрация телеграмм бота


На этом этапе нам нужно создать бота и получить к нему доступы. Для этого запускаем бота botfather в Telegram командой ниже.


/start

Создаем нового бота согласно инструкциям из сообщения от бота.


Регистрация телеграмм бота!


Бот создан, но если ему написать какое-нибудь сообщение, он никак на него не отреагирует. Исправим это.


Справка о Flask


Flask — фреймворк для создания веб-приложений на языке программирования Python, использующий набор инструментов Werkzeug, а также шаблонизатор Jinja2. Относится к категории так называемых микрофреймворков — минималистичных каркасов веб-приложений, сознательно предоставляющих лишь самые базовые возможности.


Поддерживается установка посредством пакетного менеджера PyPI, версия 1.0 совместима с Python 2.7, Python 3.3 и выше.


Источник.


Установка Flask


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


$ mkdir weather_bot
$ cd weather_bot
$ python3 -m venv venv

После завершения установки и активации виртуального окружения установим Flask.


(venv)$ pip install Flask

Подробнее на странице Installation.


Запуск простейшего приложения Flask


В директории weather_bot создадим файл app.py с содержимым.


from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, World!'

Запустим полученное приложение.


(venv)$ export FLASK_APP=app.py
(venv)$ flask run
 * Running on http://127.0.0.1:5000/

Перейдем по адресу http://127.0.0.1:5000/ и убедимся, что отображается текст "Hello, World!".
Подробнее на странице Quickstart.


Создание туннеля к localhost с помощью ngrok


Ngrok — это сервис, позволяющий создавать туннели на локальный компьютер пользователя.


  1. Зарегистрируемся на сайте ngrok.
  2. Выполним установку по инструкции.
  3. Запустим HTTP туннель на 5000 порте с помощью команды терминала ниже.

$ ./ngrok http 5000

Получение сообщений из телеграмм бота


Для того, чтобы Telegram пересылал сообщения на наш сервер, нужно сообщить ему адрес. У нас уже создан туннель, поэтому передадим адрес туннеля в Telegram. Это делается с помощью метода POST setWebhook. Подробнее на странице документации.


$ curl --location --request POST 'https://api.telegram.org/bot{token}/setWebhook' \
--header 'Content-Type: application/json' \
--data-raw '{
    "url": "{url}"
}'

где {token} — токен вида 840446984:AAFuVTW-FYP5tJVu8mqhc9y4E0j1fr2lCD0, который нам прислал BotFather,
{url} — адрес вида https://32515a83.ngrok.io, который отобразился в консоли ngrok. Обратите внимание на протокол. Он должен быть https, иначе Telegram не примет url.


Подробнее о cURL на странице wiki.


Если получен ответ "ok": true, то все в порядке.


{
    "ok": true,
    "result": true,
    "description": "Webhook was set"
}

Проверим, что сообщения доходят до нашего локального сервера. Для этого в файле app.py обновим код.


from flask import Flask, request

app = Flask(__name__)

@app.route("/", methods=["GET", "POST"])
def receive_update():
    if request.method == "POST":
        print(request.json)
    return {"ok": True}

Перезапустим Flask. Для этого остановим сервер из терминала комбинацией клавиш Ctrl+C и повторно запустим его.


(venv)$ flask run

Отправим нашему боту сообщение с произвольным текстом. В консоли должно отобразиться тело запроса из Telegram. Это признак того, что все в порядке.


Сообщение в консоли!


Ответ на сообщения пользователей


Мы научились получать сообщения от пользователей, но еще не умеем их отправлять. Для проверки отправки сообщений будем отвечат�� текстом "pong" на все сообщения.


Для отправки сообщений пользователям в API Telegram используется метод sendMessage. Подробнее на странице документации.


Запросы будем делать с помощью библиотеки requests, которой нет в списке стандартных, поэтому установим ее. Для этого в терминале с активированным виртуальным окружением выполним команду нижу.


(venv)$ pip install requests

Добавим строку импорта requests сразу за строкой from flask import Flask, request в app.py.


import requests

Для отправки сообщений нам нужно знать id чата. Его можно вытащить из тела Telegram-запроса.


chat_id = request.json["message"]["chat"]["id"]

Напишем функцию для отправки сообщений, в которую будем передавать id чата и текст сообщения.


def send_message(chat_id, text):
    method = "sendMessage"
    token = "840446984:AAFuVTW-FYP5tJVu8mqhc9y4E0j1fr2lCD0"
    url = f"https://api.telegram.org/bot{token}/{method}"
    data = {"chat_id": chat_id, "text": text}
    requests.post(url, data=data)

Подробнее о библиотеке requests на странице.


Добавим вызов функции send_message() из receive_update().


send_message(chat_id, "pong")

Вот так выглядит код в файле app.py


from flask import Flask, request
import requests

app = Flask(__name__)

def send_message(chat_id, text):
    method = "sendMessage"
    token = "840446984:AAFuVTW-FYP5tJVu8mqhc9y4E0j1fr2lCD0"
    url = f"https://api.telegram.org/bot{token}/{method}"
    data = {"chat_id": chat_id, "text": text}
    requests.post(url, data=data)

@app.route("/", methods=["GET", "POST"])
def receive_update():
    if request.method == "POST":
        print(request.json)
        chat_id = request.json["message"]["chat"]["id"]
        send_message(chat_id, "pong")
    return {"ok": True}

Отправка пользователю информации о погоде


Используем метод current Weatherstack API для получения информации о погоде.
Передадим 2 обязательных Query Params: access_key — секретный ключ вида 86a3fe972756lk34a6a042bll348b1e3, который можно получить после регистрации, и query — город, по которому получаем информацию о погоде, в нашем случае — Moscow.


Подробнее на странице документации.


Добавим функцию для получения текущей температуры в Москве после строки app = Flask(__name__).


def get_weather():
    params = {"access_key": "86a3fe972756lk34a6a042bll348b1e3", "query": "Moscow"}
    api_result = requests.get('http://api.weatherstack.com/current', params)
    api_response = api_result.json()
    return f"Сейчас в Москве {api_response['current']['temperature']} градусов"

Внутри функции receive_update() вместо сообщения с текстом "pong" передадим погоду.


weather = get_weather()
send_message(chat_id, weather)

Код всего Flask-приложения состоит из 3 функций: получения сообщений из Telegram, отправка сообщений в Telegram и получение информации о погоде из Weatherstack.


from flask import Flask, request
import requests

app = Flask(__name__)

def get_weather():
    params = {"access_key": "86a3fe972756lk34a6a042bll348b1e3", "query": "Moscow"}
    api_result = requests.get('http://api.weatherstack.com/current', params)
    api_response = api_result.json()
    return f"Сейчас в Москве {api_response['current']['temperature']} градусов"

def send_message(chat_id, text):
    method = "sendMessage"
    token = "840446984:AAFuVTW-FYP5tJVu8mqhc9y4E0j1fr2lCD0"
    url = f"https://api.telegram.org/bot{token}/{method}"
    data = {"chat_id": chat_id, "text": text}
    requests.post(url, data=data)

@app.route("/", methods=["GET", "POST"])
def receive_update():
    if request.method == "POST":
        print(request.json)
        chat_id = request.json["message"]["chat"]["id"]
        weather = get_weather()
        send_message(chat_id, weather)
    return {"ok": True}

Вот и всё! Таким несложным образом мы научили наш бот информировать нас о погоде в Москве.