import time
from aiogram import F, Router
from aiogram.filters import Command, CommandStart
from aiogram.types import (
InlineKeyboardButton,
InlineKeyboardMarkup,
KeyboardButton,
Message,
ReplyKeyboardMarkup,
ReplyKeyboardRemove,
WebAppInfo,
)
from config import Config
router = Router(name="start")
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 "?"
return f"{url}{sep}t={int(time.time())}"
def _with_query(url: str, **params: str) -> str:
"""Append query params (e.g. role=manager, go=podbor) preserving existing ones."""
sep = "&" if "?" in url else "?"
pairs = "&".join(f"{k}={v}" for k, v in params.items() if v)
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 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)))
return ReplyKeyboardMarkup(
keyboard=[
[
KeyboardButton(text="🤖 Подбор техники", web_app=wapp("podbor")),
KeyboardButton(text="📐 Новый замер", web_app=wapp("measure")),
],
[
KeyboardButton(text="👥 Мои клиенты", web_app=wapp("clients")),
KeyboardButton(
text="🏠 Кабинет",
web_app=WebAppInfo(url=_bust_cache(_with_query(miniapp_url, role="manager"))),
),
],
[
KeyboardButton(text="ℹ️ Что умеет бот?"),
KeyboardButton(text="📞 Связь с куратором"),
],
[
KeyboardButton(text="📋 Чек-лист встречи"),
],
],
resize_keyboard=True,
is_persistent=True,
input_field_placeholder="Выберите действие…",
)
# ---------- /start ----------
@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),
)
# ---------- /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),
)
# ---------- /hide (убрать клавиатуру) ----------
@router.message(Command("hide"))
async def cmd_hide(message: Message) -> None:
await message.answer("Клавиатура скрыта. Вернуть — /menu", reply_markup=ReplyKeyboardRemove())
# ---------- Текстовые кнопки нижней клавиатуры ----------
@router.message(F.text == "ℹ️ Что умеет бот?")
async def kb_about(message: Message) -> None:
await message.answer(
"ZOV Tech Picker — что умеет бот:\n\n"
"🤖 Подбор техники — AI собирает 3-7 моделей под клиента, "
"со сравнением цен на 4 маркетплейсах, плюсами/минусами и ссылками\n\n"
"📐 Замеры кухни — мастер из 6 шагов: форма, размеры, окна/двери, фото, "
"сохраняется в карточку клиента\n\n"
"👥 Клиенты — история подборов и замеров по каждому клиенту, "
"с возможностью переоткрыть отчёт или скачать PDF\n\n"
"🏠 Кабинет — главный экран менеджера с задачами на сегодня\n\n"
"Подсказка: внизу постоянная панель — открывает нужный экран одним тапом."
)
@router.message(F.text == "📞 Связь с куратором")
async def kb_contact(message: Message) -> None:
await message.answer(
"Куратор сети:\n\n"
"👤 Руслан Васильев\n"
"Telegram: @wasrusgen\n"
"Канал партнёрской сети: @wasrusgen1\n\n"
"Пишите по любым вопросам — от подключения к боту до сложных подборов техники."
)
@router.message(F.text == "📋 Чек-лист встречи")
async def kb_checklist(message: Message) -> None:
await message.answer(
"📋 Чек-лист встречи с клиентом\n\n"
"До встречи:\n"
"• Получить контакт и согласовать время\n"
"• Уточнить — новая кухня или замена техники\n"
"• Понять бюджет (премиум / средний / эконом)\n\n"
"На встрече:\n"
"1. Замер кухни (📐 Новый замер в боте)\n"
" — стены, потолок, площадь\n"
" — окна, двери, вытяжка, газ/электро\n"
" — 5-10 фото со всех углов\n\n"
"2. Образ жизни клиента (что готовит, как часто)\n"
"3. Категории техники нужны (холодильник / варочная / духовка / посудомойка / вытяжка / СВЧ / кофемашина)\n"
"4. Запустить 🤖 Подбор техники — получить 3-7 моделей за 30 сек\n\n"
"После встречи:\n"
"• Скачать PDF подбора → отправить клиенту\n"
"• Поставить замер и подбор в карточку клиента\n"
"• Следующий шаг: дизайн-проект кухни ЗОВ"
)