381 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			381 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
import logging.config
 | 
						||
 | 
						||
from aiogram import F, Router
 | 
						||
from aiogram.filters import Command
 | 
						||
from aiogram.fsm.context import FSMContext
 | 
						||
from aiogram.types import CallbackQuery, Message
 | 
						||
 | 
						||
import app.telegram.keyboards.inline as kbi
 | 
						||
import app.telegram.keyboards.reply as kbr
 | 
						||
import database.request as rq
 | 
						||
from app.bybit.profile_bybit import user_profile_bybit
 | 
						||
from app.telegram.functions.profile_tg import user_profile_tg
 | 
						||
from logger_helper.logger_helper import LOGGING_CONFIG
 | 
						||
 | 
						||
logging.config.dictConfig(LOGGING_CONFIG)
 | 
						||
logger = logging.getLogger("handlers_main")
 | 
						||
 | 
						||
router_handlers_main = Router(name="handlers_main")
 | 
						||
 | 
						||
 | 
						||
@router_handlers_main.message(Command("start", "hello"))
 | 
						||
@router_handlers_main.message(F.text.lower() == "привет")
 | 
						||
async def cmd_start(message: Message, state: FSMContext) -> None:
 | 
						||
    """
 | 
						||
    Handle the /start or /hello commands and the text message "привет".
 | 
						||
 | 
						||
    Checks if the user exists in the database and sends a user profile or creates a new user
 | 
						||
    with default settings and greeting message.
 | 
						||
 | 
						||
    Args:
 | 
						||
        message (Message): Incoming Telegram message object.
 | 
						||
        state (FSMContext): FSMContext for managing user state.
 | 
						||
 | 
						||
    Raises:
 | 
						||
        None: Exceptions are caught and logged internally.
 | 
						||
    """
 | 
						||
    tg_id = message.from_user.id
 | 
						||
    username = message.from_user.username
 | 
						||
    full_name = message.from_user.full_name
 | 
						||
    user = await rq.get_user(tg_id)
 | 
						||
    try:
 | 
						||
        if user:
 | 
						||
            await user_profile_tg(tg_id=message.from_user.id, message=message)
 | 
						||
            logger.debug(
 | 
						||
                "Command start processed successfully for user: %s",
 | 
						||
                message.from_user.id,
 | 
						||
            )
 | 
						||
        else:
 | 
						||
            await rq.create_user(tg_id=tg_id, username=username)
 | 
						||
            await rq.set_user_symbol(tg_id=tg_id, symbol="BTCUSDT")
 | 
						||
            await rq.create_user_additional_settings(tg_id=tg_id)
 | 
						||
            await rq.create_user_risk_management(tg_id=tg_id)
 | 
						||
            await rq.create_user_conditional_settings(tg_id=tg_id)
 | 
						||
            await message.answer(
 | 
						||
                text=f"Добро пожаловать, {full_name}!\n\n"
 | 
						||
                     "Чат-робот для трейдинга - ваш надежный помощник для анализа рынка и принятия взвешенных решений.😉",
 | 
						||
                reply_markup=kbi.connect_the_platform,
 | 
						||
            )
 | 
						||
            logger.debug(
 | 
						||
                "Command start processed successfully for user: %s",
 | 
						||
                message.from_user.id,
 | 
						||
            )
 | 
						||
    except Exception as e:
 | 
						||
        logger.error(
 | 
						||
            "Error processing command start for user %s: %s", message.from_user.id, e
 | 
						||
        )
 | 
						||
        await message.answer(text="Произошла ошибка. Пожалуйста, попробуйте позже.")
 | 
						||
    finally:
 | 
						||
        await state.clear()
 | 
						||
 | 
						||
 | 
						||
@router_handlers_main.message(Command("profile"))
 | 
						||
@router_handlers_main.message(F.text == "Профиль")
 | 
						||
async def cmd_to_main(message: Message, state: FSMContext) -> None:
 | 
						||
    """
 | 
						||
    Handle the /profile command or text "Профиль".
 | 
						||
 | 
						||
    Clears the current FSM state and sends the Telegram user profile.
 | 
						||
 | 
						||
    Args:
 | 
						||
        message (Message): Incoming Telegram message object.
 | 
						||
        state (FSMContext): FSM state context.
 | 
						||
 | 
						||
    Raises:
 | 
						||
        None: Exceptions are caught and logged internally.
 | 
						||
    """
 | 
						||
    try:
 | 
						||
        await user_profile_tg(tg_id=message.from_user.id, message=message)
 | 
						||
        logger.debug(
 | 
						||
            "Command to_profile_tg processed successfully for user: %s",
 | 
						||
            message.from_user.id,
 | 
						||
        )
 | 
						||
    except Exception as e:
 | 
						||
        logger.error(
 | 
						||
            "Error processing command to_profile_tg for user %s: %s",
 | 
						||
            message.from_user.id,
 | 
						||
            e,
 | 
						||
        )
 | 
						||
    finally:
 | 
						||
        await state.clear()
 | 
						||
 | 
						||
 | 
						||
