import asyncio import logging import os from pathlib import Path import pytz from datetime import datetime from aiogram import Bot, Dispatcher, executor, types from aiogram.dispatcher.filters import Text from dotenv import load_dotenv from main import start_parser load_dotenv() logging.basicConfig( level=logging.INFO, filename="logs/bot.log", format="%(asctime)s - %(module)s - %(levelname)s - %(funcName)s: %(lineno)d - %(message)s", datefmt='%H:%M:%S', ) logger = logging.getLogger(__name__) BOT_TOKEN = os.getenv("BOT_TOKEN") WEBHOOK_URL = os.getenv("WEBHOOK_URL") if not BOT_TOKEN: raise ValueError("❌ Переменная BOT_TOKEN не задана в .env") if not WEBHOOK_URL: raise ValueError("❌ Переменная WEBHOOK_URL не задана в .env") TZ = pytz.timezone('Europe/Moscow') USER_LOG = Path("logs/users.log") UPDATE_INTERVAL = 300 bot = Bot(token=BOT_TOKEN) dp = Dispatcher(bot) async def periodic_update(): while True: logger.info("Запуск автоматического обновления данных...") try: start_parser() logger.info("Автоматическое обновление завершено") except Exception as e: logger.error(f"Ошибка при автоматическом обновлении: {e}") await asyncio.sleep(UPDATE_INTERVAL) @dp.message_handler(commands='start') async def cmd_start(message: types.Message): buttons = ['Плановые', 'Внерегламентные', 'Аварийные', 'Информация'] keyboard = types.ReplyKeyboardMarkup(resize_keyboard=True) keyboard.add(*buttons) await message.answer('Выберите тип отключений:', reply_markup=keyboard) async def send_file_content(message: types.Message, file_path: Path, empty_msg: str, log_msg: str): await message.answer("Обновляю...") if not file_path.exists() or file_path.stat().st_size == 0: await message.answer(empty_msg) else: text = file_path.read_text(encoding='utf-8') if len(text) > 4095: for i in range(0, len(text), 4095): await message.answer(text[i:i + 4095]) else: await message.answer(text) now = datetime.now(TZ).strftime('%d/%m/%Y %H:%M') username = message.from_user.username or f"id{message.from_user.id}" log_entry = f"{now} t.me/{username} получил инфу по {log_msg}.\n" USER_LOG.parent.mkdir(exist_ok=True) with USER_LOG.open('a', encoding='utf-8') as f: f.write(log_entry) logger.info(f"Пользователь {username} запросил {log_msg}") @dp.message_handler(Text(equals='Плановые')) async def plan(message: types.Message): await send_file_content( message, Path("plan.txt"), "[Нет плановых отключений]", "плановым отключениям" ) @dp.message_handler(Text(equals='Внерегламентные')) async def vnereglament(message: types.Message): await send_file_content( message, Path("vnereglament.txt"), "[Нет внерегламентных отключений]", "внерегламентным отключениям" ) @dp.message_handler(Text(equals='Аварийные')) async def avar(message: types.Message): await send_file_content( message, Path("avar.txt"), "[Нет аварийных отключений]", "аварийным отключениям" ) @dp.message_handler(Text(equals='Информация')) async def info(message: types.Message): await message.answer( "ℹ️ Информация МРСК Белгород по отключениям эл-ва (v2)\n\n" "✅ Данные обновляются автоматически каждые 5 минут.\n" "📩 Вопросы и предложения: @pikusQQ" ) @dp.message_handler() async def fallback(message: types.Message): await message.answer("Нажмите /start для начала работы.") async def on_startup(dp): await bot.set_webhook(WEBHOOK_URL) asyncio.create_task(periodic_update()) logger.info(f"Webhook установлен на {WEBHOOK_URL}. Фоновое обновление запущено.") async def on_shutdown(dp): await bot.delete_webhook() logger.info("Webhook удалён") if __name__ == '__main__': executor.start_webhook( dispatcher=dp, webhook_path='', on_startup=on_startup, on_shutdown=on_shutdown, skip_updates=True, host='0.0.0.0', port=5000, )