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()