Всем привет, в этой статье я расскажу как сделать простейшего телеграмм бота на 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}

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