From af7dc0772097d9f9a9affe99c4027a1bd60a49a2 Mon Sep 17 00:00:00 2001 From: wasrusgen Date: Sat, 9 May 2026 13:05:20 +0300 Subject: [PATCH] feat: one-tap role buttons (WebApp directly, no intermediate step) + role param in URL/backend --- backend/Code.gs | 21 +++++++++++------- bot/handlers/start.py | 50 +++++++++++++++---------------------------- miniapp/assets/app.js | 5 +++++ miniapp/index.html | 6 +++--- 4 files changed, 38 insertions(+), 44 deletions(-) diff --git a/backend/Code.gs b/backend/Code.gs index fada0b3..0815731 100644 --- a/backend/Code.gs +++ b/backend/Code.gs @@ -147,7 +147,8 @@ function handleMe(body) { // Регистрируем пользователя если первый раз, обновляем last_seen_at const startParam = body.startParam || auth.start_param; - const user = getOrCreateUser(auth.user, startParam); + const explicitRole = (body.role === "manager" || body.role === "client") ? body.role : null; + const user = getOrCreateUser(auth.user, startParam, explicitRole); if (user.role === "manager") { const m = getManagerProfile(tgId) || synthesizeManagerFromUser(user); @@ -301,7 +302,7 @@ function findUser(tgId) { return null; } -function getOrCreateUser(tgUser, startParam) { +function getOrCreateUser(tgUser, startParam, explicitRole) { const tgId = tgUser.id; const props = PropertiesService.getScriptProperties(); const adminId = parseInt(props.getProperty("ADMIN_TG_ID") || "0", 10); @@ -309,22 +310,26 @@ function getOrCreateUser(tgUser, startParam) { const existing = findUser(tgId); if (existing) { updateColumnByKey("Users", "tg_id", tgId, "last_seen_at", new Date()); - // Если это админ и роль ещё не manager — повышаем + автозавод в Managers + // Админ всегда manager if (tgId === adminId && existing.role !== "manager") { updateColumnByKey("Users", "tg_id", tgId, "role", "manager"); ensureAdminManager(tgUser); existing.role = "manager"; } + // Не-админ может явно сменить роль через URL ?role= + else if (explicitRole && tgId !== adminId && existing.role !== explicitRole) { + updateColumnByKey("Users", "tg_id", tgId, "role", explicitRole); + existing.role = explicitRole; + } return existing; } - // Определяем роль: - // - админ → manager (автозавод в Managers как ZOV-employee) - // - invite-код менеджера → клиент с привязкой - // - иначе → client + // Новый пользователь — определяем роль let role = "client"; let inviteCode = ""; if (tgId === adminId) { role = "manager"; + } else if (explicitRole) { + role = explicitRole; } else if (startParam && startParam.indexOf("client_inv_") === 0) { role = "client"; inviteCode = startParam; @@ -337,7 +342,7 @@ function getOrCreateUser(tgUser, startParam) { if (tgId === adminId) { ensureAdminManager(tgUser); } - log("user_registered", tgId, { role, startParam }); + log("user_registered", tgId, { role, startParam, explicitRole }); return findUser(tgId); } diff --git a/bot/handlers/start.py b/bot/handlers/start.py index dec30bb..9dc9fe0 100644 --- a/bot/handlers/start.py +++ b/bot/handlers/start.py @@ -1,11 +1,12 @@ -from aiogram import Router, F +import time + +from aiogram import Router from aiogram.filters import CommandStart from aiogram.types import ( Message, InlineKeyboardMarkup, InlineKeyboardButton, WebAppInfo, - CallbackQuery, ) from config import Config @@ -13,25 +14,25 @@ from config import Config router = Router(name="start") -def role_choice_kb() -> InlineKeyboardMarkup: - return InlineKeyboardMarkup( - inline_keyboard=[ - [ - InlineKeyboardButton(text="👤 Менеджер", callback_data="role:manager"), - InlineKeyboardButton(text="🏠 Клиент", callback_data="role:client"), - ] - ] - ) +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 open_app_kb(miniapp_url: str) -> InlineKeyboardMarkup: +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=miniapp_url), - ) + text="👤 Менеджер", + web_app=WebAppInfo(url=_bust_cache(f"{miniapp_url}?role=manager")), + ), + InlineKeyboardButton( + text="🏠 Клиент", + web_app=WebAppInfo(url=_bust_cache(f"{miniapp_url}?role=client")), + ), ] ] ) @@ -39,25 +40,8 @@ def open_app_kb(miniapp_url: str) -> InlineKeyboardMarkup: @router.message(CommandStart()) async def cmd_start(message: Message, config: Config) -> None: - # TODO: проверить, есть ли пользователь в БД (Google Sheet → users). - # Если есть → сразу показывать "Открыть кабинет". - # Если нет → спрашивать роль. await message.answer( "👋 Здравствуйте, я бот-помощник от Руслана ВАСИЛЬЕВА.\n\n" "Кто вы?", - reply_markup=role_choice_kb(), + reply_markup=role_choice_kb(config.miniapp_url), ) - - -@router.callback_query(F.data.startswith("role:")) -async def on_role_chosen(callback: CallbackQuery, config: Config) -> None: - role = callback.data.split(":", 1)[1] - # TODO: сохранить роль в БД (Google Sheet → users) - - text = { - "manager": "Отлично, открываю кабинет менеджера 👇", - "client": "Спасибо! Открываю ваш кабинет 👇", - }.get(role, "Открываю кабинет 👇") - - await callback.message.edit_text(text, reply_markup=open_app_kb(config.miniapp_url)) - await callback.answer() diff --git a/miniapp/assets/app.js b/miniapp/assets/app.js index cdfe284..864186c 100644 --- a/miniapp/assets/app.js +++ b/miniapp/assets/app.js @@ -53,11 +53,16 @@ async function fetchMe() { // Заголовок Content-Type НЕ ставим — иначе браузер шлёт CORS preflight, // который Apps Script не обрабатывает. Без заголовка fetch использует // text/plain — Apps Script всё равно парсит body как JSON. + // Роль приходит в URL (?role=manager|client) — её бот подставляет в WebApp-кнопку + const urlParams = new URLSearchParams(window.location.search); + const explicitRole = urlParams.get("role"); + const res = await fetch(`${BACKEND_URL}?path=me`, { method: "POST", body: JSON.stringify({ initData: tg?.initData || "", startParam: tg?.initDataUnsafe?.start_param || null, + role: explicitRole, }), }); if (!res.ok) throw new Error("backend HTTP " + res.status); diff --git a/miniapp/index.html b/miniapp/index.html index b92e4bf..9aa18eb 100644 --- a/miniapp/index.html +++ b/miniapp/index.html @@ -12,7 +12,7 @@ - +
@@ -20,7 +20,7 @@
- - + +