import logging from logging.handlers import RotatingFileHandler from aiogram import Bot, Dispatcher, types from aiogram.filters import Command from aiogram.utils.keyboard import ReplyKeyboardBuilder import requests import asyncio # настройка логов logger = logging.getLogger() logger.setLevel(logging.DEBUG) # Включаем детальное логирование formatter = logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) # логирование в файл, максимум 5мб по 3 бэкапа file_handler = RotatingFileHandler( 'weather_bot.log', maxBytes=5*1024*1024, backupCount=3, encoding='utf-8' ) file_handler.setFormatter(formatter) # вывод в консоль console_handler = logging.StreamHandler() console_handler.setFormatter(formatter) logger.addHandler(file_handler) logger.addHandler(console_handler) # конфиг try: from config import BOT_TOKEN, WEATHER_API_KEY except ImportError: logging.critical("Ошибка: Создайте файл config.py с BOT_TOKEN и WEATHER_API_KEY!") exit(1) # инициализация бота bot = Bot(token=BOT_TOKEN) dp = Dispatcher() # клавиатура builder = ReplyKeyboardBuilder() builder.button(text="Узнать погоду") builder.button(text="Помощь") keyboard = builder.as_markup(resize_keyboard=True) # обработка команд @dp.message(Command("start", "help")) async def cmd_start(message: types.Message): logging.info(f"Новый пользователь: {message.from_user.id}") await message.answer( "Бот Погоды\n\n" "Отправьте мне название города, и я пришлю текущую погоду.\n" "Или нажмите кнопку ниже:", reply_markup=keyboard ) @dp.message(lambda message: message.text in ["Узнать погоду", "Помощь"]) async def button_handler(message: types.Message): if message.text == "Узнать погоду": await message.answer("Введите название города:") else: await cmd_start(message) @dp.message() async def get_weather(message: types.Message): city = message.text.strip() if not city: return logging.info(f"Запрос погоды для: {city} (от {message.from_user.id})") try: # Запрос к API url = f"https://api.openweathermap.org/data/2.5/weather?q={city}&appid={WEATHER_API_KEY}&units=metric&lang=ru" response = requests.get(url, timeout=10) logging.debug(f"API Response: {response.status_code} {response.text[:200]}...") if response.status_code != 200: error_msg = response.json().get('message', 'Unknown error') logging.error(f"API Error for {city}: {response.status_code} - {error_msg}") await message.answer(f"Ошибка: {error_msg.capitalize()}") return data = response.json() # Формирование ответа weather_report = ( f"Город: {data['name']}\n" f"Температура: {data['main']['temp']:.1f}°C\n" f"Ощущается как: {data['main']['feels_like']:.1f}°C\n" f"Ветер: {data['wind']['speed']} м/с\n" f"Погода: {data['weather'][0]['description'].capitalize()}\n" f"Влажность: {data['main']['humidity']}%" ) logging.info(f"Успешный ответ для {city}") await message.answer(weather_report) except requests.exceptions.Timeout: logging.error(f"Таймаут запроса для {city}") await message.answer("Сервер погоды не отвечает. Попробуйте позже.") except Exception as e: logging.exception(f"Ошибка для {city}:") await message.answer("⚠️ Произошла внутренняя ошибка. Попробуйте другой город.") # запуск бота async def main(): logging.info("Starting bot...") await dp.start_polling(bot) if __name__ == "__main__": try: asyncio.run(main()) except KeyboardInterrupt: logging.info("Bot stopped by user") except Exception as e: logging.critical(f"Fatal error: {e}")