@router_handlers_main.message(Command("bybit"))
 | 
						||
@router_handlers_main.message(F.text == "Панель Bybit")
 | 
						||
async def profile_bybit(message: Message, state: FSMContext) -> None:
 | 
						||
    """
 | 
						||
    Handle the /bybit command or text "Панель Bybit".
 | 
						||
 | 
						||
    Clears FSM state and sends Bybit trading panel profile.
 | 
						||
 | 
						||
    Args:
 | 
						||
        message (Message): Incoming Telegram message object.
 | 
						||
        state (FSMContext): FSM state context.
 | 
						||
 | 
						||
    Raises:
 | 
						||
        None: Exceptions are caught and logged internally.
 | 
						||
    """
 | 
						||
    try:
 | 
						||
        await state.clear()
 | 
						||
        await user_profile_bybit(
 | 
						||
            tg_id=message.from_user.id, message=message, state=state
 | 
						||
        )
 | 
						||
        logger.debug(
 | 
						||
            "Command to_profile_bybit processed successfully for user: %s",
 | 
						||
            message.from_user.id,
 | 
						||
        )
 | 
						||
    except Exception as e:
 | 
						||
        logger.error(
 | 
						||
            "Error processing command to_profile_bybit for user %s: %s",
 | 
						||
            message.from_user.id,
 | 
						||
            e,
 | 
						||
        )
 | 
						||
 | 
						||
 | 
						||
@router_handlers_main.callback_query(F.data == "profile_bybit")
 | 
						||
async def profile_bybit_callback(
 | 
						||
        callback_query: CallbackQuery, state: FSMContext
 | 
						||
) -> None:
 | 
						||
    """
 | 
						||
    Handle callback query with data "profile_bybit".
 | 
						||
 | 
						||
    Clears FSM state and sends the Bybit profile in response.
 | 
						||
 | 
						||
    Args:
 | 
						||
        callback_query (CallbackQuery): Callback query object from Telegram.
 | 
						||
        state (FSMContext): FSM state context.
 | 
						||
 | 
						||
    Raises:
 | 
						||
        None: Exceptions are caught and logged internally.
 | 
						||
    """
 | 
						||
    try:
 | 
						||
        await state.clear()
 | 
						||
        await user_profile_bybit(
 | 
						||
            tg_id=callback_query.from_user.id,
 | 
						||
            message=callback_query.message,
 | 
						||
            state=state,
 | 
						||
        )
 | 
						||
        logger.debug(
 | 
						||
            "Callback profile_bybit processed successfully for user: %s",
 | 
						||
            callback_query.from_user.id,
 | 
						||
        )
 | 
						||
        await callback_query.answer()
 | 
						||
    except Exception as e:
 | 
						||
        logger.error(
 | 
						||
            "Error processing callback profile_bybit for user %s: %s",
 | 
						||
            callback_query.from_user.id,
 | 
						||
            e,
 | 
						||
        )
 | 
						||
 | 
						||
 | 
						||
@router_handlers_main.callback_query(F.data == "main_settings")
 | 
						||
async def settings(callback_query: CallbackQuery, state: FSMContext) -> None:
 | 
						||
    """
 | 
						||
    Handle callback query with data "main_settings".
 | 
						||
 | 
						||
    Clears FSM state and edits the message to show main settings options.
 | 
						||
 | 
						||
    Args:
 | 
						||
        callback_query (CallbackQuery): Callback query object.
 | 
						||
        state (FSMContext): FSM state context.
 | 
						||
 | 
						||
    Raises:
 | 
						||
        None: Exceptions are caught and logged internally.
 | 
						||
    """
 | 
						||
    try:
 | 
						||
        await state.clear()
 | 
						||
        msg = await callback_query.message.edit_text(
 | 
						||
            text="Выберите, что вы хотите настроить:", reply_markup=kbi.main_settings
 | 
						||
        )
 | 
						||
        await state.update_data(prompt_message_id=msg.message_id)
 | 
						||
        logger.debug(
 | 
						||
            "Command settings processed successfully for user: %s",
 | 
						||
            callback_query.from_user.id,
 | 
						||
        )
 | 
						||
    except Exception as e:
 | 
						||
        logger.error(
 | 
						||
            "Error processing command settings for user %s: %s",
 | 
						||
            callback_query.from_user.id,
 | 
						||
            e,
 | 
						||
        )
 | 
						||
 | 
						||
 | 
						||
