-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbot.py
More file actions
93 lines (74 loc) · 3.33 KB
/
Copy pathbot.py
File metadata and controls
93 lines (74 loc) · 3.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
"""
Точка входа бота. Инициализация, настройка middleware, запуск polling.
"""
import asyncio
import logging
from logging.handlers import RotatingFileHandler
from aiogram import Bot, Dispatcher
from aiogram.client.session.aiohttp import AiohttpSession
from aiogram.client.telegram import TelegramAPIServer
from config import BOT_API_BASE_URL, BOT_TOKEN, PROXY_URL
from core.charts import shutdown_executor
from core.database.models import async_main, async_session, engine
from core.error_tracker import get_tracker
from core.handlers import router
from core.middleware import RateLimitMiddleware, UserMiddleware
from core.scheduler import setup_scheduler
async def main():
# Создаём таблицы в БД (если не существуют)
await async_main()
# Nginx reverse proxy → Telegram API (приоритет над SOCKS)
if BOT_API_BASE_URL:
session = AiohttpSession(api=TelegramAPIServer.from_base(BOT_API_BASE_URL))
elif PROXY_URL:
session = AiohttpSession(proxy=PROXY_URL)
else:
session = None
# Инициализация бота и диспетчера
bot = Bot(token=BOT_TOKEN, session=session)
dp = Dispatcher()
# Защита от спама: 60 запросов/мин на пользователя
dp.message.middleware(RateLimitMiddleware())
dp.callback_query.middleware(RateLimitMiddleware())
# Кэширование user_id для всех хендлеров
dp.message.middleware(UserMiddleware())
dp.callback_query.middleware(UserMiddleware())
# Подключаем обработчики
dp.include_router(router)
# Запускаем планировщик уведомлений
scheduler = setup_scheduler(bot, async_session)
try:
# Сбрасываем накопленную очередь старых апдейтов перед стартом
await bot.delete_webhook(drop_pending_updates=True)
await dp.start_polling(bot)
finally:
# Graceful shutdown: закрываем все ресурсы
logging.info("Завершение работы бота...")
# Останавливаем планировщик (синхронный метод)
scheduler.shutdown()
# Закрываем сессию бота
if session:
await session.close()
# Закрываем соединение с БД
await engine.dispose()
# Останавливаем executor для графиков
shutdown_executor()
logging.info("Бот остановлен.")
if __name__ == "__main__":
try:
# Настройка логов: консоль + файл с ротацией (макс 1MB, 3 бэкапа)
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
handlers=[
logging.StreamHandler(),
RotatingFileHandler(
"bot.log", maxBytes=1_000_000, backupCount=3, encoding="utf-8"
),
],
)
# In-memory ERROR/CRITICAL counter for the admin stats panel.
logging.getLogger().addHandler(get_tracker())
asyncio.run(main())
except KeyboardInterrupt:
print("Bot off")