From 1d5c80cd3545309c4f75f58f8ed51cb7fdb0ca30 Mon Sep 17 00:00:00 2001 From: Capybara-z Date: Sun, 16 Nov 2025 01:15:15 +0300 Subject: [PATCH] fix admin crypto links / fix sender button formatting --- handlers/admin/clusters/cluster_sync.py | 14 +---- handlers/admin/sender/sender_handler.py | 62 +++++++++++++++++--- handlers/keys/key_mode/key_country_mode.py | 6 +- handlers/keys/operations/aggregated_links.py | 3 + handlers/keys/operations/creation.py | 36 +++--------- handlers/keys/operations/renewal.py | 18 ++++++ handlers/keys/operations/update.py | 15 +++-- 7 files changed, 95 insertions(+), 59 deletions(-) diff --git a/handlers/admin/clusters/cluster_sync.py b/handlers/admin/clusters/cluster_sync.py index 879a0d0c..65fcf066 100644 --- a/handlers/admin/clusters/cluster_sync.py +++ b/handlers/admin/clusters/cluster_sync.py @@ -314,11 +314,6 @@ async def handle_sync_server( sub = await remna.get_subscription_by_username(key["email"]) if sub: new_remnawave_link = sub.get("subscriptionUrl") - if HAPP_CRYPTOLINK: - happ = sub.get("happ") or {} - new_remnawave_link = ( - happ.get("cryptoLink") or happ.get("link") or new_remnawave_link - ) if new_remnawave_link: server_result = await session.execute( @@ -336,7 +331,7 @@ async def handle_sync_server( email=key["email"], client_id=key["client_id"], tg_id=key["tg_id"], - remna_link_override=new_remnawave_link, + remna_link_override=None, plan=key["tariff_id"], ) @@ -544,11 +539,6 @@ async def handle_sync_cluster( sub = await remna.get_subscription_by_username(key["email"]) if sub: new_remnawave_link = sub.get("subscriptionUrl") - if HAPP_CRYPTOLINK: - happ = sub.get("happ") or {} - new_remnawave_link = ( - happ.get("cryptoLink") or happ.get("link") or new_remnawave_link - ) if new_remnawave_link: servers = await get_servers(session) @@ -561,7 +551,7 @@ async def handle_sync_cluster( email=key["email"], client_id=key["client_id"], tg_id=key["tg_id"], - remna_link_override=new_remnawave_link, + remna_link_override=None, plan=key["tariff_id"], ) diff --git a/handlers/admin/sender/sender_handler.py b/handlers/admin/sender/sender_handler.py index 5685954a..f5a345f6 100644 --- a/handlers/admin/sender/sender_handler.py +++ b/handlers/admin/sender/sender_handler.py @@ -170,13 +170,24 @@ async def get_recipients(session: AsyncSession, send_to: str, cluster_name: str return tg_ids, len(tg_ids) +def strip_html_tags(text: str) -> str: + text = re.sub(r'([^<]*)', r"\1", text) + text = re.sub(r'<[^>]+>', '', text) + text = text.replace('<', '<').replace('>', '>').replace('&', '&') + return text.strip() + + def parse_message_buttons(text: str) -> tuple[str, InlineKeyboardMarkup | None]: - if "BUTTONS:" not in text: + buttons_match = re.search(r'(<[^>]+>)?\s*BUTTONS\s*:\s*(]+>)?', text, re.IGNORECASE) + if not buttons_match: return text, None - parts = text.split("BUTTONS:", 1) - clean_text = parts[0].strip() - buttons_text = parts[1].strip() + clean_text = text[:buttons_match.start()].strip() + + buttons_section = text[buttons_match.start():].strip() + buttons_text = strip_html_tags(buttons_section) + + buttons_text = re.sub(r'^.*?BUTTONS\s*:\s*', '', buttons_text, flags=re.IGNORECASE).strip() if not buttons_text: return clean_text, None @@ -186,9 +197,7 @@ def parse_message_buttons(text: str) -> tuple[str, InlineKeyboardMarkup | None]: for line in button_lines: try: - cleaned_line = re.sub(r'([^<]*)', r"\1", line) - - button_data = json.loads(cleaned_line) + button_data = json.loads(line) if not isinstance(button_data, dict) or "text" not in button_data: logger.warning(f"Неверный формат кнопки: {line}") @@ -307,6 +316,21 @@ async def handle_message_input(message: Message, state: FSMContext, session: Asy cluster_name = data.get("cluster_name") _, user_count = await get_recipients(session, send_to, cluster_name) + if keyboard: + try: + keyboard_dict = keyboard.model_dump() + InlineKeyboardMarkup.model_validate(keyboard_dict) + except Exception as e: + await message.answer( + f"❌ Ошибка в клавиатуре!\n\n" + f"Не удалось сохранить клавиатуру из указанных кнопок.\n" + f"Ошибка: {str(e)}\n\n" + f"Пожалуйста, проверьте формат кнопок и попробуйте снова.", + reply_markup=build_admin_back_kb("sender"), + ) + await state.clear() + return + await state.update_data(text=clean_text, photo=photo, keyboard=keyboard.model_dump() if keyboard else None) await state.set_state(AdminSender.preview) @@ -343,9 +367,33 @@ async def handle_send_confirm(callback_query: CallbackQuery, state: FSMContext, keyboard = InlineKeyboardMarkup.model_validate(keyboard_data) except Exception as e: logger.error(f"Ошибка восстановления клавиатуры: {e}") + await callback_query.message.edit_text( + f"❌ Ошибка восстановления клавиатуры!\n\n" + f"Не удалось восстановить клавиатуру из сохраненных данных.\n" + f"Ошибка: {str(e)}\n\n" + f"Пожалуйста, создайте рассылку заново.", + reply_markup=build_admin_back_kb("sender"), + ) + await state.clear() + return tg_ids, total_users = await get_recipients(session, send_to, cluster_name) + if keyboard: + try: + keyboard.model_dump() + except Exception as e: + logger.error(f"Ошибка валидации клавиатуры перед рассылкой: {e}") + await callback_query.message.edit_text( + f"❌ Ошибка валидации клавиатуры!\n\n" + f"Клавиатура не прошла финальную проверку перед рассылкой.\n" + f"Ошибка: {str(e)}\n\n" + f"Пожалуйста, создайте рассылку заново.", + reply_markup=build_admin_back_kb("sender"), + ) + await state.clear() + return + await callback_query.message.edit_text(f"📤 Рассылка начата!\n👥 Количество получателей: {total_users}") messages = [] diff --git a/handlers/keys/key_mode/key_country_mode.py b/handlers/keys/key_mode/key_country_mode.py index dbbe2ca2..78cd6dca 100644 --- a/handlers/keys/key_mode/key_country_mode.py +++ b/handlers/keys/key_mode/key_country_mode.py @@ -486,11 +486,7 @@ async def finalize_key_creation( ) if not remnawave_link: - if bool(MODES_CONFIG.get("HAPP_CRYPTOLINK_ENABLED", HAPP_CRYPTOLINK)): - happ = sub.get("happ") or {} - remnawave_link = happ.get("cryptoLink") or happ.get("link") - if not remnawave_link: - remnawave_link = sub.get("subscriptionUrl") + remnawave_link = sub.get("subscriptionUrl") if old_key_name: await session.execute( diff --git a/handlers/keys/operations/aggregated_links.py b/handlers/keys/operations/aggregated_links.py index 55fa9438..90bbc56b 100644 --- a/handlers/keys/operations/aggregated_links.py +++ b/handlers/keys/operations/aggregated_links.py @@ -172,6 +172,9 @@ async def make_aggregated_link( ): logger.info("[agg_link] choose override Remnawave (non-vless)") return remna_link_override + if sub_url and sub_url.startswith("happ://"): + logger.info("[agg_link] choose Remnawave cryptoLink (non-vless)") + return sub_url kd = await get_key_details(session, email) stored = kd.get("remnawave_link") if kd else None if stored: diff --git a/handlers/keys/operations/creation.py b/handlers/keys/operations/creation.py index 5572fc89..a75b699a 100644 --- a/handlers/keys/operations/creation.py +++ b/handlers/keys/operations/creation.py @@ -115,6 +115,7 @@ async def create_key_on_cluster( remnawave_created = False remnawave_key = None remnawave_client_id = None + remnawave_link_value = None if remnawave_servers: remna = RemnawaveAPI(remnawave_servers[0]["api_url"]) @@ -146,38 +147,15 @@ async def create_key_on_cluster( if result: remnawave_created = True remnawave_client_id = result.get("uuid") - link_vless = None + remnawave_link_value = result.get("subscriptionUrl") + + remnawave_key = None if need_vless_key: try: - link_vless = await get_vless_link_for_remnawave_by_username(remna, email, email) + remnawave_key = await get_vless_link_for_remnawave_by_username(remna, email, email) except Exception as e: logger.error(f"{PANEL_REMNA} Ошибка сборки VLESS: {e}") - - base_use_crypto_link = bool( - MODES_CONFIG.get("HAPP_CRYPTOLINK_ENABLED", HAPP_CRYPTOLINK) - ) - use_crypto_link = base_use_crypto_link - try: - hook_results = await run_hooks( - "happ_cryptolink_override", - cluster_id=cluster_id, - plan=plan, - session=session, - email=email, - tg_id=tg_id, - happ_cryptolink=base_use_crypto_link, - ) - if hook_results: - for hook_result in hook_results: - if hook_result is True or hook_result is False: - use_crypto_link = hook_result - break - except Exception as e: - logger.warning(f"[HAPP_CRYPTOLINK_OVERRIDE] Ошибка при применении хуков: {e}") - - remnawave_key = link_vless or ( - result["happ"]["cryptoLink"] if use_crypto_link else result.get("subscriptionUrl") - ) + logger.info(f"{PANEL_REMNA} Пользователь создан: {result}") else: logger.warning(f"{PANEL_REMNA} Нет inbound_id у серверов") @@ -246,7 +224,7 @@ async def create_key_on_cluster( expiry_time=expiry_timestamp, key=public_link, server_id=server_id_to_store, - remnawave_link=remnawave_key, + remnawave_link=remnawave_link_value if remnawave_created else None, tariff_id=plan, ) await session.execute(update(User).where(User.tg_id == tg_id, User.trial.in_([0, -1])).values(trial=1)) diff --git a/handlers/keys/operations/renewal.py b/handlers/keys/operations/renewal.py index 0f20dfe2..63988b08 100644 --- a/handlers/keys/operations/renewal.py +++ b/handlers/keys/operations/renewal.py @@ -287,6 +287,24 @@ async def renew_key_in_cluster( await update_key_expiry(session, client_id, new_expiry_time) for prefix in ["key_24h", "key_10h", "key_expired", "renew"]: await delete_notification(session, tg_id, f"{email}_{prefix}") + + try: + key_link = await make_aggregated_link( + session=session, + cluster_all=cluster_scope, + cluster_id=cluster_id, + email=email, + client_id=client_id, + tg_id=tg_id, + subgroup_code=target_subgroup, + remna_link_override=None, + plan=plan, + ) + if key_link: + await update_key_link(session, email, key_link) + except Exception as le: + logger.warning(f"[Link] ошибка генерации/сохранения после продления: {le}") + return True return False diff --git a/handlers/keys/operations/update.py b/handlers/keys/operations/update.py index 0659c9d9..67d16b7d 100644 --- a/handlers/keys/operations/update.py +++ b/handlers/keys/operations/update.py @@ -6,7 +6,7 @@ from sqlalchemy import delete, select from sqlalchemy.ext.asyncio import AsyncSession from config import PUBLIC_LINK, REMNAWAVE_LOGIN, REMNAWAVE_PASSWORD, SUPERNODE -from database import filter_cluster_by_subgroup, get_servers, store_key +from database import filter_cluster_by_subgroup, get_servers, get_tariff_by_id, store_key from database.models import Key, Tariff from handlers.utils import get_least_loaded_cluster from logger import ( @@ -62,6 +62,7 @@ async def update_key_on_cluster( remnawave_client_id = None remnawave_key = None + remnawave_link_value = None if remnawave_servers: inbound_ids = [s["inbound_id"] for s in remnawave_servers if s.get("inbound_id")] @@ -104,7 +105,9 @@ async def update_key_on_cluster( result = await remna.create_user(user_data) if result: remnawave_client_id = result.get("uuid") - remnawave_key = result.get("subscriptionUrl") + remnawave_link_value = result.get("subscriptionUrl") + remnawave_key = None + logger.info(f"{PANEL_REMNA} Клиент заново создан, uuid={remnawave_client_id}") else: logger.error(f"{PANEL_REMNA} Ошибка создания клиента") @@ -162,7 +165,7 @@ async def update_key_on_cluster( await asyncio.gather(*tasks, return_exceptions=True) logger.info(f"[Update] Ключ {remnawave_client_id} обновлён на серверах подгруппы в {cluster_id}") - return remnawave_client_id, remnawave_key + return remnawave_client_id, remnawave_link_value except Exception as e: logger.error(f"[Update Error] Ошибка при обновлении ключа {client_id} на {cluster_id}: {e}") @@ -246,7 +249,7 @@ async def update_subscription( traffic_limit = int(tariff.traffic_limit) if tariff.traffic_limit is not None else None device_limit = int(tariff.device_limit) if tariff.device_limit is not None else 0 - new_client_id, remnawave_key = await update_key_on_cluster( + new_client_id, remnawave_link_value = await update_key_on_cluster( tg_id=tg_id, client_id=client_id, email=email, @@ -267,7 +270,7 @@ async def update_subscription( client_id=new_client_id, tg_id=tg_id, subgroup_code=subgroup_code, - remna_link_override=remnawave_key, + remna_link_override=None, plan=tariff_id, ) @@ -280,7 +283,7 @@ async def update_subscription( email=email, expiry_time=expiry_time, key=final_key_link, - remnawave_link=remnawave_key, + remnawave_link=remnawave_link_value or remnawave_link, server_id=new_cluster_id, tariff_id=tariff_id, alias=alias,