refactor: project architecture refactor, container splitting
This commit is contained in:
@@ -0,0 +1,512 @@
|
||||
import math
|
||||
from typing import List
|
||||
|
||||
from aiogram.types import InlineKeyboardMarkup
|
||||
from aiogram.utils.keyboard import InlineKeyboardBuilder, InlineKeyboardButton
|
||||
|
||||
from bot.middlewares.i18n import JsonI18n
|
||||
from config.settings import Settings
|
||||
from db.models import User
|
||||
|
||||
|
||||
def get_admin_panel_keyboard(i18n_instance, lang: str, settings: Settings) -> InlineKeyboardMarkup:
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
|
||||
# Статистика и мониторинг
|
||||
builder.button(
|
||||
text=_(key="admin_stats_and_monitoring_section"),
|
||||
callback_data="admin_section:stats_monitoring",
|
||||
)
|
||||
|
||||
# Управление пользователями
|
||||
builder.button(
|
||||
text=_(key="admin_user_management_section"), callback_data="admin_section:user_management"
|
||||
)
|
||||
|
||||
# Промокоды и маркетинг
|
||||
builder.button(
|
||||
text=_(key="admin_promo_marketing_section"), callback_data="admin_section:promo_marketing"
|
||||
)
|
||||
|
||||
# Реклама
|
||||
builder.button(text=_(key="admin_ads_section"), callback_data="admin_action:ads")
|
||||
|
||||
# Системные функции
|
||||
builder.button(
|
||||
text=_(key="admin_system_functions_section"), callback_data="admin_section:system_functions"
|
||||
)
|
||||
|
||||
builder.adjust(1)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_stats_monitoring_keyboard(i18n_instance, lang: str) -> InlineKeyboardMarkup:
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
|
||||
builder.button(text=_(key="admin_stats_button"), callback_data="admin_action:stats")
|
||||
builder.button(
|
||||
text=_(key="admin_view_payments_button"), callback_data="admin_action:view_payments"
|
||||
)
|
||||
builder.button(
|
||||
text=_(key="admin_view_logs_menu_button"), callback_data="admin_action:view_logs_menu"
|
||||
)
|
||||
|
||||
builder.button(text=_(key="back_to_admin_panel_button"), callback_data="admin_action:main")
|
||||
builder.adjust(2, 1, 1)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_user_management_keyboard(i18n_instance, lang: str) -> InlineKeyboardMarkup:
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
|
||||
builder.button(
|
||||
text=_(key="admin_users_management_button"), callback_data="admin_action:users_list:0"
|
||||
)
|
||||
builder.button(
|
||||
text=_(key="admin_users_search_button"), callback_data="admin_action:users_search_prompt"
|
||||
)
|
||||
builder.button(
|
||||
text=_(key="admin_ban_management_section"), callback_data="admin_section:ban_management"
|
||||
)
|
||||
builder.button(
|
||||
text=_(key="admin_user_ratings_button"), callback_data="admin_action:user_ratings"
|
||||
)
|
||||
|
||||
builder.button(text=_(key="back_to_admin_panel_button"), callback_data="admin_action:main")
|
||||
builder.adjust(2, 2, 1)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_ban_management_keyboard(i18n_instance, lang: str) -> InlineKeyboardMarkup:
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
|
||||
builder.button(
|
||||
text=_(key="admin_ban_user_button"), callback_data="admin_action:ban_user_prompt"
|
||||
)
|
||||
builder.button(
|
||||
text=_(key="admin_unban_user_button"), callback_data="admin_action:unban_user_prompt"
|
||||
)
|
||||
builder.button(
|
||||
text=_(key="admin_view_banned_users_button"), callback_data="admin_action:view_banned:0"
|
||||
)
|
||||
|
||||
builder.button(
|
||||
text=_(key="back_to_user_management_button"), callback_data="admin_section:user_management"
|
||||
)
|
||||
builder.adjust(2, 1, 1)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_promo_marketing_keyboard(i18n_instance, lang: str) -> InlineKeyboardMarkup:
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
|
||||
builder.button(
|
||||
text=_(key="admin_create_promo_button"), callback_data="admin_action:create_promo"
|
||||
)
|
||||
builder.button(
|
||||
text=_(key="admin_create_bulk_promo_button"), callback_data="admin_action:create_bulk_promo"
|
||||
)
|
||||
builder.button(
|
||||
text=_(key="admin_promo_management_button"), callback_data="admin_action:promo_management"
|
||||
)
|
||||
|
||||
builder.button(text=_(key="back_to_admin_panel_button"), callback_data="admin_action:main")
|
||||
builder.adjust(2, 1, 1)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_system_functions_keyboard(i18n_instance, lang: str) -> InlineKeyboardMarkup:
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
|
||||
builder.button(text=_(key="admin_broadcast_button"), callback_data="admin_action:broadcast")
|
||||
builder.button(text=_(key="admin_sync_panel_button"), callback_data="admin_action:sync_panel")
|
||||
builder.button(
|
||||
text=_(key="admin_queue_status_button"), callback_data="admin_action:queue_status"
|
||||
)
|
||||
|
||||
builder.button(text=_(key="back_to_admin_panel_button"), callback_data="admin_action:main")
|
||||
builder.adjust(2, 1, 1)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_ads_menu_keyboard(i18n_instance, lang: str) -> InlineKeyboardMarkup:
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.button(text=_(key="admin_ads_create_button"), callback_data="admin_action:ads_create")
|
||||
builder.button(text=_(key="back_to_admin_panel_button"), callback_data="admin_action:main")
|
||||
builder.adjust(1, 1)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_ads_list_keyboard(
|
||||
i18n_instance,
|
||||
lang: str,
|
||||
campaigns: list,
|
||||
current_page: int,
|
||||
total_pages: int,
|
||||
) -> InlineKeyboardMarkup:
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
|
||||
for c in campaigns:
|
||||
title = f"{c.source}"
|
||||
builder.button(
|
||||
text=title,
|
||||
callback_data=f"admin_ads:card:{c.ad_campaign_id}:{current_page}",
|
||||
)
|
||||
|
||||
# Pagination row (only when needed)
|
||||
if total_pages > 1:
|
||||
row = []
|
||||
if current_page > 0:
|
||||
row.append(
|
||||
InlineKeyboardButton(
|
||||
text="⬅️ " + _("prev_page_button"),
|
||||
callback_data=f"admin_ads:page:{current_page - 1}",
|
||||
)
|
||||
)
|
||||
row.append(
|
||||
InlineKeyboardButton(
|
||||
text=f"{current_page + 1}/{total_pages}",
|
||||
callback_data="ads_page_display",
|
||||
)
|
||||
)
|
||||
if current_page < total_pages - 1:
|
||||
row.append(
|
||||
InlineKeyboardButton(
|
||||
text=_("next_page_button") + " ➡️",
|
||||
callback_data=f"admin_ads:page:{current_page + 1}",
|
||||
)
|
||||
)
|
||||
if row:
|
||||
builder.row(*row)
|
||||
|
||||
builder.button(text=_(key="admin_ads_create_button"), callback_data="admin_action:ads_create")
|
||||
builder.button(text=_(key="back_to_admin_panel_button"), callback_data="admin_action:main")
|
||||
builder.adjust(1)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_ad_card_keyboard(
|
||||
i18n_instance, lang: str, campaign_id: int, back_page: int
|
||||
) -> InlineKeyboardMarkup:
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
# Dangerous action: Delete campaign
|
||||
builder.button(
|
||||
text=_(key="admin_ads_delete_button"),
|
||||
callback_data=f"admin_ads:delete:{campaign_id}:{back_page}",
|
||||
)
|
||||
builder.button(
|
||||
text=_(key="back_to_ads_list_button"), callback_data=f"admin_ads:page:{back_page}"
|
||||
)
|
||||
builder.button(text=_(key="back_to_admin_panel_button"), callback_data="admin_action:main")
|
||||
builder.adjust(1)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_logs_menu_keyboard(i18n_instance, lang: str) -> InlineKeyboardMarkup:
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.button(text=_(key="admin_view_all_logs_button"), callback_data="admin_logs:view_all:0")
|
||||
builder.button(
|
||||
text=_(key="admin_view_user_logs_prompt_button"), callback_data="admin_logs:prompt_user"
|
||||
)
|
||||
builder.button(
|
||||
text=_(key="admin_export_logs_csv_button"), callback_data="admin_logs:export_csv"
|
||||
)
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=_(key="back_to_admin_panel_button"), callback_data="admin_action:main"
|
||||
)
|
||||
)
|
||||
builder.adjust(2, 1, 1)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_logs_pagination_keyboard(
|
||||
current_page: int,
|
||||
total_pages: int,
|
||||
base_callback_data: str,
|
||||
i18n_instance,
|
||||
lang: str,
|
||||
back_to_logs_menu: bool = False,
|
||||
) -> InlineKeyboardMarkup:
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
row_buttons = []
|
||||
if current_page > 0:
|
||||
row_buttons.append(
|
||||
InlineKeyboardButton(
|
||||
text="⬅️ " + _("prev_page_button"),
|
||||
callback_data=f"{base_callback_data}:{current_page - 1}",
|
||||
)
|
||||
)
|
||||
if current_page < total_pages - 1:
|
||||
row_buttons.append(
|
||||
InlineKeyboardButton(
|
||||
text=_("next_page_button") + " ➡️",
|
||||
callback_data=f"{base_callback_data}:{current_page + 1}",
|
||||
)
|
||||
)
|
||||
|
||||
if row_buttons:
|
||||
builder.row(*row_buttons)
|
||||
|
||||
if back_to_logs_menu:
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=_(key="admin_logs_menu_title"), callback_data="admin_action:view_logs_menu"
|
||||
)
|
||||
)
|
||||
else:
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=_(key="back_to_admin_panel_button"), callback_data="admin_action:main"
|
||||
)
|
||||
)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_banned_users_keyboard(
|
||||
banned_users: List[User],
|
||||
current_page: int,
|
||||
total_banned: int,
|
||||
i18n_instance: JsonI18n,
|
||||
lang: str,
|
||||
settings: Settings,
|
||||
) -> InlineKeyboardMarkup:
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
page_size = settings.LOGS_PAGE_SIZE
|
||||
|
||||
if not banned_users and total_banned == 0:
|
||||
pass
|
||||
|
||||
for user_row in banned_users:
|
||||
user_display_parts = []
|
||||
if user_row.first_name:
|
||||
user_display_parts.append(user_row.first_name)
|
||||
if user_row.username:
|
||||
user_display_parts.append(f"(@{user_row.username})")
|
||||
elif user_row.email:
|
||||
user_display_parts.append(f"({user_row.email})")
|
||||
if not user_display_parts:
|
||||
user_display_parts.append(f"ID: {user_row.user_id}")
|
||||
|
||||
user_display = " ".join(user_display_parts).strip()
|
||||
|
||||
button_text = _(
|
||||
"admin_banned_user_button_text", user_display=user_display, user_id=user_row.user_id
|
||||
)
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=button_text, callback_data=f"admin_user_card:{user_row.user_id}:{current_page}"
|
||||
)
|
||||
)
|
||||
|
||||
if total_banned > page_size:
|
||||
total_pages = math.ceil(total_banned / page_size)
|
||||
pagination_buttons = []
|
||||
if current_page > 0:
|
||||
pagination_buttons.append(
|
||||
InlineKeyboardButton(
|
||||
text=_("prev_page_button"),
|
||||
callback_data=f"admin_action:view_banned:{current_page - 1}",
|
||||
)
|
||||
)
|
||||
pagination_buttons.append(
|
||||
InlineKeyboardButton(
|
||||
text=f"{current_page + 1}/{total_pages}", callback_data="stub_page_display"
|
||||
)
|
||||
)
|
||||
if current_page < total_pages - 1:
|
||||
pagination_buttons.append(
|
||||
InlineKeyboardButton(
|
||||
text=_("next_page_button"),
|
||||
callback_data=f"admin_action:view_banned:{current_page + 1}",
|
||||
)
|
||||
)
|
||||
if pagination_buttons:
|
||||
builder.row(*pagination_buttons)
|
||||
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=_("back_to_admin_panel_button"), callback_data="admin_action:main"
|
||||
)
|
||||
)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_users_list_keyboard(
|
||||
users: List[User],
|
||||
current_page: int,
|
||||
total_users: int,
|
||||
i18n_instance,
|
||||
lang: str,
|
||||
page_size: int = 15,
|
||||
) -> InlineKeyboardMarkup:
|
||||
"""Generate keyboard for paginated user list"""
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
|
||||
# Add user buttons
|
||||
for user in users:
|
||||
user_display_parts = []
|
||||
if user.username:
|
||||
user_display_parts.append(f"@{user.username}")
|
||||
elif user.email:
|
||||
user_display_parts.append(user.email)
|
||||
user_display_parts.append(f"ID: {user.user_id}")
|
||||
if user.first_name:
|
||||
user_display_parts.append(f"- {user.first_name}")
|
||||
|
||||
button_text = " ".join(user_display_parts)
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=button_text,
|
||||
callback_data=f"admin_user_card_from_list:{user.user_id}:{current_page}",
|
||||
)
|
||||
)
|
||||
|
||||
# Pagination buttons
|
||||
if total_users > page_size:
|
||||
total_pages = math.ceil(total_users / page_size)
|
||||
pagination_buttons = []
|
||||
if current_page > 0:
|
||||
pagination_buttons.append(
|
||||
InlineKeyboardButton(
|
||||
text=_("prev_page_button"),
|
||||
callback_data=f"admin_action:users_list:{current_page - 1}",
|
||||
)
|
||||
)
|
||||
pagination_buttons.append(
|
||||
InlineKeyboardButton(
|
||||
text=f"{current_page + 1}/{total_pages}", callback_data="stub_page_display"
|
||||
)
|
||||
)
|
||||
if current_page < total_pages - 1:
|
||||
pagination_buttons.append(
|
||||
InlineKeyboardButton(
|
||||
text=_("next_page_button"),
|
||||
callback_data=f"admin_action:users_list:{current_page + 1}",
|
||||
)
|
||||
)
|
||||
if pagination_buttons:
|
||||
builder.row(*pagination_buttons)
|
||||
|
||||
# Back button
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=_("back_to_user_management_button"), callback_data="admin_section:user_management"
|
||||
)
|
||||
)
|
||||
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_user_card_keyboard(
|
||||
user_id: int, is_banned: bool, i18n_instance, lang: str, banned_list_page: int = 0
|
||||
) -> InlineKeyboardMarkup:
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
if is_banned:
|
||||
builder.button(
|
||||
text=_(key="user_card_unban_button"),
|
||||
callback_data=f"admin_unban_confirm:{user_id}:{banned_list_page}",
|
||||
)
|
||||
else:
|
||||
builder.button(
|
||||
text=_(key="user_card_ban_button"),
|
||||
callback_data=f"admin_ban_confirm:{user_id}:{banned_list_page}",
|
||||
)
|
||||
builder.button(text=_(key="user_card_open_profile_button"), url=f"tg://user?id={user_id}")
|
||||
builder.button(
|
||||
text=_(key="user_card_back_to_banned_list_button"),
|
||||
callback_data=f"admin_action:view_banned:{banned_list_page}",
|
||||
)
|
||||
builder.button(text=_(key="back_to_admin_panel_button"), callback_data="admin_action:main")
|
||||
builder.adjust(1)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_confirmation_keyboard(
|
||||
yes_callback_data: str, no_callback_data: str, i18n_instance, lang: str
|
||||
) -> InlineKeyboardMarkup:
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.button(text=_(key="yes_button"), callback_data=yes_callback_data)
|
||||
builder.button(text=_(key="no_button"), callback_data=no_callback_data)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_broadcast_confirmation_keyboard(
|
||||
lang: str, i18n_instance, target: str = "all"
|
||||
) -> InlineKeyboardMarkup:
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
|
||||
# Row: target selection (all / active / inactive)
|
||||
target_all_label = _(key="broadcast_target_all_button")
|
||||
target_active_label = _(key="broadcast_target_active_button")
|
||||
target_inactive_label = _(key="broadcast_target_inactive_button")
|
||||
|
||||
# Highlight current selection with a prefix
|
||||
def mark_selected(label: str, is_selected: bool) -> str:
|
||||
return ("• " + label) if is_selected else label
|
||||
|
||||
builder.button(
|
||||
text=mark_selected(target_all_label, target == "all"),
|
||||
callback_data="broadcast_target:all",
|
||||
)
|
||||
builder.button(
|
||||
text=mark_selected(target_active_label, target == "active"),
|
||||
callback_data="broadcast_target:active",
|
||||
)
|
||||
builder.button(
|
||||
text=mark_selected(target_inactive_label, target == "inactive"),
|
||||
callback_data="broadcast_target:inactive",
|
||||
)
|
||||
builder.adjust(3)
|
||||
|
||||
# Row: confirmation
|
||||
builder.button(
|
||||
text=_(key="confirm_broadcast_send_button"), callback_data="broadcast_final_action:send"
|
||||
)
|
||||
builder.button(
|
||||
text=_(key="cancel_broadcast_button"), callback_data="broadcast_final_action:cancel"
|
||||
)
|
||||
builder.adjust(2)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_back_to_admin_panel_keyboard(lang: str, i18n_instance) -> InlineKeyboardMarkup:
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.button(text=_(key="back_to_admin_panel_button"), callback_data="admin_action:main")
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_back_to_stats_monitoring_keyboard(lang: str, i18n_instance) -> InlineKeyboardMarkup:
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.button(
|
||||
text=_(key="back_to_stats_monitoring_button"),
|
||||
callback_data="admin_section:stats_monitoring",
|
||||
)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_back_to_user_management_keyboard(lang: str, i18n_instance) -> InlineKeyboardMarkup:
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.button(
|
||||
text=_(key="back_to_user_management_button"), callback_data="admin_section:user_management"
|
||||
)
|
||||
return builder.as_markup()
|
||||
@@ -0,0 +1,801 @@
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
|
||||
from aiogram.types import InlineKeyboardMarkup, WebAppInfo
|
||||
from aiogram.utils.keyboard import InlineKeyboardBuilder, InlineKeyboardButton
|
||||
|
||||
from config.settings import Settings
|
||||
|
||||
|
||||
def get_main_menu_inline_keyboard(
|
||||
lang: str, i18n_instance, settings: Settings, show_trial_button: bool = False
|
||||
) -> InlineKeyboardMarkup:
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
|
||||
if settings.SUBSCRIPTION_MINI_APP_URL:
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=_(key="menu_personal_account_button"),
|
||||
web_app=WebAppInfo(url=settings.SUBSCRIPTION_MINI_APP_URL),
|
||||
)
|
||||
)
|
||||
else:
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=_(key="menu_personal_account_button"),
|
||||
callback_data="main_action:my_subscription",
|
||||
)
|
||||
)
|
||||
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=_(key="menu_bot_interface_button"), callback_data="main_action:bot_interface"
|
||||
)
|
||||
)
|
||||
|
||||
if settings.SUPPORT_LINK:
|
||||
builder.row(
|
||||
InlineKeyboardButton(text=_(key="menu_support_button"), url=settings.SUPPORT_LINK)
|
||||
)
|
||||
|
||||
user_agreement_url = settings.USER_AGREEMENT_URL or settings.TERMS_OF_SERVICE_URL
|
||||
if settings.PRIVACY_POLICY_URL or user_agreement_url:
|
||||
builder.row(
|
||||
InlineKeyboardButton(text=_(key="menu_info_button"), callback_data="main_action:info")
|
||||
)
|
||||
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_bot_interface_inline_keyboard(
|
||||
lang: str, i18n_instance, settings: Settings, show_trial_button: bool = False
|
||||
) -> InlineKeyboardMarkup:
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
|
||||
if show_trial_button and settings.TRIAL_ENABLED:
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=_(key="menu_activate_trial_button"), callback_data="main_action:request_trial"
|
||||
)
|
||||
)
|
||||
|
||||
if settings.SUBSCRIPTION_MINI_APP_URL:
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=_(key="menu_personal_account_button"),
|
||||
web_app=WebAppInfo(url=settings.SUBSCRIPTION_MINI_APP_URL),
|
||||
)
|
||||
)
|
||||
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=_(key="menu_subscribe_inline"), callback_data="main_action:bot_subscribe"
|
||||
)
|
||||
)
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=_(key="menu_my_subscription_inline"),
|
||||
callback_data="main_action:bot_my_subscription",
|
||||
)
|
||||
)
|
||||
|
||||
referral_button = InlineKeyboardButton(
|
||||
text=_(key="menu_referral_inline"), callback_data="main_action:bot_referral"
|
||||
)
|
||||
promo_button = InlineKeyboardButton(
|
||||
text=_(key="menu_apply_promo_button"), callback_data="main_action:bot_apply_promo"
|
||||
)
|
||||
builder.row(referral_button)
|
||||
builder.row(promo_button)
|
||||
|
||||
language_button = InlineKeyboardButton(
|
||||
text=_(key="menu_language_settings_inline"), callback_data="main_action:bot_language"
|
||||
)
|
||||
status_button_list = []
|
||||
if settings.SERVER_STATUS_URL:
|
||||
status_button_list.append(
|
||||
InlineKeyboardButton(
|
||||
text=_(key="menu_server_status_button"), url=settings.SERVER_STATUS_URL
|
||||
)
|
||||
)
|
||||
|
||||
if status_button_list:
|
||||
builder.row(language_button, *status_button_list)
|
||||
else:
|
||||
builder.row(language_button)
|
||||
|
||||
if settings.SUPPORT_LINK:
|
||||
builder.row(
|
||||
InlineKeyboardButton(text=_(key="menu_support_button"), url=settings.SUPPORT_LINK)
|
||||
)
|
||||
|
||||
user_agreement_url = settings.USER_AGREEMENT_URL or settings.TERMS_OF_SERVICE_URL
|
||||
if settings.PRIVACY_POLICY_URL or user_agreement_url:
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=_(key="menu_info_button"), callback_data="main_action:bot_info"
|
||||
)
|
||||
)
|
||||
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=_(key="back_to_main_menu_button"), callback_data="main_action:back_to_main"
|
||||
)
|
||||
)
|
||||
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_information_links_keyboard(
|
||||
lang: str,
|
||||
i18n_instance,
|
||||
privacy_policy_url: Optional[str],
|
||||
user_agreement_url: Optional[str],
|
||||
back_callback: str = "main_action:back_to_main",
|
||||
) -> InlineKeyboardMarkup:
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
if privacy_policy_url:
|
||||
builder.row(
|
||||
InlineKeyboardButton(text=_(key="privacy_policy_button"), url=privacy_policy_url)
|
||||
)
|
||||
if user_agreement_url:
|
||||
builder.row(
|
||||
InlineKeyboardButton(text=_(key="user_agreement_button"), url=user_agreement_url)
|
||||
)
|
||||
builder.row(
|
||||
InlineKeyboardButton(text=_(key="back_to_main_menu_button"), callback_data=back_callback)
|
||||
)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_language_selection_keyboard(
|
||||
i18n_instance, current_lang: str, back_callback: str = "main_action:back_to_main"
|
||||
) -> InlineKeyboardMarkup:
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(current_lang, key, **kwargs)
|
||||
callback_suffix = ":bot" if back_callback == "main_action:bot_interface" else ""
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.button(
|
||||
text=f"🇬🇧 English {'✅' if current_lang == 'en' else ''}",
|
||||
callback_data=f"set_lang_en{callback_suffix}",
|
||||
)
|
||||
builder.button(
|
||||
text=f"🇷🇺 Русский {'✅' if current_lang == 'ru' else ''}",
|
||||
callback_data=f"set_lang_ru{callback_suffix}",
|
||||
)
|
||||
builder.button(text=_(key="back_to_main_menu_button"), callback_data=back_callback)
|
||||
builder.adjust(1)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_trial_confirmation_keyboard(lang: str, i18n_instance) -> InlineKeyboardMarkup:
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.button(
|
||||
text=_(key="trial_confirm_activate_button"), callback_data="trial_action:confirm_activate"
|
||||
)
|
||||
builder.button(text=_(key="cancel_button"), callback_data="main_action:back_to_main")
|
||||
builder.adjust(1)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_subscription_options_keyboard(
|
||||
subscription_options: Dict[float, Optional[float]],
|
||||
currency_symbol_val: str,
|
||||
lang: str,
|
||||
i18n_instance,
|
||||
traffic_mode: bool = False,
|
||||
back_callback: str = "main_action:back_to_main",
|
||||
) -> InlineKeyboardMarkup:
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
|
||||
def _format_gb(val: float) -> str:
|
||||
return str(int(val)) if float(val).is_integer() else f"{val:g}"
|
||||
|
||||
if subscription_options:
|
||||
for months, price in subscription_options.items():
|
||||
if price is not None:
|
||||
if traffic_mode:
|
||||
button_text = _(
|
||||
"buy_traffic_package_button",
|
||||
traffic_gb=_format_gb(months),
|
||||
price=price,
|
||||
currency_symbol=currency_symbol_val,
|
||||
)
|
||||
callback_data = f"subscribe_period:{_format_gb(months)}"
|
||||
else:
|
||||
button_text = _(
|
||||
"subscribe_for_months_button",
|
||||
months=months,
|
||||
price=price,
|
||||
currency_symbol=currency_symbol_val,
|
||||
)
|
||||
callback_data = f"subscribe_period:{months}"
|
||||
builder.button(text=button_text, callback_data=callback_data)
|
||||
builder.adjust(1)
|
||||
builder.row(
|
||||
InlineKeyboardButton(text=_(key="back_to_main_menu_button"), callback_data=back_callback)
|
||||
)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_tariff_catalog_keyboard(
|
||||
tariffs: List[Any], lang: str, i18n_instance
|
||||
) -> InlineKeyboardMarkup:
|
||||
builder = InlineKeyboardBuilder()
|
||||
for tariff in tariffs:
|
||||
label = tariff.name(lang)
|
||||
if tariff.billing_model == "period":
|
||||
min_price = tariff.min_period_price_rub()
|
||||
if min_price is not None:
|
||||
label = f"{label} от {min_price:g}"
|
||||
else:
|
||||
package = tariff.min_traffic_package_rub()
|
||||
if package:
|
||||
label = f"{label} от {package.price:g} / {package.gb:g} GB"
|
||||
builder.row(InlineKeyboardButton(text=label, callback_data=f"tariff:select:{tariff.key}"))
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=_(key="back_to_main_menu_button"), callback_data="main_action:back_to_main"
|
||||
)
|
||||
)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_tariff_periods_keyboard(
|
||||
tariff: Any, lang: str, i18n_instance, settings: Settings
|
||||
) -> InlineKeyboardMarkup:
|
||||
builder = InlineKeyboardBuilder()
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
for months in tariff.enabled_periods:
|
||||
rub_price = tariff.period_price(months, "rub")
|
||||
if rub_price and rub_price > 0:
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=_(
|
||||
"subscribe_for_months_button",
|
||||
months=months,
|
||||
price=rub_price,
|
||||
currency_symbol=settings.DEFAULT_CURRENCY_SYMBOL,
|
||||
),
|
||||
callback_data=f"tariff:period:{tariff.key}:{months}",
|
||||
)
|
||||
)
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=_(key="back_to_main_menu_button"), callback_data="main_action:subscribe"
|
||||
)
|
||||
)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_tariff_packages_keyboard(
|
||||
tariff: Any,
|
||||
packages: List[Any],
|
||||
lang: str,
|
||||
i18n_instance,
|
||||
back_callback: str = "main_action:subscribe",
|
||||
) -> InlineKeyboardMarkup:
|
||||
builder = InlineKeyboardBuilder()
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
for package in packages:
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=_(
|
||||
"buy_traffic_package_button",
|
||||
traffic_gb=f"{package.gb:g}",
|
||||
price=package.price,
|
||||
currency_symbol="RUB",
|
||||
),
|
||||
callback_data=f"tariff:package:{tariff.key}:{package.gb:g}",
|
||||
)
|
||||
)
|
||||
builder.row(
|
||||
InlineKeyboardButton(text=_(key="back_to_main_menu_button"), callback_data=back_callback)
|
||||
)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_hwid_device_packages_keyboard(
|
||||
tariff: Any,
|
||||
packages: List[Any],
|
||||
lang: str,
|
||||
i18n_instance,
|
||||
settings: Settings,
|
||||
back_callback: str = "main_action:my_subscription",
|
||||
) -> InlineKeyboardMarkup:
|
||||
builder = InlineKeyboardBuilder()
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
for package in packages:
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=_(
|
||||
"buy_hwid_devices_button",
|
||||
count=package.count,
|
||||
price=package.price,
|
||||
currency_symbol=settings.DEFAULT_CURRENCY_SYMBOL,
|
||||
),
|
||||
callback_data=f"hwid_devices:package:{tariff.key}:{package.count}",
|
||||
)
|
||||
)
|
||||
builder.row(
|
||||
InlineKeyboardButton(text=_(key="back_to_main_menu_button"), callback_data=back_callback)
|
||||
)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_payment_method_keyboard(
|
||||
months: int,
|
||||
price: float,
|
||||
stars_price: Optional[int],
|
||||
currency_symbol_val: str,
|
||||
lang: str,
|
||||
i18n_instance,
|
||||
settings: Settings,
|
||||
sale_mode: str = "subscription",
|
||||
) -> InlineKeyboardMarkup:
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
|
||||
def _format_value(val: float) -> str:
|
||||
return str(int(val)) if float(val).is_integer() else f"{val:g}"
|
||||
|
||||
value_str = _format_value(months)
|
||||
mode_suffix = f":{sale_mode}"
|
||||
import logging as _kbd_logging
|
||||
|
||||
_kbd_logging.info(
|
||||
"payment_method_keyboard build: order=%s | platega_enabled=%s sbp=%s crypto=%s",
|
||||
settings.payment_methods_order,
|
||||
settings.PLATEGA_ENABLED,
|
||||
settings.PLATEGA_SBP_ENABLED,
|
||||
settings.PLATEGA_CRYPTO_ENABLED,
|
||||
)
|
||||
for method in settings.payment_methods_order:
|
||||
if method == "severpay" and getattr(settings, "SEVERPAY_ENABLED", False):
|
||||
builder.button(
|
||||
text=_("pay_with_severpay_button"),
|
||||
callback_data=f"pay_severpay:{value_str}:{price}{mode_suffix}",
|
||||
)
|
||||
elif method == "freekassa" and settings.FREEKASSA_ENABLED:
|
||||
builder.button(
|
||||
text=_("pay_with_sbp_button"),
|
||||
callback_data=f"pay_fk:{value_str}:{price}{mode_suffix}",
|
||||
)
|
||||
elif method == "platega_sbp" and settings.PLATEGA_ENABLED and settings.PLATEGA_SBP_ENABLED:
|
||||
builder.button(
|
||||
text=_("pay_with_platega_sbp_button"),
|
||||
callback_data=f"pay_platega_sbp:{value_str}:{price}{mode_suffix}",
|
||||
)
|
||||
elif (
|
||||
method == "platega_crypto"
|
||||
and settings.PLATEGA_ENABLED
|
||||
and settings.PLATEGA_CRYPTO_ENABLED
|
||||
):
|
||||
builder.button(
|
||||
text=_("pay_with_platega_crypto_button"),
|
||||
callback_data=f"pay_platega_crypto:{value_str}:{price}{mode_suffix}",
|
||||
)
|
||||
elif method == "yookassa" and settings.YOOKASSA_ENABLED:
|
||||
builder.button(
|
||||
text=_("pay_with_yookassa_button"),
|
||||
callback_data=f"pay_yk:{value_str}:{price}{mode_suffix}",
|
||||
)
|
||||
elif method == "stars" and settings.STARS_ENABLED and stars_price is not None:
|
||||
builder.button(
|
||||
text=_("pay_with_stars_button"),
|
||||
callback_data=f"pay_stars:{value_str}:{stars_price}{mode_suffix}",
|
||||
)
|
||||
elif method == "cryptopay" and settings.CRYPTOPAY_ENABLED:
|
||||
builder.button(
|
||||
text=_("pay_with_cryptopay_button"),
|
||||
callback_data=f"pay_crypto:{value_str}:{price}{mode_suffix}",
|
||||
)
|
||||
builder.button(text=_(key="cancel_button"), callback_data="main_action:subscribe")
|
||||
builder.adjust(1)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_payment_url_keyboard(
|
||||
payment_url: str,
|
||||
lang: str,
|
||||
i18n_instance,
|
||||
back_callback: Optional[str] = None,
|
||||
back_text_key: str = "back_to_main_menu_button",
|
||||
) -> InlineKeyboardMarkup:
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.button(text=_(key="pay_button"), url=payment_url)
|
||||
if back_callback:
|
||||
builder.button(text=_(key=back_text_key), callback_data=back_callback)
|
||||
else:
|
||||
builder.button(
|
||||
text=_(key="back_to_main_menu_button"), callback_data="main_action:back_to_main"
|
||||
)
|
||||
builder.adjust(1)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_yk_autopay_choice_keyboard(
|
||||
months: int,
|
||||
price: float,
|
||||
lang: str,
|
||||
i18n_instance,
|
||||
has_saved_cards: bool = True,
|
||||
sale_mode: str = "subscription",
|
||||
) -> InlineKeyboardMarkup:
|
||||
"""Keyboard for choosing between saved card charge or new card payment when auto-renew is enabled.""" # noqa: E501
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
price_str = str(price)
|
||||
|
||||
def _format_value(val: float) -> str:
|
||||
return str(int(val)) if float(val).is_integer() else f"{val:g}"
|
||||
|
||||
value_str = _format_value(months)
|
||||
suffix = f":{sale_mode}"
|
||||
if has_saved_cards:
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=_(key="yookassa_autopay_pay_saved_card_button"),
|
||||
callback_data=f"pay_yk_saved_list:{value_str}:{price_str}{suffix}",
|
||||
)
|
||||
)
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=_(key="yookassa_autopay_pay_new_card_button"),
|
||||
callback_data=f"pay_yk_new:{value_str}:{price_str}{suffix}",
|
||||
)
|
||||
)
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=_(key="back_to_payment_methods_button"),
|
||||
callback_data=f"subscribe_period:{value_str}",
|
||||
)
|
||||
)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_yk_saved_cards_keyboard(
|
||||
cards: List[Tuple[str, str]],
|
||||
months: int,
|
||||
price: float,
|
||||
lang: str,
|
||||
i18n_instance,
|
||||
page: int = 0,
|
||||
sale_mode: str = "subscription",
|
||||
) -> InlineKeyboardMarkup:
|
||||
"""Paginated keyboard for selecting a saved YooKassa card."""
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
per_page = 5
|
||||
total = len(cards)
|
||||
start = page * per_page
|
||||
end = min(total, start + per_page)
|
||||
price_str = str(price)
|
||||
|
||||
def _format_value(val: float) -> str:
|
||||
return str(int(val)) if float(val).is_integer() else f"{val:g}"
|
||||
|
||||
value_str = _format_value(months)
|
||||
suffix = f":{sale_mode}"
|
||||
|
||||
for method_id, title in cards[start:end]:
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=title,
|
||||
callback_data=f"pay_yk_use_saved:{value_str}:{price_str}:{method_id}{suffix}",
|
||||
)
|
||||
)
|
||||
|
||||
nav_buttons: List[InlineKeyboardButton] = []
|
||||
if start > 0:
|
||||
nav_buttons.append(
|
||||
InlineKeyboardButton(
|
||||
text="⬅️",
|
||||
callback_data=f"pay_yk_saved_list:{value_str}:{price_str}:{page - 1}{suffix}",
|
||||
)
|
||||
)
|
||||
if end < total:
|
||||
nav_buttons.append(
|
||||
InlineKeyboardButton(
|
||||
text="➡️",
|
||||
callback_data=f"pay_yk_saved_list:{value_str}:{price_str}:{page + 1}{suffix}",
|
||||
)
|
||||
)
|
||||
if nav_buttons:
|
||||
builder.row(*nav_buttons)
|
||||
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=_(key="yookassa_autopay_pay_new_card_button"),
|
||||
callback_data=f"pay_yk_new:{value_str}:{price_str}{suffix}",
|
||||
)
|
||||
)
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=_(key="back_to_autopay_method_choice_button"),
|
||||
callback_data=f"pay_yk:{value_str}:{price_str}{suffix}",
|
||||
)
|
||||
)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_referral_link_keyboard(
|
||||
lang: str, i18n_instance, back_callback: str = "main_action:back_to_main"
|
||||
) -> InlineKeyboardMarkup:
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.button(
|
||||
text=_(key="referral_share_message_button"), callback_data="referral_action:share_message"
|
||||
)
|
||||
builder.button(text=_(key="back_to_main_menu_button"), callback_data=back_callback)
|
||||
builder.adjust(1)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_back_to_main_menu_markup(
|
||||
lang: str, i18n_instance, callback_data: Optional[str] = None
|
||||
) -> InlineKeyboardMarkup:
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
if callback_data:
|
||||
builder.button(text=_(key="back_to_main_menu_button"), callback_data=callback_data)
|
||||
else:
|
||||
builder.button(
|
||||
text=_(key="back_to_main_menu_button"), callback_data="main_action:back_to_main"
|
||||
)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_subscribe_only_markup(lang: str, i18n_instance) -> InlineKeyboardMarkup:
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.button(text=_(key="menu_subscribe_inline"), callback_data="main_action:subscribe")
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_user_banned_keyboard(
|
||||
support_link: Optional[str], lang: str, i18n_instance
|
||||
) -> Optional[InlineKeyboardMarkup]:
|
||||
if not support_link:
|
||||
return None
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.button(text=_(key="menu_support_button"), url=support_link)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_channel_subscription_keyboard(
|
||||
lang: str, i18n_instance, channel_link: Optional[str], include_check_button: bool = True
|
||||
) -> Optional[InlineKeyboardMarkup]:
|
||||
"""
|
||||
Return keyboard with buttons to open the required channel and trigger a subscription re-check.
|
||||
"""
|
||||
if i18n_instance is None:
|
||||
return None
|
||||
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
|
||||
has_buttons = False
|
||||
|
||||
if channel_link:
|
||||
builder.button(
|
||||
text=_(key="channel_subscription_join_button"),
|
||||
url=channel_link,
|
||||
)
|
||||
has_buttons = True
|
||||
|
||||
if include_check_button:
|
||||
builder.button(
|
||||
text=_(key="channel_subscription_verify_button"),
|
||||
callback_data="channel_subscription:verify",
|
||||
)
|
||||
has_buttons = True
|
||||
|
||||
if not has_buttons:
|
||||
return None
|
||||
|
||||
builder.adjust(1)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_connect_and_main_keyboard(
|
||||
lang: str,
|
||||
i18n_instance,
|
||||
settings: Settings,
|
||||
config_link: Optional[str],
|
||||
connect_button_url: Optional[str] = None,
|
||||
preserve_message: bool = False,
|
||||
) -> InlineKeyboardMarkup:
|
||||
"""Keyboard with a connect button and a back to main menu button."""
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
button_target = connect_button_url or config_link
|
||||
|
||||
if button_target:
|
||||
builder.row(InlineKeyboardButton(text=_("connect_button"), url=button_target))
|
||||
elif settings.SUBSCRIPTION_MINI_APP_URL:
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=_("connect_button"),
|
||||
web_app=WebAppInfo(url=settings.SUBSCRIPTION_MINI_APP_URL),
|
||||
)
|
||||
)
|
||||
else:
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=_("connect_button"),
|
||||
callback_data="main_action:my_subscription",
|
||||
)
|
||||
)
|
||||
|
||||
back_callback = (
|
||||
"main_action:back_to_main_keep" if preserve_message else "main_action:back_to_main"
|
||||
)
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=_("back_to_main_menu_button"),
|
||||
callback_data=back_callback,
|
||||
)
|
||||
)
|
||||
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_payment_methods_manage_keyboard(
|
||||
lang: str, i18n_instance, has_card: bool
|
||||
) -> InlineKeyboardMarkup:
|
||||
"""Deprecated in favor of get_payment_methods_list_keyboard. Kept for backward compatibility."""
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.row(
|
||||
InlineKeyboardButton(text=_(key="payment_method_bind_button"), callback_data="pm:bind")
|
||||
)
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=_(key="back_to_main_menu_button"), callback_data="main_action:back_to_main"
|
||||
)
|
||||
)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_payment_methods_list_keyboard(
|
||||
cards: List[Tuple[str, str]],
|
||||
page: int,
|
||||
lang: str,
|
||||
i18n_instance,
|
||||
) -> InlineKeyboardMarkup:
|
||||
"""
|
||||
Build a paginated list of saved payment methods.
|
||||
cards: list of tuples (payment_method_id, display_title)
|
||||
page: 0-based page index
|
||||
"""
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
per_page = 5
|
||||
total = len(cards)
|
||||
start = page * per_page
|
||||
end = start + per_page
|
||||
for pm_id, title in cards[start:end]:
|
||||
builder.row(InlineKeyboardButton(text=title, callback_data=f"pm:view:{pm_id}"))
|
||||
|
||||
# Pagination controls if needed
|
||||
nav_buttons: List[InlineKeyboardButton] = []
|
||||
if start > 0:
|
||||
nav_buttons.append(InlineKeyboardButton(text="⬅️", callback_data=f"pm:list:{page - 1}"))
|
||||
if end < total:
|
||||
nav_buttons.append(InlineKeyboardButton(text="➡️", callback_data=f"pm:list:{page + 1}"))
|
||||
if nav_buttons:
|
||||
builder.row(*nav_buttons)
|
||||
|
||||
# Bind new card and back
|
||||
builder.row(
|
||||
InlineKeyboardButton(text=_(key="payment_method_bind_button"), callback_data="pm:bind")
|
||||
)
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=_(key="back_to_main_menu_button"), callback_data="main_action:back_to_main"
|
||||
)
|
||||
)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_payment_method_delete_confirm_keyboard(
|
||||
pm_id: str, lang: str, i18n_instance
|
||||
) -> InlineKeyboardMarkup:
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.row(
|
||||
InlineKeyboardButton(text=_(key="yes_button"), callback_data=f"pm:delete:{pm_id}"),
|
||||
InlineKeyboardButton(text=_(key="cancel_button"), callback_data=f"pm:view:{pm_id}"),
|
||||
)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_payment_method_details_keyboard(
|
||||
pm_id: str, lang: str, i18n_instance
|
||||
) -> InlineKeyboardMarkup:
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=_(key="payment_method_tx_history_title"), callback_data=f"pm:history:{pm_id}"
|
||||
)
|
||||
)
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=_(key="payment_method_delete_button"), callback_data=f"pm:delete_confirm:{pm_id}"
|
||||
)
|
||||
)
|
||||
builder.row(
|
||||
InlineKeyboardButton(text=_(key="back_to_main_menu_button"), callback_data="pm:list:0")
|
||||
)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_bind_url_keyboard(bind_url: str, lang: str, i18n_instance) -> InlineKeyboardMarkup:
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.button(text=_(key="payment_method_bind_button"), url=bind_url)
|
||||
builder.button(text=_(key="back_to_main_menu_button"), callback_data="pm:manage")
|
||||
builder.adjust(1)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_back_to_payment_methods_keyboard(lang: str, i18n_instance) -> InlineKeyboardMarkup:
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.row(
|
||||
InlineKeyboardButton(text=_(key="back_to_main_menu_button"), callback_data="pm:list:0")
|
||||
)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_back_to_payment_method_details_keyboard(
|
||||
pm_id: str, lang: str, i18n_instance
|
||||
) -> InlineKeyboardMarkup:
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
# Back one step: return to specific payment method details
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=_(key="back_to_main_menu_button"), callback_data=f"pm:view:{pm_id}"
|
||||
)
|
||||
)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_autorenew_cancel_keyboard(lang: str, i18n_instance) -> InlineKeyboardMarkup:
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=_(key="autorenew_disable_button"), callback_data="autorenew:cancel"
|
||||
)
|
||||
)
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=_(key="menu_my_subscription_inline"), callback_data="main_action:my_subscription"
|
||||
)
|
||||
)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_autorenew_confirm_keyboard(
|
||||
enable: bool, sub_id: int, lang: str, i18n_instance
|
||||
) -> InlineKeyboardMarkup:
|
||||
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=_(key="yes_button"),
|
||||
callback_data=f"autorenew:confirm:{sub_id}:{1 if enable else 0}",
|
||||
),
|
||||
InlineKeyboardButton(text=_(key="no_button"), callback_data="main_action:my_subscription"),
|
||||
)
|
||||
return builder.as_markup()
|
||||
Reference in New Issue
Block a user