Implement buy flow and migrate DTOs to schemas

Move DTO definitions from dto/ to schemas/ and update imports.
Add buy handler with FSM state management for device selection,
whitelist toggling, and duration choice. Include pricing logic,
inline keyboards, and new configuration fields.
This commit is contained in:
2026-04-08 19:34:25 +07:00
parent fc45816eba
commit 930fabc028
17 changed files with 291 additions and 50 deletions

View File

@@ -1,6 +1,10 @@
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
from aiogram.utils.keyboard import InlineKeyboardBuilder
from config import settings
from misc.utils import convert_duration_to_int
from schemas.subscriptions import SubscriptionDuration
main_menu: InlineKeyboardMarkup = InlineKeyboardBuilder(
[
[
@@ -14,6 +18,71 @@ main_menu: InlineKeyboardMarkup = InlineKeyboardBuilder(
]
).as_markup()
_return_to_menu_btn: InlineKeyboardButton = InlineKeyboardButton(
text="⬅️ Назад", callback_data="menu:main"
)
def _return_btn(
data: str = "menu:main", text: str = "⬅️ Назад"
) -> InlineKeyboardButton:
return InlineKeyboardButton(text=text, callback_data=data)
return_to_menu: InlineKeyboardMarkup = InlineKeyboardBuilder(
[[InlineKeyboardButton(text="⬅️ Назад", callback_data="menu:main")]]
[[_return_to_menu_btn]]
).as_markup()
def devices_selector(current: int = settings.min_devices, whitelists: bool = False):
builder = InlineKeyboardBuilder()
buttons = [
InlineKeyboardButton(
text=f"{'' if i == current else ''}{i}", callback_data=f"devices:{i}"
)
for i in range(settings.min_devices, settings.max_devices + 1)
]
builder.add(*buttons).adjust(6)
wl_icon = "" if whitelists else ""
builder.row(
InlineKeyboardButton(
text=f"{wl_icon}Обход Белых Списков (+100₽)",
callback_data="whitelist:toggle",
)
)
builder.row(InlineKeyboardButton(text="🟢", callback_data="confirm"))
builder.row(_return_to_menu_btn)
return builder.as_markup()
def duration_selector(
current: SubscriptionDuration = SubscriptionDuration.MONTH,
back_cb: str = "menu:main",
) -> InlineKeyboardMarkup:
builder = InlineKeyboardBuilder()
duration_int = convert_duration_to_int(current)
durations = [
(1, "1 Месяц"),
(3, "3 Месяца"),
(6, "6 Месяцев"),
(12, "1 Год"),
]
for val, label in durations:
is_selected = val == duration_int
builder.button(
text=f"{'' if is_selected else ''}{label}",
callback_data=f"duration:{val}",
)
builder.row(
InlineKeyboardButton(text="🛒 Перейти к оплате", callback_data="confirm")
)
builder.row(_return_btn(back_cb))
return builder.as_markup()