@router_handlers_main.message(Command("connect"))
 | 
						||
@router_handlers_main.message(F.text == "Подключить платформу Bybit")
 | 
						||
async def cmd_connect(message: Message, state: FSMContext) -> None:
 | 
						||
    """
 | 
						||
    Handle the /connect command or text "Подключить платформу Bybit".
 | 
						||
 | 
						||
    Clears FSM state and sends a connection message.
 | 
						||
 | 
						||
    Args:
 | 
						||
        message (Message): Incoming Telegram message object.
 | 
						||
        state (FSMContext): FSM state context.
 | 
						||
 | 
						||
    Raises:
 | 
						||
        None: Exceptions are caught and logged internally.
 | 
						||
    """
 | 
						||
    try:
 | 
						||
        await state.clear()
 | 
						||
        user = await rq.get_user(tg_id=message.from_user.id)
 | 
						||
        if user:
 | 
						||
            await message.answer(
 | 
						||
                text=(
 | 
						||
                    "Подключение Bybit аккаунта \n\n"
 | 
						||
                    "1. Зарегистрируйтесь или войдите в свой аккаунт на Bybit по ссылке: "
 | 
						||
                    "[Перейти на Bybit](https://www.bybit.com/invite?ref=YME83OJ).\n"
 | 
						||
                    "2. В личном кабинете выберите раздел API. \n"
 | 
						||
                    "3. Создание нового API ключа\n"
 | 
						||
                    "   - Нажмите кнопку Create New Key (Создать новый ключ).\n"
 | 
						||
                    "   - Выберите системно-сгенерированный ключ.\n"
 | 
						||
                    "   - Укажите название API ключа (любое).  \n"
 | 
						||
                    "   - Выберите права доступа для торговли (Trade).  \n"
 | 
						||
                    "   - Можно ограничить доступ по IP для безопасности.\n"
 | 
						||
                    "4. Подтверждение создания\n"
 | 
						||
                    "   - Подтвердите создание ключа.\n"
 | 
						||
                    "   - Отправьте чат-роботу.\n\n"
 | 
						||
                    "Важно: сохраните отдельно API Key и Secret Key в надежном месте. Secret ключ отображается только один раз."
 | 
						||
                ),
 | 
						||
                parse_mode="Markdown",
 | 
						||
                reply_markup=kbi.add_bybit_api,
 | 
						||
                disable_web_page_preview=True,
 | 
						||
            )
 | 
						||
        else:
 | 
						||
            await rq.create_user(
 | 
						||
                tg_id=message.from_user.id, username=message.from_user.username
 | 
						||
            )
 | 
						||
            await rq.set_user_symbol(tg_id=message.from_user.id, symbol="BTCUSDT")
 | 
						||
            await rq.create_user_additional_settings(tg_id=message.from_user.id)
 | 
						||
            await rq.create_user_risk_management(tg_id=message.from_user.id)
 | 
						||
            await rq.create_user_conditional_settings(tg_id=message.from_user.id)
 | 
						||
            await cmd_connect(message=message, state=state)
 | 
						||
        logger.debug(
 | 
						||
            "Command connect processed successfully for user: %s",
 | 
						||
            message.from_user.id,
 | 
						||
        )
 | 
						||
    except Exception as e:
 | 
						||
        logger.error(
 | 
						||
            "Error processing command connect for user %s: %s",
 | 
						||
            message.from_user.id,
 | 
						||
            e,
 | 
						||
        )
 | 
						||
 | 
						||
 | 
						||
@router_handlers_main.message(Command("help"))
 | 
						||
