From 7495bd949b7fe3a3390bf3a5331dc401b5bf9b0e Mon Sep 17 00:00:00 2001 From: FazziCLAY Date: Mon, 13 Apr 2026 23:10:54 +0300 Subject: [PATCH] hotfix: log any update and use index for excels --- .gitignore | 3 +- __pycache__/data_manager.cpython-313.pyc | Bin 4498 -> 0 bytes __pycache__/database.cpython-313.pyc | Bin 4242 -> 0 bytes __pycache__/keyboards.cpython-313.pyc | Bin 1939 -> 0 bytes data_manager.py | 17 +++++--- main.py | 39 +++++++++++------ middleware.py | 51 +++++++++++++++++++++++ 7 files changed, 91 insertions(+), 19 deletions(-) delete mode 100644 __pycache__/data_manager.cpython-313.pyc delete mode 100644 __pycache__/database.cpython-313.pyc delete mode 100644 __pycache__/keyboards.cpython-313.pyc create mode 100644 middleware.py diff --git a/.gitignore b/.gitignore index 2eea525..dc12cb7 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -.env \ No newline at end of file +.env +__pycache__ \ No newline at end of file diff --git a/__pycache__/data_manager.cpython-313.pyc b/__pycache__/data_manager.cpython-313.pyc deleted file mode 100644 index b717da011ed0b7a9a06750e84e7d1109a1dfde3c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4498 zcmbtXYiv}<6`r}TeRz%6tg*2d+lwE-+KFvs%)4;UCzElD7HLbMD=1 zYy+u8$G&II%$#|i?|d`6SX$~Pkbd{!-BhiUkbhvoPC`*xJPOKXA`_V&A;UPKoy?td1>Hu;{%AJZAJw8sl|7JPtQfy+8e9dIi?a-Q z(@Tv*fwa8_(m@7Ey0~Ium6k?HlAAOV6pYeDG?q(e)vT`MSlVzWStd7X=>jzAhGf+Q zc|*|Ebb`V8!a_mlI94DKFb;>ihn4=94-Aj8Op--MMp9Z5^|#bmRv$hXJ{meU{JQ+w z;P4qen^Uq$Wi)ppor)Ll3XLQHt~m}BRvK7t zTdZp!gH)!wC?$u$fhS?BXXs@;J3J0?D|Z|qN#Z1t>+qd}FSd~!Uu%!+AkhXwG=AAG*4b^3bJ;P-uOXe> z;~DE=K8+9YkzOlL&;;4T*OLT=8M-%;7l^qE2yV-BPU0b3iUY ze1Zm@ksvi(v5ckxdNV8rmP-lIcM-nDJcwRu;pjDPV7X1vc#zmDeG);L@_{A)7tJB- zDypl|qQhDK_}Mn$*}ec@6@0Ge-8sYE1v!IHs#!yLOV4N!@;q_^Wrjmnbv>2QiZOqV zm4dM$Ce&=~q_Jgrq%t+ZRQ;q9i^fi>ZM)N%STwEowwacQPJKIw?;l_Q`x~zJTP%H9Dr(($G53w9kWjy#wl535+}DIGTyZ z)p%gc9YEd-L^aSI0gL^{D#N&+Kyi0qkxHxa?m)1_a70s?liBR3Q5sICR4pr8CuQ*@ zMvzIC<{XBU&Ln}*4I!l^G6tVkHN$n_Y)l=^!r2<4%2)tD zJn(JcCF}G{R~)rb{Y)>Quk^oyFpvE!UroWc<*skbtz8A*p1Zz1cdEYSXlWmPD2Sy! z_Zv1(356})vs=378hQ#FdM1QRuKUd`Q`V$eb=q%EjeGitKjXP^>+W~ArFoJ{;tgG37zx4JwNEb zfX$q*-d{?8MfdHNKHx#S;Rtu~Q|rPU_b0lqSNd}uXww^Lm`KyLp*2wWthEHpKHE;i zTZPYdNT~NxsGlLGOx(C}K?;kNQ>$_A{N*ez#+07Y*hxpr?<22suk$+JPIRts2O&AQokkl6`GF|E@ zZI&6N-9v=LtKepMy5$mc7A;}a{;h-zHCp${HtrR!e-|Moz=t}o@fz0z^V@oY1ck`s zr-G7IA?$tj%RymkER)nyV`|)RF*TZ2wCIQ$wJFa)-R>vA9}&o{h5|;RWpfknk?|zZMCoI#R{2+e7uzbh#Vk3fJS+YPx?XS}^KQ zSov{$ewk^I8^H5hmF$bphH?{uOPFl}D~7`vk_rr&)(vM)OPwB#W>1cl<3qBI<=R

QNB0=S@Wxa6XgUgj0cth@C>2TfOf;QSEd&}4rh-vSHH36Z&$1v)5R@#!ZNbSz z?5(pGQ0LUM2A9xu^ee*C2B;SiXxb@7d~R5c&*iOk&|QBQ1YCLEwd3M^`MN9qNq?cd zakjki%kt*&(Emc_7oErEYy8)nuQlIrPBna4^Wwz5`D*{w!`JEx)$Oy@?fH)Ioq@UP zKBx{fkY)ly&SG({D69l8a4+Qw}Nf%aD zT~R01t9>_4d|4Ts;O2eRH{93W{KD5d@2|atuBd8x#DgmyqAQ*}d;!Ka4}S_2IDGoF zN2MJp8UyJASJqrPHF>H~xp}s7GdSVG!SOIe)`b7U&kALYvt^CQ`xRf5y*OW0 zbM@46Mc|zWzf5s$@XOS#C?lD8E{(cJ zQBLQg>0(Qn0=&TVY&xZ>T1HXW8XTq(#fvDqQS_n+gD}=9ik^*T;iqskn`NmJxvZ)y z%1vUf#Jmg{;=&Y2LV5ti&&dKOh}8=om)NsVDTwX`k3(!+SSyJY-+D#S+J@N$hz)-# z8xDMR!f$fJ1<*fgzChUlXkfJ{%oz@&hKyic!Oq>*274KvGNMFp1_6&TO8-vQ!2j1S cZ^2b_*Hv?M=bWqQob;X0Mr#%b2y>YK0)?@Yq5uE@ diff --git a/__pycache__/database.cpython-313.pyc b/__pycache__/database.cpython-313.pyc deleted file mode 100644 index ec16ae7737e93c705a54639a55e8ca9937d5b524..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4242 zcmd57&-72YM6Kce^}iWDVFvMl(YvCTC8t7WIQWtp^1S#qP59LHK@u_V_N|771O?&%0b-Ux8ctCdMSBrYparC!+xKSqCtJ;- zXn_*o+nM*ic{?+2zV~J~JsuYaztE?@%S`le+@G=2`q`U}&A&k7At!PoKh4EC!o_*Q zk7M65Z6OwTTBogXfe3LMvBmAg9(NE&+)14A9^zs+VY)Z&ChoY0c;a5-jr)j?>21?} zaX;~ob3@!-PP7kmqC>LG^|vO_d7iSGZPL$>PC)iZe#w1GXzBw@?*e_V6lkM^OzQ@% zr%OA)v|iBqy0oFRCET}x?!p%8OeuzHWU?xCqP(Bask31_bzRD&ujLFitrBXV%4@}J zjyf*`bRMC0(IA;znz~|nqGr;$X?2AP(OfYb7O3yCn$#4c7@1}DlA;56Mky536!m1; zQ%;)R+(gv?l+Wo)nL?PSu1S3*mz*>Vq9@Iznv!pJHbb<0@t#Pwq2X)GFPRXjWOkJ{^&8LbQw%xM) zZc)*i7N0EN$q?PpGC4Ju2ea+U+>M2wQd{)aRP;I>kY(L~i6>yvLuH^un{X<0XlvXPbx#e@d#5_v;{ zU?>ULv`Arv_`w+yxES3H;zRCxcc2sn@j;aAZkc!9tst0R-mSD1A|zl~HhOV9&U!X% zBO&Yzq8LK43kB{s1DLNG!|r%X;Aq_j;zN!KP7{H~ZIfx!>M2;ZPv!tniU^WzFDKbW zt0+uZh(mHpJ?K($iMB)dL~Ae7T#4}premnH#=oh13FMXqZrF}cO$W8++(~(l|C`HYn0-e5Wc_?$(89 z!$0^N=j#62(5tnH#hPC##s75t{L?$n-X^u;tovX_GAU|@ql#o7hXLdj93`ZS(7%Yy9yOO@PE~5v zpgcrCxh*1AP~iW+On34l@JU`nfjPe`rJsZ@4RrRRP4GQn(3=Eze(C(IHUQF_|Tf`*$J_Jc)mWoP>Pjf z)f1KJy0BnUaPILtpPj4?UF=ZsVq^C!Yp2)uJ~;~%+#M}lD=$}*mG_W>gZn?ext^*G zo-0k2Lse^Kq%NFm^o1VWTMgC*BegTPYki9!Tr;-|r?)NAZSMUFs@wmbd%y!{lW#2+ zn-vkOtQY>i7T;P=f}HZb#7*Fp*yd&%+Yi`46Wd|u&e&iV*5oS8fz@7~41g`v^wZa3 zdDAx7Wn&eY-4NW(l?{QlwX%LTau=*>m}P(fWPl)EgJB0oq*2ffh@lCd?3NtjVDoK5 zI7Zw>#?r%M~8Hui#eDJ^vK-AoGv&-eWo(g(W=M4FY~tUW9lwChM>g*q`+7;s@F zVSJNqS<#9rbuAUMN{(GrV+2`7T^U_2z;zRD30PIJf~Ke+7@;d^RW_EOGNmBp{J&(A zf$q}X&L^y%o`&U^e8BP<=E+%9>_ahu!n}~~`A_r7Vf2CVBnu$md*F@h_*r7!rEj91 zC7U;~#gfe}Y`uk8A(78(*wu40YxV{7U4bUkvuK7N6f|2Hf7x^SVf z>)t~+|wF5KHgn{bvqvQj~ax*(zG)4}szh4`g~WId6FgUONCVr zR!lGwG}MJr?->yVKOQrA;zKX&%V`{2>|jN)#Uix5^ug8E`~*BtQUJkk3_Stj?^d4Y zpL2))!1X@oZvCFS^_;s==WhIwJGLcQ_+UBp>r5rH#X)n+$@4wsv(@*vIB0Cy9lXJp fPi}Ge+zL8)iLVB+Cqd7{U*)SO|H0vt?bg2lM&Pyw diff --git a/__pycache__/keyboards.cpython-313.pyc b/__pycache__/keyboards.cpython-313.pyc deleted file mode 100644 index 17cc6446d7ce6576bb10c9419e8b1181ea4d41b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1939 zcmbVNO>7%Q6n?uqyZ%|*Nl2U~O&X_70(B)!n^H<6Xhluvk4ccc-O?%++x6N>Ht}C) z*EHcmia<@oA+6L(LHq?yt%O7lNFZ@Sy>PVCh*&EsRlp@D97U}VT$ov}(X}p; zlL_9OE~E|1uWF{A&nX3*9@VETG^%Qb#?j4=c!;I`#@7rz-w4q)IpWwHCo}P_J9f-B z!)XPe$O04wWfZU9*I7l4#2j(5?suXpSv|Ce%s@`)0-2Xsx=rG!&Iz)h2>^B+ZAR#h zmjGlg?sAfBF(dA}6Sq52_iA<@$bt+3ka!sd;4mi%$JqLbx?xBK5TTo9_HW-uJ<0WW z<$X#W(eXrD zGmXR>(Klbam^d$;lN0Y6W>GQ6ltM9?(KRKRHxrW%2aHHzniNnqWip8g$bg>RSRrZ# zehu{83-ryLsHWm}g>TL`iz;Hn{E4{}3x`+5P+1Ip)%L*OG|N_5))TBW?OO1c zo1R^~Ty6@_o_rws=LX1xzr7OdScsK_`>(TA0epdvk5_!n^RckKL#skZZ z2TBK{CF$*Q3l8T{oStZh$XnR?bd@C}bJDhoB#*eG3~?WGF3b(*ifs0KG15p(;pKD^Yc%(+_RAvg(wg&cJH2bRoo5p;+%MMaJ40}+!9$j^YiohvH#TM%Z~2xI{F z5f%1Wt*UMlG*UG6j1j4Gul9(zoXOg&!5JpDP)gTKn~fIA=p;g~dIxYX%~|^juVnM7 zVkUhY6W9T*WgH@^$}tRcA6~u>?dzzO2`%(gftbZ}52;x{=q90P6^OZw9#Lbb{1=j2 BlrsPT diff --git a/data_manager.py b/data_manager.py index f1c814a..d5943d2 100644 --- a/data_manager.py +++ b/data_manager.py @@ -4,11 +4,13 @@ import logging logger = logging.getLogger(__name__) class DataManager: - def __init__(self, facultets_url, groups_url): + def __init__(self, facultets_url, groups_url, parser_url): self.facultets_url = facultets_url self.groups_url = groups_url + self.parser_url = parser_url self.facs = {} # {id: {name: ...}} self.groups = {} # {id: {real_name: ..., facultet_tech: ..., ...}} + self.parser = {} async def refresh_cache(self, connector): async with aiohttp.ClientSession(connector=connector) as session: @@ -17,6 +19,8 @@ class DataManager: try: self.facs = await fetch(self.facultets_url) 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") except Exception as e: logger.error(f"Cache refresh failed: {e}") @@ -29,11 +33,12 @@ class DataManager: def get_files_by_fac(self, fac_id): files = {} # {uniqpath*: display_name} - for g in self.groups.values(): - if g.get('facultet_tech') == fac_id or g.get('facultet_recognized') == fac_id: - for ex in g.get('excels', []): - fn = ex['uniqpath'].replace("vstu.ru/rasp?dep=", "") - files[fn] = ex['display_filename'] + i = -1 + for ex in self.parser['all_files']: + i += 1 + if ex.get('facultet') == fac_id: + files[i] = ex['display_filename'] + return list(files.items()) def get_excel_by_uniqpath(self, uniqid): diff --git a/main.py b/main.py index e4f763a..19395cc 100644 --- a/main.py +++ b/main.py @@ -22,12 +22,22 @@ import keyboards as kb import re import fnmatch +from middleware import LoggingMiddleware + 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")) 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 dp = Dispatcher() @@ -203,16 +213,17 @@ async def view_group(callback: types.CallbackQuery): # Просмотр файла @dp.callback_query(F.data.startswith("view_excel:")) async def view_excel(callback: types.CallbackQuery): - pre_uniq_path = callback.data.split(":")[1] - uniqpath = "vstu.ru/rasp?dep=" + pre_uniq_path + pre_index = callback.data.split(":")[1] # Находим имя файла для красоты h_name = "Excel файл" - for g in data_mgr.groups.values(): - for ex in g.get('excels', []): - if ex.get('uniqpath', "") == uniqpath: h_name = ex['display_filename']; break + try: + ex = data_mgr.parser['all_files'][int(pre_index)] + h_name = ex['display_filename'] + except Exception as e: + print(f"safe?: {e}") 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")) 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 if mode == "excel": - pre_uniq_path = h_name - uniqpath = "vstu.ru/rasp?dep=" + pre_uniq_path - excel = data_mgr.get_excel_by_uniqpath(uniqpath) - sub_value = excel['url'].split("/")[-1] - h_name = sub_value + pre_index = h_name + try: + excel = data_mgr.parser['all_files'][int(pre_index)] + sub_value = excel['url'].split("/")[-1] + h_name = sub_value + except Exception as e: + print(f"safe? e={e}") async with async_session() as session: # Регистрация пользователя если нет @@ -337,6 +350,8 @@ async def main(): await conn.run_sync(Base.metadata.create_all) await data_mgr.refresh_cache(connector) + + dp.update.middleware(LoggingMiddleware()) # <-- добавьте эту строку asyncio.create_task(start_rabbitmq_consumer()) # Периодическое обновление индексов diff --git a/middleware.py b/middleware.py new file mode 100644 index 0000000..637ced3 --- /dev/null +++ b/middleware.py @@ -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