mirror of
https://github.com/BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot.git
synced 2026-01-29 00:00:26 +00:00
202 lines
8.1 KiB
Python
202 lines
8.1 KiB
Python
import logging
|
||
from typing import Optional, Dict, Any, Callable
|
||
from aiogram.types import CallbackQuery
|
||
from aiogram import Router
|
||
from remnawave_api import RemnaWaveAPI
|
||
from translations import t
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
class APIErrorHandler:
|
||
|
||
@staticmethod
|
||
async def handle_api_error(callback: CallbackQuery, error: Exception,
|
||
operation: str, user_language: str = 'ru',
|
||
fallback_keyboard=None) -> bool:
|
||
error_message = str(error).lower()
|
||
|
||
if "timeout" in error_message or "connection" in error_message:
|
||
text = "⏱ Таймаут подключения к API\n\n"
|
||
text += "Возможные причины:\n"
|
||
text += "• Медленный интернет\n"
|
||
text += "• Перегрузка сервера RemnaWave\n"
|
||
text += "• Временные проблемы с сетью\n\n"
|
||
text += "🔄 Попробуйте повторить операцию через несколько секунд"
|
||
|
||
elif "401" in error_message or "unauthorized" in error_message:
|
||
text = "🔐 Ошибка авторизации API\n\n"
|
||
text += "Токен доступа недействителен или истек.\n"
|
||
text += "Обратитесь к администратору для обновления токена."
|
||
|
||
elif "404" in error_message or "not found" in error_message:
|
||
text = f"❌ Ресурс не найден\n\n"
|
||
text += f"Операция: {operation}\n"
|
||
text += "Возможно, запрашиваемый объект был удален или не существует."
|
||
|
||
elif "500" in error_message or "internal server error" in error_message:
|
||
text = "🔥 Внутренняя ошибка сервера RemnaWave\n\n"
|
||
text += "Сервер временно недоступен.\n"
|
||
text += "Попробуйте повторить операцию позже."
|
||
|
||
else:
|
||
text = f"❌ Ошибка API операции: {operation}\n\n"
|
||
text += f"Детали: {str(error)[:100]}{'...' if len(str(error)) > 100 else ''}\n\n"
|
||
text += "Обратитесь к администратору если проблема повторяется."
|
||
|
||
try:
|
||
await callback.message.edit_text(
|
||
text,
|
||
reply_markup=fallback_keyboard or error_recovery_keyboard(operation, user_language)
|
||
)
|
||
return True
|
||
except Exception as edit_error:
|
||
logger.error(f"Failed to edit message with error info: {edit_error}")
|
||
try:
|
||
await callback.answer(f"❌ Ошибка: {operation}", show_alert=True)
|
||
return True
|
||
except:
|
||
return False
|
||
|
||
@staticmethod
|
||
async def safe_api_call(api_method: Callable, *args, **kwargs) -> tuple[bool, Any]:
|
||
try:
|
||
result = await api_method(*args, **kwargs)
|
||
return True, result
|
||
except Exception as e:
|
||
logger.error(f"API call failed: {api_method.__name__} - {e}")
|
||
return False, str(e)
|
||
|
||
def create_error_recovery_keyboard(error_context: str, language: str = 'ru'):
|
||
from keyboards import error_recovery_keyboard
|
||
return error_recovery_keyboard(error_context, language)
|
||
|
||
async def safe_get_nodes(api: RemnaWaveAPI) -> tuple[bool, list]:
|
||
try:
|
||
logger.info("Attempting to fetch nodes from API...")
|
||
nodes = await api.get_all_nodes()
|
||
|
||
if nodes is None:
|
||
logger.warning("API returned None for nodes")
|
||
return False, []
|
||
|
||
if not isinstance(nodes, list):
|
||
logger.warning(f"API returned non-list for nodes: {type(nodes)}")
|
||
return False, []
|
||
|
||
logger.info(f"Successfully fetched {len(nodes)} nodes")
|
||
return True, nodes
|
||
|
||
except Exception as e:
|
||
logger.error(f"Error fetching nodes: {e}")
|
||
return False, []
|
||
|
||
async def safe_get_system_users(api: RemnaWaveAPI) -> tuple[bool, list]:
|
||
try:
|
||
logger.info("Attempting to fetch system users from API...")
|
||
users = await api.get_all_system_users_full()
|
||
|
||
if users is None:
|
||
logger.warning("API returned None for users")
|
||
return False, []
|
||
|
||
if not isinstance(users, list):
|
||
logger.warning(f"API returned non-list for users: {type(users)}")
|
||
return False, []
|
||
|
||
logger.info(f"Successfully fetched {len(users)} users")
|
||
return True, users
|
||
|
||
except Exception as e:
|
||
logger.error(f"Error fetching system users: {e}")
|
||
return False, []
|
||
|
||
async def safe_restart_nodes(api: RemnaWaveAPI, all_nodes: bool = True, node_id: str = None) -> tuple[bool, str]:
|
||
try:
|
||
if all_nodes:
|
||
logger.info("Attempting to restart all nodes...")
|
||
result = await api.restart_all_nodes()
|
||
else:
|
||
logger.info(f"Attempting to restart node {node_id}...")
|
||
result = await api.restart_node(node_id)
|
||
|
||
if result:
|
||
message = "Команда перезагрузки отправлена успешно"
|
||
logger.info(f"Restart command sent successfully")
|
||
return True, message
|
||
else:
|
||
message = "API вернул отрицательный результат"
|
||
logger.warning("API returned negative result for restart")
|
||
return False, message
|
||
|
||
except Exception as e:
|
||
logger.error(f"Error restarting nodes: {e}")
|
||
return False, str(e)
|
||
|
||
async def check_api_health(api: RemnaWaveAPI) -> Dict[str, Any]:
|
||
health_info = {
|
||
'api_available': False,
|
||
'nodes_accessible': False,
|
||
'users_accessible': False,
|
||
'system_stats_accessible': False,
|
||
'errors': []
|
||
}
|
||
|
||
if api is None:
|
||
health_info['errors'].append("API instance is None")
|
||
return health_info
|
||
|
||
try:
|
||
success, nodes = await safe_get_nodes(api)
|
||
if success:
|
||
health_info['api_available'] = True
|
||
health_info['nodes_accessible'] = True
|
||
else:
|
||
health_info['errors'].append("Cannot fetch nodes")
|
||
except Exception as e:
|
||
health_info['errors'].append(f"Nodes check failed: {e}")
|
||
|
||
try:
|
||
success, users = await safe_get_system_users(api)
|
||
if success:
|
||
health_info['users_accessible'] = True
|
||
else:
|
||
health_info['errors'].append("Cannot fetch users")
|
||
except Exception as e:
|
||
health_info['errors'].append(f"Users check failed: {e}")
|
||
|
||
try:
|
||
stats = await api.get_system_stats()
|
||
if stats:
|
||
health_info['system_stats_accessible'] = True
|
||
else:
|
||
health_info['errors'].append("Cannot fetch system stats")
|
||
except Exception as e:
|
||
health_info['errors'].append(f"System stats check failed: {e}")
|
||
|
||
return health_info
|
||
|
||
def handle_api_errors(operation_name: str):
|
||
def decorator(func):
|
||
async def wrapper(callback: CallbackQuery, user, *args, **kwargs):
|
||
try:
|
||
return await func(callback, user, *args, **kwargs)
|
||
except Exception as e:
|
||
logger.error(f"Error in {func.__name__}: {e}")
|
||
|
||
api = kwargs.get('api')
|
||
fallback_keyboard = None
|
||
|
||
if 'nodes' in operation_name.lower():
|
||
from keyboards import admin_system_keyboard
|
||
fallback_keyboard = admin_system_keyboard(user.language)
|
||
elif 'users' in operation_name.lower():
|
||
from keyboards import system_users_keyboard
|
||
fallback_keyboard = system_users_keyboard(user.language)
|
||
|
||
await APIErrorHandler.handle_api_error(
|
||
callback, e, operation_name, user.language, fallback_keyboard
|
||
)
|
||
|
||
return wrapper
|
||
return decorator
|