async def cmd_help(message: Message, state: FSMContext) -> None:
 | 
						||
    """
 | 
						||
    Handle the /help command.
 | 
						||
 | 
						||
    Clears FSM state and sends a help message with available commands and reply keyboard.
 | 
						||
 | 
						||
    Args:
 | 
						||
        message (Message): Incoming Telegram message object.
 | 
						||
        state (FSMContext): FSM state context.
 | 
						||
 | 
						||
    Raises:
 | 
						||
        None: Exceptions are caught and logged internally.
 | 
						||
    """
 | 
						||
    try:
 | 
						||
        await state.clear()
 | 
						||
        await message.answer(
 | 
						||
            text="Используйте одну из следующих команд:\n"
 | 
						||
                 "/start - Запустить бота\n"
 | 
						||
                 "/profile - Профиль\n"
 | 
						||
                 "/bybit - Панель Bybit\n"
 | 
						||
                 "/connect - Подключиться к платформе\n",
 | 
						||
            reply_markup=kbr.profile,
 | 
						||
        )
 | 
						||
        logger.debug(
 | 
						||
            "Command help processed successfully for user: %s",
 | 
						||
            message.from_user.id,
 | 
						||
        )
 | 
						||
    except Exception as e:
 | 
						||
        logger.error(
 | 
						||
            "Error processing command help for user %s: %s", message.from_user.id, e
 | 
						||
        )
 | 
						||
        await message.answer(
 | 
						||
            text="Произошла ошибка. Пожалуйста, попробуйте позже.",
 | 
						||
            reply_markup=kbr.profile,
 | 
						||
        )
 | 
						||
 | 
						||
 | 
						||
@router_handlers_main.message(Command("cancel"))
 | 
						||
@router_handlers_main.message(
 | 
						||
    lambda message: message.text.casefold() in ["cancel", "отмена"]
 | 
						||
)
 | 
						||
async def cmd_cancel_handler(message: Message, state: FSMContext) -> None:
 | 
						||
    """
 | 
						||
    Handle /cancel command or text 'cancel'/'отмена'.
 | 
						||
 | 
						||
    If there is an active FSM state, clears it and informs the user.
 | 
						||
    Otherwise, informs that no operation was in progress.
 | 
						||
 | 
						||
    Args:
 | 
						||
        message (Message): Incoming Telegram message object.
 | 
						||
        state (FSMContext): FSM state context.
 | 
						||
 | 
						||
    Raises:
 | 
						||
        None: Exceptions are caught and logged internally.
 | 
						||
    """
 | 
						||
    current_state = await state.get_state()
 | 
						||
 | 
						||
    if current_state is None:
 | 
						||
        await message.reply(
 | 
						||
            text="Хорошо, но ничего не происходило.", reply_markup=kbr.profile
 | 
						||
        )
 | 
						||
        logger.debug(
 | 
						||
            "Cancel command received but no active state for user %s.",
 | 
						||
            message.from_user.id,
 | 
						||
        )
 | 
						||
        return
 | 
						||
 | 
						||
    try:
 | 
						||
        await state.clear()
 | 
						||
        await message.reply(text="Команда отменена.", reply_markup=kbr.profile)
 | 
						||
        logger.debug(
 | 
						||
            "Command cancel executed successfully. State cleared for user %s.",
 | 
						||
            message.from_user.id,
 | 
						||
        )
 | 
						||
    except Exception as e:
 | 
						||
        logger.error(
 | 
						||
            "Error while cancelling command for user %s: %s", message.from_user.id, e
 | 
						||
        )
 | 
						||
        await message.answer(
 | 
						||
            text="Произошла ошибка при отмене. Пожалуйста, попробуйте позже.",
 | 
						||
            reply_markup=kbr.profile,
 | 
						||
        )
 | 
						||
 | 
						||
 | 
						||
@router_handlers_main.callback_query(F.data == "cancel")
 | 
						||
async def cmd_cancel(callback_query: CallbackQuery, state: FSMContext) -> None:
 | 
						||
    """
 | 
						||
    Handle callback query with data "cancel".
 | 
						||
 | 
						||
    Clears the FSM state and sends a cancellation message.
 | 
						||
 | 
						||
    Args:
 | 
						||
        callback_query (CallbackQuery): Callback query object.
 | 
						||
        state (FSMContext): FSM state context.
 | 
						||
 | 
						||
    Raises:
 | 
						||
        None: Exceptions are caught and logged internally.
 | 
						||
    """
 | 
						||
    try:
 | 
						||
        await callback_query.message.delete()
 | 
						||
        await user_profile_bybit(
 | 
						||
            tg_id=callback_query.from_user.id,
 | 
						||
            message=callback_query.message,
 | 
						||
            state=state,
 | 
						||
        )
 | 
						||
        logger.debug(
 | 
						||
            "Command cancel processed successfully for user: %s",
 | 
						||
            callback_query.from_user.id,
 | 
						||
        )
 | 
						||
    except Exception as e:
 | 
						||
        logger.error(
 | 
						||
            "Error processing command cancel for user %s: %s",
 | 
						||
            callback_query.from_user.id,
 | 
						||
            e,
 | 
						||
        )
 | 
						||
    finally:
 | 
						||
        await state.clear() |