Обновить bot-tg.py

This commit is contained in:
yogurtmenn 2025-03-25 12:29:50 +00:00
parent e17de635b5
commit 8cb2843fd0

158
bot-tg.py
View file

@ -6,18 +6,30 @@ import requests
import asyncio import asyncio
from config import BOT_TOKEN, WEATHER_API_KEY from config import BOT_TOKEN, WEATHER_API_KEY
# Настройка логирования # настройка логирования
logging.basicConfig( logging.basicConfig(
level=logging.INFO, level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
) )
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# Инициализация бота # пасхалочка
CUSTOM_CITY_PHOTOS = {
"киев": {
"photo_url": "https://imgur.com/a/jzVHNoF",
"author": "Wikipedia"
},
"kyiv": {
"photo_url": "https://imgur.com/a/jzVHNoF",
"author": "Wikipedia"
}
}
# инициализация бота
bot = Bot(token=BOT_TOKEN) bot = Bot(token=BOT_TOKEN)
dp = Dispatcher() dp = Dispatcher()
# Клавиатура # клавиатура
def get_keyboard(): def get_keyboard():
builder = ReplyKeyboardBuilder() builder = ReplyKeyboardBuilder()
builder.button(text="🌤️ Узнать погоду") builder.button(text="🌤️ Узнать погоду")
@ -25,10 +37,10 @@ def get_keyboard():
return builder.as_markup(resize_keyboard=True) return builder.as_markup(resize_keyboard=True)
async def get_coordinates(city: str): async def get_coordinates(city: str):
"""Получение координат города через Nominatim""" """Получение координат города"""
url = f"https://nominatim.openstreetmap.org/search?city={city}&format=json" url = f"https://nominatim.openstreetmap.org/search?city={city}&format=json"
headers = {"User-Agent": "CityPhotoBot/1.0"} headers = {"User-Agent": "CityPhotoBot/1.0"}
try: try:
response = requests.get(url, headers=headers, timeout=10) response = requests.get(url, headers=headers, timeout=10)
if response.status_code == 200: if response.status_code == 200:
@ -45,27 +57,34 @@ async def get_coordinates(city: str):
async def get_weather(lat: float, lon: float, city_name: str) -> str: async def get_weather(lat: float, lon: float, city_name: str) -> str:
"""Получение данных о погоде""" """Получение данных о погоде"""
url = f"https://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&appid={WEATHER_API_KEY}&units=metric&lang=ru" url = f"https://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&appid={WEATHER_API_KEY}&units=metric&lang=ru"
try: try:
response = requests.get(url, timeout=10) response = requests.get(url, timeout=10)
if response.status_code == 200: if response.status_code == 200:
data = response.json() data = response.json()
weather_info = ( weather_info = (
f"🌆 Город: {city_name}\n" f"🌆 Город: {city_name}\n"
f"🌡 Температура: {data['main']['temp']:.1f}°C\n" f"🌡 Температура: {data['main']['temp']:.1f}°C\n"
f"🧭 Ощущается как: {data['main']['feels_like']:.1f}°C\n" f"🧭 Ощущается как: {data['main']['feels_like']:.1f}°C\n"
f"💨 Ветер: {data['wind']['speed']} м/с\n" f"💨 Ветер: {data['wind']['speed']} м/с\n"
f"☁️ Погода: {data['weather'][0]['description'].capitalize()}\n" f"☁️ Погода: {data['weather'][0]['description'].capitalize()}\n"
f"💧 Влажность: {data['main']['humidity']}%" f"💧 Влажность: {data['main']['humidity']}%"
) )
return weather_info return weather_info
except Exception as e: except Exception as e:
logger.error(f"Weather API error: {e}") logger.error(f"Weather API error: {e}")
return "Не удалось получить данные о погоде" return "Не удалось получить данные о погоде"
async def get_city_photo(city: str, lat: float = None, lon: float = None): async def get_city_photo(city: str):
"""Поиск фотографии города через Wikipedia и другие источники""" """Получение фото города с приоритетом ваших собственных фото"""
# 1. Попробуем Wikipedia city_lower = city.lower()
# проверочка посхалки
if city_lower in CUSTOM_CITY_PHOTOS:
custom = CUSTOM_CITY_PHOTOS[city_lower]
return custom["photo_url"], custom["author"]
# генерация фото по городам
wikipedia_url = f"https://ru.wikipedia.org/w/api.php?action=query&prop=pageimages&titles={city}&pithumbsize=800&format=json" wikipedia_url = f"https://ru.wikipedia.org/w/api.php?action=query&prop=pageimages&titles={city}&pithumbsize=800&format=json"
try: try:
response = requests.get(wikipedia_url, timeout=10) response = requests.get(wikipedia_url, timeout=10)
@ -78,45 +97,21 @@ async def get_city_photo(city: str, lat: float = None, lon: float = None):
except Exception as e: except Exception as e:
logger.error(f"Wikipedia API error: {e}") logger.error(f"Wikipedia API error: {e}")
# 2. Попробуем Wikimedia Commons
wikimedia_url = f"https://commons.wikimedia.org/w/api.php?action=query&generator=images&titles={city}&prop=imageinfo&iiprop=url&iiurlwidth=800&format=json"
try:
response = requests.get(wikimedia_url, timeout=10)
if response.status_code == 200:
data = response.json()
pages = data.get('query', {}).get('pages', {})
for page in pages.values():
if 'imageinfo' in page:
return page['imageinfo'][0]['thumburl'], "Wikimedia Commons"
except Exception as e:
logger.error(f"Wikimedia API error: {e}")
# 3. Для российских городов попробуем PhotoBank RG
if "россия" in city.lower() or any(city.lower().endswith(x) for x in ["ск", "ов", "ев", "ин"]):
try:
photobank_url = f"https://rusneb.ru/api/v1/photos?query={city}&limit=1"
response = requests.get(photobank_url, timeout=10)
if response.status_code == 200:
data = response.json()
if data.get('results'):
return data['results'][0]['image_url'], "PhotoBank RG"
except Exception as e:
logger.error(f"PhotoBank error: {e}")
return None, None return None, None
# ответ на /start
@dp.message(Command("start", "help")) @dp.message(Command("start", "help"))
async def cmd_start(message: types.Message): async def cmd_start(message: types.Message):
await message.answer( await message.answer(
"🏙️ Бот Погоды с фотографиями городов\n\n" "🏙️ Бот Погоды с фотографиями городов\n\n"
"Отправьте мне название города или поделитесь геопозицией, " "Отправьте мне название города или поделитесь геопозицией:",
"и я покажу погоду и фото города:", reply_markup=get_keyboard()
reply_markup=get_keyboard() )
)
@dp.message(F.text == "🌤️ Узнать погоду") @dp.message(F.text == "🌤️ Узнать погоду")
async def weather_button(message: types.Message): async def weather_button(message: types.Message):
await message.answer("Введите точное название города:") await message.answer("Введите название города:")
@dp.message(F.location) @dp.message(F.location)
async def handle_location(message: types.Message): async def handle_location(message: types.Message):
@ -124,26 +119,26 @@ async def handle_location(message: types.Message):
try: try:
lat = message.location.latitude lat = message.location.latitude
lon = message.location.longitude lon = message.location.longitude
# Получаем название города # принятие названия города
url = f"https://nominatim.openstreetmap.org/reverse?lat={lat}&lon={lon}&format=json" url = f"https://nominatim.openstreetmap.org/reverse?lat={lat}&lon={lon}&format=json"
headers = {"User-Agent": "CityPhotoBot/1.0"} headers = {"User-Agent": "CityPhotoBot/1.0"}
response = requests.get(url, headers=headers, timeout=10) response = requests.get(url, headers=headers, timeout=10)
if response.status_code == 200: if response.status_code == 200:
data = response.json() data = response.json()
city_name = data.get('address', {}).get('city', city_name = data.get('address', {}).get('city',
data.get('address', {}).get('town', data.get('address', {}).get('town',
data.get('address', {}).get('village', "это место"))) data.get('address', {}).get('village', "это место")))
weather_data = await get_weather(lat, lon, city_name) weather_data = await get_weather(lat, lon, city_name)
photo_url, source = await get_city_photo(city_name, lat, lon) photo_url, author = await get_city_photo(city_name)
if photo_url: if photo_url:
caption = f"{weather_data}\n\n📷 Источник: {source}" if source else weather_data caption = f"{weather_data}\n\n📷 Фото: {author}" if author else weather_data
await message.answer_photo(photo_url, caption=caption) await message.answer_photo(photo_url, caption=caption)
else: else:
await message.answer(f"{weather_data}\n\n(Не удалось найти фото города)") await message.answer(weather_data)
except Exception as e: except Exception as e:
logger.error(f"Location error: {e}") logger.error(f"Location error: {e}")
await message.answer("Ошибка обработки местоположения") await message.answer("Ошибка обработки местоположения")
@ -152,32 +147,40 @@ async def handle_location(message: types.Message):
async def city_handler(message: types.Message): async def city_handler(message: types.Message):
"""Обработка текстовых запросов""" """Обработка текстовых запросов"""
city = message.text.strip() city = message.text.strip()
if city.startswith('/'): if city.startswith('/'):
return return
logger.info(f"Processing city: {city}") logger.info(f"Processing city: {city}")
try: try:
# проверка не киев ли это
city_lower = city.lower()
if city_lower in CUSTOM_CITY_PHOTOS:
custom = CUSTOM_CITY_PHOTOS[city_lower]
weather_data = await get_weather(50.45, 30.52, "Киев") # корды киева
await message.answer_photo(
custom["photo_url"],
caption=f"{weather_data}\n\n📷 Фото: {custom['author']}"
)
return
# обработка других городов
coords = await get_coordinates(city) coords = await get_coordinates(city)
if not coords: if not coords:
await message.answer("Город не найден. Попробуйте уточнить название") await message.answer("Город не найден. Попробуйте уточнить название")
return return
lon, lat = coords lon, lat = coords
weather_data = await get_weather(lat, lon, city) weather_data = await get_weather(lat, lon, city)
photo_url, author = await get_city_photo(city)
# Пробуем получить фото с 3 попытками
for attempt in range(3): if photo_url:
photo_url, source = await get_city_photo(city, lat, lon) caption = f"{weather_data}\n\n📷 Фото: {author}" if author else weather_data
if photo_url: await message.answer_photo(photo_url, caption=caption)
caption = f"{weather_data}\n\n📷 Источник: {source}" if source else weather_data else:
await message.answer_photo(photo_url, caption=caption) await message.answer(weather_data)
return
await asyncio.sleep(1)
await message.answer(f"{weather_data}\n\n(Не удалось найти фото города)")
except Exception as e: except Exception as e:
logger.error(f"City handler error: {e}") logger.error(f"City handler error: {e}")
await message.answer("Произошла ошибка при обработке запроса") await message.answer("Произошла ошибка при обработке запроса")
@ -186,4 +189,5 @@ async def main():
await dp.start_polling(bot) await dp.start_polling(bot)
if __name__ == "__main__": if __name__ == "__main__":
asyncio.run(main()) asyncio.run(main())