import logging import random import string import csv import io from aiogram import Router, F, types from aiogram.filters import StateFilter from aiogram.fsm.context import FSMContext from datetime import datetime, timedelta, timezone from typing import Optional from sqlalchemy.ext.asyncio import AsyncSession from config.settings import Settings from db.dal import promo_code_dal from bot.states.admin_states import AdminStates from bot.keyboards.inline.admin_keyboards import get_back_to_admin_panel_keyboard, get_admin_panel_keyboard from aiogram.utils.keyboard import InlineKeyboardBuilder, InlineKeyboardButton from bot.middlewares.i18n import JsonI18n router = Router(name="promo_bulk_router") async def create_bulk_promo_prompt_handler(callback: types.CallbackQuery, state: FSMContext, i18n_data: dict, settings: Settings, session: AsyncSession): current_lang = i18n_data.get("current_language", settings.DEFAULT_LANGUAGE) i18n: Optional[JsonI18n] = i18n_data.get("i18n_instance") if not i18n or not callback.message: await callback.answer("Error preparing bulk promo creation.", show_alert=True) return _ = lambda key, **kwargs: i18n.gettext(current_lang, key, **kwargs) # Step 1: Ask for quantity prompt_text = _( "admin_bulk_promo_step1_quantity", default="🎟 МассовоС созданиС ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄ΠΎΠ²\n\nΠ¨Π°Π³ 1 ΠΈΠ· 4: ΠšΠΎΠ»ΠΈΡ‡Π΅ΡΡ‚Π²ΠΎ\n\nΠ’Π²Π΅Π΄ΠΈΡ‚Π΅ количСство ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄ΠΎΠ² для создания (1-100):" ) try: await callback.message.edit_text( prompt_text, reply_markup=get_back_to_admin_panel_keyboard(current_lang, i18n), parse_mode="HTML") except Exception as e: logging.warning( f"Could not edit message for bulk promo prompt: {e}. Sending new.") await callback.message.answer( prompt_text, reply_markup=get_back_to_admin_panel_keyboard(current_lang, i18n), parse_mode="HTML") await callback.answer() await state.set_state(AdminStates.waiting_for_bulk_promo_quantity) def generate_unique_promo_code(length: int = 8) -> str: """Generate a unique random promo code""" characters = string.ascii_uppercase + string.digits return ''.join(random.choice(characters) for _ in range(length)) # Step 1: Process quantity @router.message(AdminStates.waiting_for_bulk_promo_quantity, F.text) async def process_bulk_promo_quantity_handler(message: types.Message, state: FSMContext, i18n_data: dict, settings: Settings): current_lang = i18n_data.get("current_language", settings.DEFAULT_LANGUAGE) i18n: Optional[JsonI18n] = i18n_data.get("i18n_instance") if not i18n: await message.reply("Language service error.") return _ = lambda key, **kwargs: i18n.gettext(current_lang, key, **kwargs) try: quantity = int(message.text.strip()) if not (1 <= quantity <= 100): await message.answer(_( "admin_bulk_promo_invalid_quantity", default="❌ ΠšΠΎΠ»ΠΈΡ‡Π΅ΡΡ‚Π²ΠΎ ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄ΠΎΠ² Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π±Ρ‹Ρ‚ΡŒ ΠΎΡ‚ 1 Π΄ΠΎ 100" )) return await state.update_data(quantity=quantity) # Step 2: Ask for bonus days prompt_text = _( "admin_bulk_promo_step2_bonus_days", default="🎟 МассовоС созданиС ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄ΠΎΠ²\n\nΠ¨Π°Π³ 2 ΠΈΠ· 4: БонусныС Π΄Π½ΠΈ\n\nΠšΠΎΠ»ΠΈΡ‡Π΅ΡΡ‚Π²ΠΎ: {quantity}\n\nΠ’Π²Π΅Π΄ΠΈΡ‚Π΅ количСство бонусных Π΄Π½Π΅ΠΉ для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄Π° (1-365):", quantity=quantity ) await message.answer( prompt_text, reply_markup=get_back_to_admin_panel_keyboard(current_lang, i18n), parse_mode="HTML" ) await state.set_state(AdminStates.waiting_for_bulk_promo_bonus_days) except ValueError: await message.answer(_( "admin_promo_invalid_number", default="❌ Π’Π²Π΅Π΄ΠΈΡ‚Π΅ ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½ΠΎΠ΅ число" )) except Exception as e: logging.error(f"Error processing bulk promo quantity: {e}") await message.answer(_("error_occurred_try_again")) # Step 2: Process bonus days @router.message(AdminStates.waiting_for_bulk_promo_bonus_days, F.text) async def process_bulk_promo_bonus_days_handler(message: types.Message, state: FSMContext, i18n_data: dict, settings: Settings): current_lang = i18n_data.get("current_language", settings.DEFAULT_LANGUAGE) i18n: Optional[JsonI18n] = i18n_data.get("i18n_instance") if not i18n: await message.reply("Language service error.") return _ = lambda key, **kwargs: i18n.gettext(current_lang, key, **kwargs) try: bonus_days = int(message.text.strip()) if not (1 <= bonus_days <= 365): await message.answer(_( "admin_promo_invalid_bonus_days", default="❌ ΠšΠΎΠ»ΠΈΡ‡Π΅ΡΡ‚Π²ΠΎ бонусных Π΄Π½Π΅ΠΉ Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π±Ρ‹Ρ‚ΡŒ ΠΎΡ‚ 1 Π΄ΠΎ 365" )) return await state.update_data(bonus_days=bonus_days) # Step 3: Ask for max activations data = await state.get_data() prompt_text = _( "admin_bulk_promo_step3_max_activations", default="🎟 МассовоС созданиС ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄ΠΎΠ²\n\nΠ¨Π°Π³ 3 ΠΈΠ· 4: Π›ΠΈΠΌΠΈΡ‚ Π°ΠΊΡ‚ΠΈΠ²Π°Ρ†ΠΈΠΉ\n\nΠšΠΎΠ»ΠΈΡ‡Π΅ΡΡ‚Π²ΠΎ: {quantity}\nБонусныС Π΄Π½ΠΈ: {bonus_days}\n\nΠ’Π²Π΅Π΄ΠΈΡ‚Π΅ максимальноС количСство Π°ΠΊΡ‚ΠΈΠ²Π°Ρ†ΠΈΠΉ для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄Π° (1-10000):", quantity=data.get("quantity"), bonus_days=bonus_days ) await message.answer( prompt_text, reply_markup=get_back_to_admin_panel_keyboard(current_lang, i18n), parse_mode="HTML" ) await state.set_state(AdminStates.waiting_for_bulk_promo_max_activations) except ValueError: await message.answer(_( "admin_promo_invalid_number", default="❌ Π’Π²Π΅Π΄ΠΈΡ‚Π΅ ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½ΠΎΠ΅ число" )) except Exception as e: logging.error(f"Error processing bulk promo bonus days: {e}") await message.answer(_("error_occurred_try_again")) # Step 3: Process max activations @router.message(AdminStates.waiting_for_bulk_promo_max_activations, F.text) async def process_bulk_promo_max_activations_handler(message: types.Message, state: FSMContext, i18n_data: dict, settings: Settings): current_lang = i18n_data.get("current_language", settings.DEFAULT_LANGUAGE) i18n: Optional[JsonI18n] = i18n_data.get("i18n_instance") if not i18n: await message.reply("Language service error.") return _ = lambda key, **kwargs: i18n.gettext(current_lang, key, **kwargs) try: max_activations = int(message.text.strip()) if not (1 <= max_activations <= 10000): await message.answer(_( "admin_promo_invalid_max_activations", default="❌ МаксимальноС количСство Π°ΠΊΡ‚ΠΈΠ²Π°Ρ†ΠΈΠΉ Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π±Ρ‹Ρ‚ΡŒ ΠΎΡ‚ 1 Π΄ΠΎ 10000" )) return await state.update_data(max_activations=max_activations) # Step 4: Ask for validity data = await state.get_data() prompt_text = _( "admin_bulk_promo_step4_validity", default="🎟 МассовоС созданиС ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄ΠΎΠ²\n\nΠ¨Π°Π³ 4 ΠΈΠ· 4: Π‘Ρ€ΠΎΠΊ дСйствия\n\nΠšΠΎΠ»ΠΈΡ‡Π΅ΡΡ‚Π²ΠΎ: {quantity}\nБонусныС Π΄Π½ΠΈ: {bonus_days}\nМакс. Π°ΠΊΡ‚ΠΈΠ²Π°Ρ†ΠΈΠΉ: {max_activations}\n\nΠ’Ρ‹Π±Π΅Ρ€ΠΈΡ‚Π΅ срок дСйствия ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄ΠΎΠ²:", quantity=data.get("quantity"), bonus_days=data.get("bonus_days"), max_activations=max_activations ) # Create keyboard for validity options builder = InlineKeyboardBuilder() builder.row( InlineKeyboardButton( text=_("admin_promo_unlimited_validity", default="πŸ”„ Π‘Π΅Π· ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΠΉ"), callback_data="bulk_promo_unlimited_validity" ) ) builder.row( InlineKeyboardButton( text=_("admin_promo_set_validity_days", default="πŸ“… Π£ΠΊΠ°Π·Π°Ρ‚ΡŒ Π΄Π½ΠΈ"), callback_data="bulk_promo_set_validity" ) ) builder.row( InlineKeyboardButton( text=_("admin_back_to_panel", default="πŸ”™ Π’ Π°Π΄ΠΌΠΈΠ½ панСль"), callback_data="admin_action:main" ) ) await message.answer( prompt_text, reply_markup=builder.as_markup(), parse_mode="HTML" ) await state.set_state(AdminStates.waiting_for_bulk_promo_validity_days) except ValueError: await message.answer(_( "admin_promo_invalid_number", default="❌ Π’Π²Π΅Π΄ΠΈΡ‚Π΅ ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½ΠΎΠ΅ число" )) except Exception as e: logging.error(f"Error processing bulk promo max activations: {e}") await message.answer(_("error_occurred_try_again")) # Step 4: Handle unlimited validity @router.callback_query(F.data == "bulk_promo_unlimited_validity", StateFilter(AdminStates.waiting_for_bulk_promo_validity_days)) async def process_bulk_promo_unlimited_validity(callback: types.CallbackQuery, state: FSMContext, i18n_data: dict, settings: Settings, session: AsyncSession): await state.update_data(validity_days=None) await create_bulk_promo_codes_final(callback, state, i18n_data, settings, session) # Step 4: Handle set validity @router.callback_query(F.data == "bulk_promo_set_validity", StateFilter(AdminStates.waiting_for_bulk_promo_validity_days)) async def process_bulk_promo_set_validity(callback: types.CallbackQuery, state: FSMContext, i18n_data: dict, settings: Settings): current_lang = i18n_data.get("current_language", settings.DEFAULT_LANGUAGE) i18n: Optional[JsonI18n] = i18n_data.get("i18n_instance") if not i18n or not callback.message: await callback.answer("Error processing validity.", show_alert=True) return _ = lambda key, **kwargs: i18n.gettext(current_lang, key, **kwargs) data = await state.get_data() prompt_text = _( "admin_bulk_promo_enter_validity_days", default="🎟 МассовоС созданиС ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄ΠΎΠ²\n\nΠ¨Π°Π³ 4 ΠΈΠ· 4: Π‘Ρ€ΠΎΠΊ дСйствия\n\nΠšΠΎΠ»ΠΈΡ‡Π΅ΡΡ‚Π²ΠΎ: {quantity}\nБонусныС Π΄Π½ΠΈ: {bonus_days}\nМакс. Π°ΠΊΡ‚ΠΈΠ²Π°Ρ†ΠΈΠΉ: {max_activations}\n\nΠ’Π²Π΅Π΄ΠΈΡ‚Π΅ количСство Π΄Π½Π΅ΠΉ дСйствия ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄ΠΎΠ² (1-365):", quantity=data.get("quantity"), bonus_days=data.get("bonus_days"), max_activations=data.get("max_activations") ) try: await callback.message.edit_text( prompt_text, reply_markup=get_back_to_admin_panel_keyboard(current_lang, i18n), parse_mode="HTML" ) except Exception: await callback.message.answer( prompt_text, reply_markup=get_back_to_admin_panel_keyboard(current_lang, i18n), parse_mode="HTML" ) await callback.answer() # Step 4: Process validity days @router.message(AdminStates.waiting_for_bulk_promo_validity_days, F.text) async def process_bulk_promo_validity_days_handler(message: types.Message, state: FSMContext, i18n_data: dict, settings: Settings, session: AsyncSession): current_lang = i18n_data.get("current_language", settings.DEFAULT_LANGUAGE) i18n: Optional[JsonI18n] = i18n_data.get("i18n_instance") if not i18n: await message.reply("Language service error.") return _ = lambda key, **kwargs: i18n.gettext(current_lang, key, **kwargs) try: validity_days = int(message.text.strip()) if not (1 <= validity_days <= 365): await message.answer(_( "admin_promo_invalid_validity_days", default="❌ Π‘Ρ€ΠΎΠΊ дСйствия Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ ΠΎΡ‚ 1 Π΄ΠΎ 365 Π΄Π½Π΅ΠΉ" )) return await state.update_data(validity_days=validity_days) await create_bulk_promo_codes_final(message, state, i18n_data, settings, session) except ValueError: await message.answer(_( "admin_promo_invalid_number", default="❌ Π’Π²Π΅Π΄ΠΈΡ‚Π΅ ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½ΠΎΠ΅ число" )) except Exception as e: logging.error(f"Error processing bulk promo validity days: {e}") await message.answer(_("error_occurred_try_again")) async def create_bulk_promo_codes_final(callback_or_message, state: FSMContext, i18n_data: dict, settings: Settings, session: AsyncSession): """Final step - create multiple promo codes in database""" current_lang = i18n_data.get("current_language", settings.DEFAULT_LANGUAGE) i18n: Optional[JsonI18n] = i18n_data.get("i18n_instance") if not i18n: return _ = lambda key, **kwargs: i18n.gettext(current_lang, key, **kwargs) try: data = await state.get_data() quantity = data["quantity"] # Show progress message progress_text = _( "admin_bulk_promo_creating", default="πŸ”„ Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ {quantity} ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄ΠΎΠ²...", quantity=quantity ) if hasattr(callback_or_message, 'message'): # CallbackQuery try: await callback_or_message.message.edit_text(progress_text, parse_mode="HTML") except Exception: await callback_or_message.message.answer(progress_text, parse_mode="HTML") await callback_or_message.answer() else: # Message await callback_or_message.answer(progress_text, parse_mode="HTML") # Generate and create promo codes created_codes = [] failed_codes = [] for i in range(quantity): try: # Generate unique code attempts = 0 while attempts < 10: # Max 10 attempts to generate unique code promo_code = generate_unique_promo_code() existing_promo = await promo_code_dal.get_promo_code_by_code(session, promo_code) if not existing_promo: break attempts += 1 if attempts >= 10: failed_codes.append(f"Код #{i+1} (Π½Π΅ ΡƒΠ΄Π°Π»ΠΎΡΡŒ ΡΠ³Π΅Π½Π΅Ρ€ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½Ρ‹ΠΉ)") continue # Prepare promo code data promo_data = { "code": promo_code, "bonus_days": data["bonus_days"], "max_activations": data["max_activations"], "current_activations": 0, "is_active": True, "created_by_admin_id": callback_or_message.from_user.id, "created_at": datetime.now(timezone.utc) } # Set validity if data.get("validity_days"): promo_data["valid_until"] = datetime.now(timezone.utc) + timedelta(days=data["validity_days"]) else: promo_data["valid_until"] = None # Create promo code created_promo = await promo_code_dal.create_promo_code(session, promo_data) created_codes.append(created_promo.code) except Exception as e: logging.error(f"Error creating bulk promo code #{i+1}: {e}") failed_codes.append(f"Код #{i+1} ({str(e)[:50]})") await session.commit() # Success message success_lines = [ _( "admin_bulk_promo_created_title", default="βœ… МассовоС созданиС Π·Π°Π²Π΅Ρ€ΡˆΠ΅Π½ΠΎ!\n" ), _( "admin_bulk_promo_created_stats", default="πŸ“Š Π‘ΠΎΠ·Π΄Π°Π½ΠΎ: {created} ΠΈΠ· {total}", created=len(created_codes), total=quantity ) ] if data.get("validity_days"): validity_text = f"{data['validity_days']} Π΄Π½Π΅ΠΉ" else: validity_text = _("admin_promo_unlimited", default="Π‘Π΅Π· ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΠΉ") success_lines.append( _( "admin_bulk_promo_settings", default="🎁 БонусныС Π΄Π½ΠΈ: {bonus_days}\n" "πŸ“Š Макс. Π°ΠΊΡ‚ΠΈΠ²Π°Ρ†ΠΈΠΉ: {max_activations}\n" "⏰ Π‘Ρ€ΠΎΠΊ дСйствия: {validity}", bonus_days=data["bonus_days"], max_activations=data["max_activations"], validity=validity_text ) ) # Create CSV file with promo codes if any were created csv_file = None if created_codes: success_lines.append(f"\n🎟 Π‘ΠΎΠ·Π΄Π°Π½ΠΎ {len(created_codes)} ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄ΠΎΠ²") success_lines.append("πŸ“„ CSV Ρ„Π°ΠΉΠ» с ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄Π°ΠΌΠΈ ΠΎΡ‚ΠΏΡ€Π°Π²Π»Π΅Π½ ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Ρ‹ΠΌ сообщСниСм") # Create CSV file output = io.StringIO() writer = csv.writer(output) # CSV headers writer.writerow([ "ΠŸΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄", "БонусныС Π΄Π½ΠΈ", "Макс. Π°ΠΊΡ‚ΠΈΠ²Π°Ρ†ΠΈΠΈ", "ДСйствитСлСн Π΄ΠΎ", "Команда для старта", "Бсылка для Π°ΠΊΡ‚ΠΈΠ²Π°Ρ†ΠΈΠΈ" ]) # Get real bot username bot_username = 'your_bot' # fallback try: if hasattr(callback_or_message, 'message'): bot = callback_or_message.message.bot else: bot = callback_or_message.bot bot_info = await bot.get_me() bot_username = bot_info.username or 'your_bot' except Exception as e: logging.error(f"Failed to get bot username for CSV links: {e}") bot_username = 'your_bot' for code in created_codes: # Determine validity info if data.get("validity_days"): valid_until = (datetime.now(timezone.utc) + timedelta(days=data["validity_days"])).strftime("%Y-%m-%d %H:%M:%S") else: valid_until = "Π‘Π΅Π· ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΠΉ" start_command = f"/start promo_{code}" telegram_link = f"https://t.me/{bot_username}?start=promo_{code}" writer.writerow([ code, data["bonus_days"], data["max_activations"], valid_until, start_command, telegram_link ]) output.seek(0) # Create file for sending filename = f"bulk_promo_codes_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv" csv_file = types.BufferedInputFile( output.getvalue().encode('utf-8-sig'), # BOM for correct Excel display filename=filename ) if failed_codes: success_lines.append(f"\n❌ Ошибки ({len(failed_codes)}):") for error in failed_codes[:5]: # Show first 5 errors success_lines.append(error) if len(failed_codes) > 5: success_lines.append(f"... ΠΈ Π΅Ρ‰Π΅ {len(failed_codes) - 5} ошибок") success_text = "\n".join(success_lines) if hasattr(callback_or_message, 'message'): # CallbackQuery try: await callback_or_message.message.edit_text( success_text, reply_markup=get_back_to_admin_panel_keyboard(current_lang, i18n), parse_mode="HTML" ) message_obj = callback_or_message.message except Exception: message_obj = await callback_or_message.message.answer( success_text, reply_markup=get_back_to_admin_panel_keyboard(current_lang, i18n), parse_mode="HTML" ) await callback_or_message.answer() else: # Message message_obj = await callback_or_message.answer( success_text, reply_markup=get_back_to_admin_panel_keyboard(current_lang, i18n), parse_mode="HTML" ) # Send CSV file if created if csv_file: csv_caption = f"πŸ“„ ΠŸΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄Ρ‹ для массового создания\nπŸ’« ВсСго: {len(created_codes)} ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄ΠΎΠ²\n🎁 Бонус: {data['bonus_days']} Π΄Π½Π΅ΠΉ ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ" await message_obj.answer_document(csv_file, caption=csv_caption) await state.clear() except Exception as e: logging.error(f"Error creating bulk promo codes: {e}") error_text = _("error_occurred_try_again", default="❌ ΠŸΡ€ΠΎΠΈΠ·ΠΎΡˆΠ»Π° ошибка. ΠŸΠΎΠΏΡ€ΠΎΠ±ΡƒΠΉΡ‚Π΅ снова.") if hasattr(callback_or_message, 'message'): # CallbackQuery await callback_or_message.message.answer(error_text) else: # Message await callback_or_message.answer(error_text) await state.clear() # Cancel bulk promo creation @router.callback_query( F.data == "admin_action:main", StateFilter( AdminStates.waiting_for_bulk_promo_quantity, AdminStates.waiting_for_bulk_promo_bonus_days, AdminStates.waiting_for_bulk_promo_max_activations, AdminStates.waiting_for_bulk_promo_validity_days, ), ) async def cancel_bulk_promo_creation_state_to_menu(callback: types.CallbackQuery, state: FSMContext, settings: Settings, i18n_data: dict, session: AsyncSession): current_lang = i18n_data.get("current_language", settings.DEFAULT_LANGUAGE) i18n: Optional[JsonI18n] = i18n_data.get("i18n_instance") if not i18n or not callback.message: await callback.answer("Error cancelling.", show_alert=True) return _ = lambda key, **kwargs: i18n.gettext(current_lang, key, **kwargs) try: await callback.message.edit_text( _(key="admin_panel_title"), reply_markup=get_admin_panel_keyboard(i18n, current_lang, settings) ) except Exception: await callback.message.answer( _(key="admin_panel_title"), reply_markup=get_admin_panel_keyboard(i18n, current_lang, settings) ) await callback.answer(_("admin_bulk_promo_creation_cancelled", default="МассовоС созданиС ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄ΠΎΠ² ΠΎΡ‚ΠΌΠ΅Π½Π΅Π½ΠΎ")) await state.clear()