From 8f6b5e56bb39da7df728ddf7d4d81734d7df29fb Mon Sep 17 00:00:00 2001 From: wasrusgen Date: Tue, 12 May 2026 18:46:30 +0300 Subject: [PATCH] =?UTF-8?q?bot:=203-=D1=83=D1=80=D0=BE=D0=B2=D0=BD=D0=B5?= =?UTF-8?q?=D0=B2=D0=BE=D0=B5=20=D0=BC=D0=B5=D0=BD=D1=8E=20=E2=80=94=20?= =?UTF-8?q?=D1=80=D0=BE=D0=BB=D1=8C=20=E2=86=92=20action=20=E2=86=92=20Min?= =?UTF-8?q?iApp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /start теперь показывает только две reply-кнопки внизу: [👤 Я менеджер] [🏠 Я клиент] Тап «Я менеджер» → меню менеджера (4 ряда): 🤖 Подбор техники | 📐 Новый замер ← WebApp 👥 Мои клиенты | 🏠 Кабинет ← WebApp ℹ️ Что умеет бот?| 📞 Куратор ← текст 📋 Чек-лист встречи | ⬅️ Сменить роль ← текст Тап «Я клиент» → меню клиента (3 ряда): 🏠 Мой кабинет | 📐 Мой замер ← WebApp 📞 Связь с менеджером | ℹ️ О сервисе ← текст ⬅️ Сменить роль «⬅️ Сменить роль» в любом меню → возврат к выбору роли. Заменён inline-keyboard на reply-keyboard (постоянная панель снизу). --- bot/handlers/start.py | 172 +++++++++++++++++++++++++++++------------- 1 file changed, 121 insertions(+), 51 deletions(-) diff --git a/bot/handlers/start.py b/bot/handlers/start.py index da38adb..8a0ccbd 100644 --- a/bot/handlers/start.py +++ b/bot/handlers/start.py @@ -3,8 +3,6 @@ import time from aiogram import F, Router from aiogram.filters import Command, CommandStart from aiogram.types import ( - InlineKeyboardButton, - InlineKeyboardMarkup, KeyboardButton, Message, ReplyKeyboardMarkup, @@ -17,6 +15,10 @@ from config import Config router = Router(name="start") +# ============================================================ +# URL helpers +# ============================================================ + def _bust_cache(url: str) -> str: """Append unique timestamp to MiniApp URL so Telegram WebView can't cache between sessions.""" sep = "&" if "?" in url else "?" @@ -30,42 +32,41 @@ def _with_query(url: str, **params: str) -> str: return f"{url}{sep}{pairs}" if pairs else url -def role_choice_kb(miniapp_url: str) -> InlineKeyboardMarkup: - """Two WebApp buttons — one tap opens the cabinet directly, no intermediate step.""" - return InlineKeyboardMarkup( - inline_keyboard=[ - [ - InlineKeyboardButton( - text="👤 Менеджер", - web_app=WebAppInfo(url=_bust_cache(_with_query(miniapp_url, role="manager"))), - ), - InlineKeyboardButton( - text="🏠 Клиент", - web_app=WebAppInfo(url=_bust_cache(_with_query(miniapp_url, role="client"))), - ), - ] - ] - ) +def _wapp(miniapp_url: str, role: str, go: str = "") -> WebAppInfo: + """Build a WebAppInfo with role + optional ?go=.""" + return WebAppInfo(url=_bust_cache(_with_query(miniapp_url, role=role, go=go))) -def manager_reply_kb(miniapp_url: str) -> ReplyKeyboardMarkup: - """Persistent bottom keyboard — fast access to key MiniApp screens + info text actions. - Reply-keyboard `web_app` buttons открывают MiniApp с указанным URL/query.""" - def wapp(go: str) -> WebAppInfo: - return WebAppInfo(url=_bust_cache(_with_query(miniapp_url, role="manager", go=go))) +# ============================================================ +# Reply keyboards (3 уровня) +# ============================================================ +# Уровень 1 — выбор роли (плоские текстовые кнопки внизу) +def role_choice_kb() -> ReplyKeyboardMarkup: return ReplyKeyboardMarkup( keyboard=[ [ - KeyboardButton(text="🤖 Подбор техники", web_app=wapp("podbor")), - KeyboardButton(text="📐 Новый замер", web_app=wapp("measure")), + KeyboardButton(text="👤 Я менеджер"), + KeyboardButton(text="🏠 Я клиент"), + ], + ], + resize_keyboard=True, + is_persistent=True, + input_field_placeholder="Выберите кто вы…", + ) + + +# Уровень 2a — меню менеджера (WebApp + текст) +def manager_kb(miniapp_url: str) -> ReplyKeyboardMarkup: + return ReplyKeyboardMarkup( + keyboard=[ + [ + KeyboardButton(text="🤖 Подбор техники", web_app=_wapp(miniapp_url, "manager", "podbor")), + KeyboardButton(text="📐 Новый замер", web_app=_wapp(miniapp_url, "manager", "measure")), ], [ - KeyboardButton(text="👥 Мои клиенты", web_app=wapp("clients")), - KeyboardButton( - text="🏠 Кабинет", - web_app=WebAppInfo(url=_bust_cache(_with_query(miniapp_url, role="manager"))), - ), + KeyboardButton(text="👥 Мои клиенты", web_app=_wapp(miniapp_url, "manager", "clients")), + KeyboardButton(text="🏠 Кабинет", web_app=_wapp(miniapp_url, "manager")), ], [ KeyboardButton(text="ℹ️ Что умеет бот?"), @@ -73,6 +74,7 @@ def manager_reply_kb(miniapp_url: str) -> ReplyKeyboardMarkup: ], [ KeyboardButton(text="📋 Чек-лист встречи"), + KeyboardButton(text="⬅️ Сменить роль"), ], ], resize_keyboard=True, @@ -81,42 +83,82 @@ def manager_reply_kb(miniapp_url: str) -> ReplyKeyboardMarkup: ) -# ---------- /start ---------- +# Уровень 2b — меню клиента (WebApp + текст) +def client_kb(miniapp_url: str) -> ReplyKeyboardMarkup: + return ReplyKeyboardMarkup( + keyboard=[ + [ + KeyboardButton(text="🏠 Мой кабинет", web_app=_wapp(miniapp_url, "client")), + KeyboardButton(text="📐 Мой замер", web_app=_wapp(miniapp_url, "client", "measure")), + ], + [ + KeyboardButton(text="📞 Связь с менеджером"), + KeyboardButton(text="ℹ️ О сервисе"), + ], + [ + KeyboardButton(text="⬅️ Сменить роль"), + ], + ], + resize_keyboard=True, + is_persistent=True, + input_field_placeholder="Выберите действие…", + ) + + +# ============================================================ +# Commands +# ============================================================ @router.message(CommandStart()) async def cmd_start(message: Message, config: Config) -> None: - # Сразу даём постоянную клавиатуру + inline-выбор роли. Менеджер будет - # видеть нижнюю клавиатуру после первого тапа на роль. await message.answer( "👋 Здравствуйте, я бот-помощник от Руслана ВАСИЛЬЕВА.\n\n" - "Кто вы?", - reply_markup=role_choice_kb(config.miniapp_url), - ) - # Постоянная клавиатура снизу — для быстрого доступа из любого экрана чата - await message.answer( - "📲 Внизу появилась панель быстрого доступа — открывайте кабинет или нужный экран одним тапом.", - reply_markup=manager_reply_kb(config.miniapp_url), + "Выберите, кто вы — внизу появилась панель.", + reply_markup=role_choice_kb(), ) -# ---------- /menu (вернуть клавиатуру если она была скрыта) ---------- - @router.message(Command("menu")) -async def cmd_menu(message: Message, config: Config) -> None: - await message.answer( - "📲 Панель быстрого доступа:", - reply_markup=manager_reply_kb(config.miniapp_url), - ) +async def cmd_menu(message: Message) -> None: + """Возвращает к выбору роли.""" + await message.answer("Выберите роль:", reply_markup=role_choice_kb()) -# ---------- /hide (убрать клавиатуру) ---------- - @router.message(Command("hide")) async def cmd_hide(message: Message) -> None: await message.answer("Клавиатура скрыта. Вернуть — /menu", reply_markup=ReplyKeyboardRemove()) -# ---------- Текстовые кнопки нижней клавиатуры ---------- +# ============================================================ +# Уровень 1 → 2: выбор роли +# ============================================================ + +@router.message(F.text == "👤 Я менеджер") +async def role_manager(message: Message, config: Config) -> None: + await message.answer( + "Меню менеджера\n\n" + "Выбирайте действие — большинство кнопок открывают кабинет на нужном экране одним тапом.", + reply_markup=manager_kb(config.miniapp_url), + ) + + +@router.message(F.text == "🏠 Я клиент") +async def role_client(message: Message, config: Config) -> None: + await message.answer( + "Меню клиента\n\n" + "Здесь видны ваш замер и личный кабинет от менеджера ЗОВ.", + reply_markup=client_kb(config.miniapp_url), + ) + + +@router.message(F.text == "⬅️ Сменить роль") +async def back_to_role(message: Message) -> None: + await message.answer("Выберите роль:", reply_markup=role_choice_kb()) + + +# ============================================================ +# Текстовые кнопки меню менеджера +# ============================================================ @router.message(F.text == "ℹ️ Что умеет бот?") async def kb_about(message: Message) -> None: @@ -134,7 +176,7 @@ async def kb_about(message: Message) -> None: @router.message(F.text == "📞 Связь с куратором") -async def kb_contact(message: Message) -> None: +async def kb_contact_curator(message: Message) -> None: await message.answer( "Куратор сети:\n\n" "👤 Руслан Васильев\n" @@ -165,3 +207,31 @@ async def kb_checklist(message: Message) -> None: "• Поставить замер и подбор в карточку клиента\n" "• Следующий шаг: дизайн-проект кухни ЗОВ" ) + + +# ============================================================ +# Текстовые кнопки меню клиента +# ============================================================ + +@router.message(F.text == "📞 Связь с менеджером") +async def kb_contact_manager(message: Message) -> None: + await message.answer( + "Ваш менеджер ЗОВ:\n\n" + "Связаться с менеджером можно через ваш кабинет — там указаны контакты " + "сотрудника, который ведёт ваш проект.\n\n" + "Если кабинет ещё не открывался — попросите менеджера прислать " + "приглашение или напишите куратору сети @wasrusgen." + ) + + +@router.message(F.text == "ℹ️ О сервисе") +async def kb_about_service(message: Message) -> None: + await message.answer( + "О сервисе ЗОВ\n\n" + "ЗОВ — фабрика кухонной мебели премиум-сегмента из Беларуси.\n\n" + "Этот бот помогает менеджерам ЗОВ:\n" + "• сделать замер вашей кухни\n" + "• подобрать встраиваемую технику под ваш бюджет и образ жизни\n" + "• сохранить всё в одном кабинете для совместной работы\n\n" + "🌐 zov.by · 📍 СПб / Москва · 💬 @wasrusgen1" + )