Часть 1. Простой Telegram бот на Python, метод getUpdates
Часть 2. Telegram бот на Python, отправка файлов, встроенная клавиатура
Часть 3. Telegram бот на Python, работа с геолокацией пользователяа
Вот и третья часть, где мы продолжим создавать телеграм бота с помощью Python и библиотеки requests
. Сегодня вы узнаете как работать с геолокацией пользователя, и как с помощью бесплатного "API геокодера" определить адрес по координатам.
Функционал бота будем выглядеть следующим образом: Пользователь нажимает кнопку в боте и отправляет свою геолокацию. А в ответ к нему бот пишет, в каком городе он находится.
Это часть добавлена по просьбе одно из читателей сайта ramziv.com :)
Код телеграм бота
Перед тем как продолжить писать код, предлагаю убрать для удобства весь лишний функционал, что мы добавили в прошлых частях. Оставить только настраиваемую клавиатуру и ответ на приветствие.
Прошу вас обратить внимание на функцию
run
, я добавил новое условие, так как бот прекращал работу ошибкой, если вместо текста отправить ему файл, картинку или геолокацию. Данный код будет работать на Python 3.8 и выше.
#https://ramziv.com/blog/29
import requests
import time
import json
TOKEN = 'ТОКЕН'
URL = 'https://api.telegram.org/bot'
def get_updates(offset=0):
result = requests.get(f'{URL}{TOKEN}/getUpdates?offset={offset}').json()
return result['result']
def send_message(chat_id, text):
requests.get(f'{URL}{TOKEN}/sendMessage?chat_id={chat_id}&text={text}')
def reply_keyboard(chat_id, text):
reply_markup ={ "keyboard": [["Привет", "Hello"]], "resize_keyboard": True, "one_time_keyboard": True}
data = {'chat_id': chat_id, 'text': text, 'reply_markup': json.dumps(reply_markup)}
requests.post(f'{URL}{TOKEN}/sendMessage', data=data)
def check_message(chat_id, message):
if message.lower() in ['привет', 'hello']:
send_message(chat_id, 'Привет :)')
else:
reply_keyboard(chat_id, 'Я не понимаю тебя :(')
def run():
update_id = get_updates()[-1]['update_id'] # Сохраняем ID последнего отправленного сообщения боту
while True:
time.sleep(2)
messages = get_updates(update_id) # Получаем обновления
for message in messages:
# Если в обновлении есть ID больше чем ID последнего сообщения, значит пришло новое сообщение
if update_id < message['update_id']:
update_id = message['update_id']# Сохраняем ID последнего отправленного сообщения боту
if (user_message := message['message'].get('text')): # Проверим, есть ли текст в сообщении
check_message(message['message']['chat']['id'], user_message) # Отвечаем
if __name__ == '__main__':
run()
Геолокация пользователя
Что бы получить геолокацию пользователя, добавим в нашу клавиатуру новую кнопку с текстом "Где я нахожусь" и укажем ключ request_location
со значением True
.
#https://ramziv.com/blog/29
def reply_keyboard(chat_id, text):
reply_markup ={ "keyboard": [["Привет", "Hello"], [{"request_location":True, "text":"Где я нахожусь"}]], "resize_keyboard": True, "one_time_keyboard": True}
data = {'chat_id': chat_id, 'text': text, 'reply_markup': json.dumps(reply_markup)}
requests.post(f'{URL}{TOKEN}/sendMessage', data=data)
Так же добавим новое условие в функцию run
, что бы проверить, что вместо текста в сообщение была отправлена геолокация. Если условие истина, выведем на экран сообщение с координатами пользователя.
#https://ramziv.com/blog/29
def run():
update_id = get_updates()[-1]['update_id'] # Сохраняем ID последнего отправленного сообщения боту
while True:
time.sleep(2)
messages = get_updates(update_id) # Получаем обновления
for message in messages:
# Если в обновлении есть ID больше чем ID последнего сообщения, значит пришло новое сообщение
if update_id < message['update_id']:
update_id = message['update_id']# Сохраняем ID последнего отправленного сообщения боту
if (user_message := message['message'].get('text')): # Проверим, есть ли текст в сообщении
check_message(message['message']['chat']['id'], user_message) # Отвечаем
if (user_location := message['message'].get('location')): # Проверим, если ли location в сообщении
print(user_location)
Запустим, и проверим нашего бота.
Если нажать кнопку "Где я нахожусь", всплывает push сообщения, где телеграм спрашивает разрешение на отправку геолокации боту.
Нажимаем "Ок", и видим что в консоли появилось сообщение с координатами пользователя.
{'latitude': 54.602916, 'longitude': 53.699995}
Все работает, теперь можно переходить к следующему этапу, где мы научимся определять адрес по координатам с помощью обратного геокодирования.
API геокодер
LocationIQ API: позволяет определять координаты и получать сведения об объекте по его адресу (прямое геокодирование) и наоборот, определять адрес объекта по его координатам (обратное геокодирование). Сервис предлагает бесплатный план, где разрешено делать 5000 запросов в день, с 2 запросами в секунду.
Зарегистрируемся, и получим токен.
Проверим как работает API, откроем в новой вкладке ссылку заменив токен и ранее полученные координаты пользователя.
https://eu1.locationiq.com/v1/reverse.php?key=<TOKEN>&lat=<LATITUDE>&lon=<LONGITUDE>&format=json
В ответ мы получим JSON, где указанно все о переданных координатах.
Теперь осталось повторить все на Python. Создадим функцию geocoder
которая будет принимать координаты, и возвращать текст 'Твое местоположение: + адрес из API геокодера'.
#https://ramziv.com/blog/29
def geocoder(latitude, longitude):
token = 'pk.токен'
headers = {"Accept-Language": "ru"}
address = requests.get(f'https://eu1.locationiq.com/v1/reverse.php?key={token}&lat={latitude}&lon={longitude}&format=json', headers=headers).json()
return f'Твое местоположение: {address.get("display_name")}'
Почти все готово, осталось изменить функцию run
, что бы бот отправил пользователю сообщение с его местоположением, если в боте нажали кнопку "Где я нахожусь".
Полный код.
#https://ramziv.com/blog/29
import requests
import time
import json
TOKEN = 'ТОКЕН'
URL = 'https://api.telegram.org/bot'
def get_updates(offset=0):
result = requests.get(f'{URL}{TOKEN}/getUpdates?offset={offset}').json()
return result['result']
def send_message(chat_id, text):
requests.get(f'{URL}{TOKEN}/sendMessage?chat_id={chat_id}&text={text}')
def reply_keyboard(chat_id, text):
reply_markup ={ "keyboard": [["Привет", "Hello"], [{"request_location":True, "text":"Где я нахожусь"}]], "resize_keyboard": True, "one_time_keyboard": True}
data = {'chat_id': chat_id, 'text': text, 'reply_markup': json.dumps(reply_markup)}
requests.post(f'{URL}{TOKEN}/sendMessage', data=data)
def check_message(chat_id, message):
if message.lower() in ['привет', 'hello']:
send_message(chat_id, 'Привет :)')
else:
reply_keyboard(chat_id, 'Я не понимаю тебя :(')
def geocoder(latitude, longitude):
token = 'pk.токен'
headers = {"Accept-Language": "ru"}
address = requests.get(f'https://eu1.locationiq.com/v1/reverse.php?key={token}&lat={latitude}&lon={longitude}&format=json', headers=headers).json()
return f'Твое местоположение: {address.get("display_name")}'
def run():
update_id = get_updates()[-1]['update_id'] # Присваиваем ID последнего отправленного сообщения боту
while True:
time.sleep(2)
messages = get_updates(update_id) # Получаем обновления
for message in messages:
# Если в обновлении есть ID больше чем ID последнего сообщения, значит пришло новое сообщение
if update_id < message['update_id']:
update_id = message['update_id']# Присваиваем ID последнего отправленного сообщения боту
if (user_message := message['message'].get('text')): # Проверим, есть ли текст в сообщении
check_message(message['message']['chat']['id'], user_message) # Отвечаем
if (user_location := message['message'].get('location')): # Проверим, если ли location в сообщении
latitude = user_location['latitude']
longitude = user_location['longitude']
send_message(message['message']['chat']['id'], geocoder(latitude, longitude))
if __name__ == '__main__':
run()
Проверим как работает.
Ответ с местоположением выглядит смешно :), но это сделано для демонстрации. Вы можете в функции geocoder
заменить address.get("display_name")
на address["address"].get("town")
и в ответ получать только название города.
Конец
Теперь вы знаете как работать с геолокацией пользователя. Надеюсь полученный знания помогут вам реализовать, что ни будь интересное в вашем проекте. Если у вас есть пожелания для следующей части, прошу отпишите мне в обратной связи.