Compare commits
3 Commits
8d84931fca
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
7495bd949b
|
|||
|
8bbb440bce
|
|||
|
64c3f27386
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
.env
|
.env
|
||||||
|
__pycache__
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -4,11 +4,13 @@ import logging
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class DataManager:
|
class DataManager:
|
||||||
def __init__(self, facultets_url, groups_url):
|
def __init__(self, facultets_url, groups_url, parser_url):
|
||||||
self.facultets_url = facultets_url
|
self.facultets_url = facultets_url
|
||||||
self.groups_url = groups_url
|
self.groups_url = groups_url
|
||||||
|
self.parser_url = parser_url
|
||||||
self.facs = {} # {id: {name: ...}}
|
self.facs = {} # {id: {name: ...}}
|
||||||
self.groups = {} # {id: {real_name: ..., facultet_tech: ..., ...}}
|
self.groups = {} # {id: {real_name: ..., facultet_tech: ..., ...}}
|
||||||
|
self.parser = {}
|
||||||
|
|
||||||
async def refresh_cache(self, connector):
|
async def refresh_cache(self, connector):
|
||||||
async with aiohttp.ClientSession(connector=connector) as session:
|
async with aiohttp.ClientSession(connector=connector) as session:
|
||||||
@@ -17,6 +19,8 @@ class DataManager:
|
|||||||
try:
|
try:
|
||||||
self.facs = await fetch(self.facultets_url)
|
self.facs = await fetch(self.facultets_url)
|
||||||
self.groups = (await fetch(self.groups_url))['groups']
|
self.groups = (await fetch(self.groups_url))['groups']
|
||||||
|
self.parser = (await fetch(self.parser_url))
|
||||||
|
|
||||||
logger.info(f"Loaded {len(self.facs)} facs and {len(self.groups)} groups")
|
logger.info(f"Loaded {len(self.facs)} facs and {len(self.groups)} groups")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Cache refresh failed: {e}")
|
logger.error(f"Cache refresh failed: {e}")
|
||||||
@@ -29,11 +33,12 @@ class DataManager:
|
|||||||
|
|
||||||
def get_files_by_fac(self, fac_id):
|
def get_files_by_fac(self, fac_id):
|
||||||
files = {} # {uniqpath*: display_name}
|
files = {} # {uniqpath*: display_name}
|
||||||
for g in self.groups.values():
|
i = -1
|
||||||
if g.get('facultet_tech') == fac_id or g.get('facultet_recognized') == fac_id:
|
for ex in self.parser['all_files']:
|
||||||
for ex in g.get('excels', []):
|
i += 1
|
||||||
fn = ex['uniqpath'].replace("vstu.ru/rasp?dep=", "")
|
if ex.get('facultet') == fac_id:
|
||||||
files[fn] = ex['display_filename']
|
files[i] = ex['display_filename']
|
||||||
|
|
||||||
return list(files.items())
|
return list(files.items())
|
||||||
|
|
||||||
def get_excel_by_uniqpath(self, uniqid):
|
def get_excel_by_uniqpath(self, uniqid):
|
||||||
|
|||||||
45
main.py
45
main.py
@@ -22,12 +22,22 @@ import keyboards as kb
|
|||||||
import re
|
import re
|
||||||
import fnmatch
|
import fnmatch
|
||||||
|
|
||||||
|
from middleware import LoggingMiddleware
|
||||||
|
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.INFO,
|
||||||
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||||
|
handlers=[
|
||||||
|
logging.StreamHandler()
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
# --- Настройки ---
|
# --- Настройки ---
|
||||||
engine = create_async_engine(os.getenv("DATABASE_URL"))
|
engine = create_async_engine(os.getenv("DATABASE_URL"))
|
||||||
async_session = async_sessionmaker(engine, expire_on_commit=False)
|
async_session = async_sessionmaker(engine, expire_on_commit=False)
|
||||||
data_mgr = DataManager(os.getenv("FACULTETS_JSON_URL"), os.getenv("GROUPS_JSON_URL"))
|
data_mgr = DataManager(os.getenv("FACULTETS_JSON_URL"), os.getenv("GROUPS_JSON_URL"), os.getenv("PARSER_JSON_URL"))
|
||||||
bot = None
|
bot = None
|
||||||
dp = Dispatcher()
|
dp = Dispatcher()
|
||||||
|
|
||||||
@@ -203,16 +213,17 @@ async def view_group(callback: types.CallbackQuery):
|
|||||||
# Просмотр файла
|
# Просмотр файла
|
||||||
@dp.callback_query(F.data.startswith("view_excel:"))
|
@dp.callback_query(F.data.startswith("view_excel:"))
|
||||||
async def view_excel(callback: types.CallbackQuery):
|
async def view_excel(callback: types.CallbackQuery):
|
||||||
pre_uniq_path = callback.data.split(":")[1]
|
pre_index = callback.data.split(":")[1]
|
||||||
uniqpath = "vstu.ru/rasp?dep=" + pre_uniq_path
|
|
||||||
# Находим имя файла для красоты
|
# Находим имя файла для красоты
|
||||||
h_name = "Excel файл"
|
h_name = "Excel файл"
|
||||||
for g in data_mgr.groups.values():
|
try:
|
||||||
for ex in g.get('excels', []):
|
ex = data_mgr.parser['all_files'][int(pre_index)]
|
||||||
if ex.get('uniqpath', "") == uniqpath: h_name = ex['display_filename']; break
|
h_name = ex['display_filename']
|
||||||
|
except Exception as e:
|
||||||
|
print(f"safe?: {e}")
|
||||||
|
|
||||||
builder = InlineKeyboardBuilder()
|
builder = InlineKeyboardBuilder()
|
||||||
builder.row(types.InlineKeyboardButton(text="🔔 Подписаться на файл", callback_data=f"suco:excel:{pre_uniq_path}"))
|
builder.row(types.InlineKeyboardButton(text="🔔 Подписаться на файл", callback_data=f"suco:excel:{pre_index}"))
|
||||||
builder.row(types.InlineKeyboardButton(text="⬅️ Назад", callback_data="menu_start"))
|
builder.row(types.InlineKeyboardButton(text="⬅️ Назад", callback_data="menu_start"))
|
||||||
await callback.message.edit_text(f"📄 Файл: *{h_name}*", reply_markup=builder.as_markup(), parse_mode="Markdown")
|
await callback.message.edit_text(f"📄 Файл: *{h_name}*", reply_markup=builder.as_markup(), parse_mode="Markdown")
|
||||||
|
|
||||||
@@ -224,11 +235,13 @@ async def sub_confirm(callback: types.CallbackQuery):
|
|||||||
sub_value = val
|
sub_value = val
|
||||||
|
|
||||||
if mode == "excel":
|
if mode == "excel":
|
||||||
pre_uniq_path = h_name
|
pre_index = h_name
|
||||||
uniqpath = "vstu.ru/rasp?dep=" + pre_uniq_path
|
try:
|
||||||
excel = data_mgr.get_excel_by_uniqpath(uniqpath)
|
excel = data_mgr.parser['all_files'][int(pre_index)]
|
||||||
sub_value = excel['url'].split("/")[-1]
|
sub_value = excel['url'].split("/")[-1]
|
||||||
h_name = sub_value
|
h_name = sub_value
|
||||||
|
except Exception as e:
|
||||||
|
print(f"safe? e={e}")
|
||||||
|
|
||||||
async with async_session() as session:
|
async with async_session() as session:
|
||||||
# Регистрация пользователя если нет
|
# Регистрация пользователя если нет
|
||||||
@@ -270,18 +283,18 @@ async def my_subs(callback: types.CallbackQuery):
|
|||||||
@dp.callback_query(F.data.startswith("unsub:"))
|
@dp.callback_query(F.data.startswith("unsub:"))
|
||||||
async def unsub(callback: types.CallbackQuery):
|
async def unsub(callback: types.CallbackQuery):
|
||||||
sid = int(callback.data.split(":")[1])
|
sid = int(callback.data.split(":")[1])
|
||||||
user_id = callback.from_user.id # ID того, кто нажал на кнопку
|
chat_id = callback.message.chat.id
|
||||||
|
|
||||||
async with async_session() as session:
|
async with async_session() as session:
|
||||||
# Добавляем условие chat_id == user_id
|
|
||||||
stmt = select(Subscription).where(
|
stmt = select(Subscription).where(
|
||||||
Subscription.id == sid,
|
Subscription.id == sid,
|
||||||
Subscription.chat_id == user_id,
|
Subscription.chat_id == chat_id,
|
||||||
Subscription.deleted == False
|
Subscription.deleted == False
|
||||||
)
|
)
|
||||||
result = await session.execute(stmt)
|
result = await session.execute(stmt)
|
||||||
result.deleted = True
|
sub = result.scalar_one_or_none()
|
||||||
session.add(result)
|
sub.deleted = True
|
||||||
|
session.add(sub)
|
||||||
await session.commit()
|
await session.commit()
|
||||||
|
|
||||||
# Проверяем, было ли реально что-то удалено
|
# Проверяем, было ли реально что-то удалено
|
||||||
@@ -337,6 +350,8 @@ async def main():
|
|||||||
await conn.run_sync(Base.metadata.create_all)
|
await conn.run_sync(Base.metadata.create_all)
|
||||||
|
|
||||||
await data_mgr.refresh_cache(connector)
|
await data_mgr.refresh_cache(connector)
|
||||||
|
|
||||||
|
dp.update.middleware(LoggingMiddleware()) # <-- добавьте эту строку
|
||||||
asyncio.create_task(start_rabbitmq_consumer())
|
asyncio.create_task(start_rabbitmq_consumer())
|
||||||
|
|
||||||
# Периодическое обновление индексов
|
# Периодическое обновление индексов
|
||||||
|
|||||||
51
middleware.py
Normal file
51
middleware.py
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
from aiogram.types import Update
|
||||||
|
from aiogram import BaseMiddleware
|
||||||
|
from typing import Callable, Dict, Any, Awaitable
|
||||||
|
import logging
|
||||||
|
import time
|
||||||
|
import json
|
||||||
|
|
||||||
|
class LoggingMiddleware(BaseMiddleware):
|
||||||
|
async def __call__(
|
||||||
|
self,
|
||||||
|
handler: Callable[[Update, Dict[str, Any]], Awaitable[Any]],
|
||||||
|
event: Update,
|
||||||
|
data: Dict[str, Any]
|
||||||
|
) -> Any:
|
||||||
|
# Логируем входящий апдейт
|
||||||
|
update_id = event.update_id
|
||||||
|
update_type = "unknown"
|
||||||
|
|
||||||
|
if event.message:
|
||||||
|
update_type = "message"
|
||||||
|
chat_id = event.message.chat.id
|
||||||
|
text = event.message.text or "[non-text]"
|
||||||
|
log_msg = f"📥 [UPDATE {update_id}] type={update_type} chat={chat_id} text={text}"
|
||||||
|
elif event.callback_query:
|
||||||
|
update_type = "callback_query"
|
||||||
|
chat_id = event.callback_query.message.chat.id
|
||||||
|
data_str = event.callback_query.data or "[no data]"
|
||||||
|
log_msg = f"📥 [UPDATE {update_id}] type={update_type} chat={chat_id} data={data_str}"
|
||||||
|
elif event.my_chat_member:
|
||||||
|
update_type = "my_chat_member"
|
||||||
|
log_msg = f"📥 [UPDATE {update_id}] type={update_type}"
|
||||||
|
elif event.chat_member:
|
||||||
|
update_type = "chat_member"
|
||||||
|
log_msg = f"📥 [UPDATE {update_id}] type={update_type}"
|
||||||
|
else:
|
||||||
|
log_msg = f"📥 [UPDATE {update_id}] type={update_type} raw={event.json()}"
|
||||||
|
|
||||||
|
# Печатаем или используем logging
|
||||||
|
logging.info(log_msg)
|
||||||
|
# Если настроен logging: logging.info(log_msg)
|
||||||
|
|
||||||
|
# Передаём управление дальше (в хендлеры)
|
||||||
|
try:
|
||||||
|
result = await handler(event, data)
|
||||||
|
except Exception as e:
|
||||||
|
# Логируем ошибку, но не подавляем
|
||||||
|
logging.error(f"❌ [UPDATE {update_id}] handler error: {e}")
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
logging.info(f"✅ [UPDATE {update_id}] handled successfully")
|
||||||
|
return result
|
||||||
Reference in New Issue
Block a user