API 0.5.1/ Minor changes and improvements

This commit is contained in:
Vladless
2026-02-08 22:30:05 +03:00
parent 8693115571
commit 833721bd60
37 changed files with 242 additions and 190 deletions
+1 -1
View File
@@ -3,7 +3,7 @@ from api.routes import users, keys, coupons, servers, tariffs, gifts, referrals,
app = FastAPI( app = FastAPI(
title="SoloBot API (Alpha)", title="SoloBot API (Alpha)",
version="0.5.0", version="0.5.1",
docs_url="/api/docs", docs_url="/api/docs",
redoc_url="/api/redoc", redoc_url="/api/redoc",
openapi_url="/api/openapi.json", openapi_url="/api/openapi.json",
+3 -3
View File
@@ -1,6 +1,6 @@
from typing import Any from typing import Any
from fastapi import APIRouter, Depends, HTTPException, Path, Query from fastapi import APIRouter, Body, Depends, HTTPException, Path, Query
from sqlalchemy import select from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm.attributes import InstrumentedAttribute from sqlalchemy.orm.attributes import InstrumentedAttribute
@@ -104,7 +104,7 @@ def generate_crud_router(
@router.post("/", response_model=schema_response) @router.post("/", response_model=schema_response)
async def create( async def create(
payload: Any, payload: Any = Body(...),
admin: Admin = Depends(verify_admin_token), admin: Admin = Depends(verify_admin_token),
session: AsyncSession = Depends(get_session), session: AsyncSession = Depends(get_session),
): ):
@@ -122,7 +122,7 @@ def generate_crud_router(
@router.patch(f"/{{{parameter_name}}}", response_model=schema_response) @router.patch(f"/{{{parameter_name}}}", response_model=schema_response)
async def update( async def update(
payload: Any, payload: Any = Body(...),
value: int | str = Path(..., alias=parameter_name), value: int | str = Path(..., alias=parameter_name),
admin: Admin = Depends(verify_admin_token), admin: Admin = Depends(verify_admin_token),
session: AsyncSession = Depends(get_session), session: AsyncSession = Depends(get_session),
+51 -10
View File
@@ -11,15 +11,34 @@ class CouponBase(BaseModel):
is_used: bool = False is_used: bool = False
days: int | None = Field(default=None) days: int | None = Field(default=None)
percent: int | None = Field(default=None)
max_discount_amount: int | None = Field(default=None)
min_order_amount: int | None = Field(default=None)
new_users_only: bool = False
@model_validator(mode="after") @model_validator(mode="after")
def check_exactly_one_of_amount_or_days(self) -> "CouponBase": def check_coupon_type(self) -> "CouponBase":
has_amount = self.amount not in (None, 0) has_amount = self.amount not in (None, 0)
has_days = self.days is not None has_days = self.days is not None
has_percent = self.percent is not None
provided = int(has_amount) + int(has_days) + int(has_percent)
if provided != 1:
raise ValueError("Coupon must have exactly one of: 'amount', 'days', 'percent'")
if has_days and self.days is not None and self.days <= 0:
raise ValueError("'days' must be > 0")
if has_percent and self.percent is not None and not (1 <= self.percent <= 100):
raise ValueError("'percent' must be between 1 and 100")
if has_percent:
if self.min_order_amount is not None and self.min_order_amount < 0:
raise ValueError("'min_order_amount' must be >= 0")
if self.max_discount_amount is not None and self.max_discount_amount < 0:
raise ValueError("'max_discount_amount' must be >= 0")
if has_amount and has_days:
raise ValueError("Coupon must have exactly one of: 'amount' or 'days'")
if not has_amount and not has_days:
raise ValueError("Coupon must have exactly one of: 'amount' or 'days'")
return self return self
@@ -38,12 +57,34 @@ class CouponUpdate(BaseModel):
is_used: bool | None = None is_used: bool | None = None
days: int | None = Field(default=None) days: int | None = Field(default=None)
percent: int | None = None
max_discount_amount: int | None = None
min_order_amount: int | None = None
new_users_only: bool | None = None
@model_validator(mode="after") @model_validator(mode="after")
def validate_amount_or_days(self) -> "CouponUpdate": def validate_coupon_update(self) -> "CouponUpdate":
if self.amount is None and self.days is None: has_amount = self.amount not in (None, 0)
return self has_days = self.days is not None
if self.amount is not None and self.days is not None: has_percent = self.percent is not None
raise ValueError("Specify only one of: 'amount' or 'days'")
provided = int(has_amount) + int(has_days) + int(has_percent)
if provided > 1:
raise ValueError("Specify only one of: 'amount', 'days', 'percent'")
if has_days and self.days is not None and self.days <= 0:
raise ValueError("'days' must be > 0")
if has_percent and self.percent is not None and not (1 <= self.percent <= 100):
raise ValueError("'percent' must be between 1 and 100")
if has_percent:
if self.min_order_amount is not None and self.min_order_amount < 0:
raise ValueError("'min_order_amount' must be >= 0")
if self.max_discount_amount is not None and self.max_discount_amount < 0:
raise ValueError("'max_discount_amount' must be >= 0")
return self return self
+1 -2
View File
@@ -5,6 +5,5 @@ services:
restart: unless-stopped restart: unless-stopped
network_mode: host network_mode: host
volumes: volumes:
- /etc/machine-id:/etc/machine-id:ro - /:/host:ro
- /var/lib/dbus/machine-id:/var/lib/dbus/machine-id:ro
+1 -3
View File
@@ -13,9 +13,7 @@ class IsAdminFilter(BaseFilter):
try: try:
async with async_session_maker() as session: async with async_session_maker() as session:
result = await session.execute( result = await session.execute(select(exists().where(Admin.tg_id == event.from_user.id)))
select(exists().where(Admin.tg_id == event.from_user.id))
)
return result.scalar() return result.scalar()
except (Exception,): except (Exception,):
return False return False
+2 -1
View File
@@ -3,6 +3,7 @@ from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
from aiogram.utils.keyboard import InlineKeyboardBuilder from aiogram.utils.keyboard import InlineKeyboardBuilder
from ..panel.keyboard import build_admin_back_btn from ..panel.keyboard import build_admin_back_btn
from handlers.buttons import BACK
class AdminAdsCallback(CallbackData, prefix="admin_ads"): class AdminAdsCallback(CallbackData, prefix="admin_ads"):
@@ -43,7 +44,7 @@ def build_ads_list_kb(ads: list, current_page: int, total_pages: int) -> InlineK
if current_page > 1: if current_page > 1:
pagination_buttons.append( pagination_buttons.append(
InlineKeyboardButton( InlineKeyboardButton(
text="⬅️ Назад", text=BACK,
callback_data=AdminAdsCallback(action="list", code=f"{current_page - 1}").pack(), callback_data=AdminAdsCallback(action="list", code=f"{current_page - 1}").pack(),
) )
) )
+5 -4
View File
@@ -1,6 +1,7 @@
from aiogram.utils.keyboard import InlineKeyboardBuilder from aiogram.utils.keyboard import InlineKeyboardBuilder
from ..panel.keyboard import AdminPanelCallback from ..panel.keyboard import AdminPanelCallback
from handlers.buttons import BACK
def build_bans_kb(): def build_bans_kb():
@@ -19,7 +20,7 @@ def build_bans_kb():
callback_data=AdminPanelCallback(action="bans_manual_menu").pack(), callback_data=AdminPanelCallback(action="bans_manual_menu").pack(),
) )
builder.button( builder.button(
text="🔙 Назад", text=BACK,
callback_data=AdminPanelCallback(action="management").pack(), callback_data=AdminPanelCallback(action="management").pack(),
) )
@@ -43,7 +44,7 @@ def build_blocked_users_kb():
callback_data=AdminPanelCallback(action="bans_clear_blocked").pack(), callback_data=AdminPanelCallback(action="bans_clear_blocked").pack(),
) )
builder.button( builder.button(
text="🔙 Назад", text=BACK,
callback_data=AdminPanelCallback(action="bans").pack(), callback_data=AdminPanelCallback(action="bans").pack(),
) )
@@ -67,7 +68,7 @@ def build_shadow_bans_kb():
callback_data=AdminPanelCallback(action="bans_clear_shadow").pack(), callback_data=AdminPanelCallback(action="bans_clear_shadow").pack(),
) )
builder.button( builder.button(
text="🔙 Назад", text=BACK,
callback_data=AdminPanelCallback(action="bans").pack(), callback_data=AdminPanelCallback(action="bans").pack(),
) )
@@ -87,7 +88,7 @@ def build_manual_bans_kb():
callback_data=AdminPanelCallback(action="bans_clear_manual").pack(), callback_data=AdminPanelCallback(action="bans_clear_manual").pack(),
) )
builder.button( builder.button(
text="🔙 Назад", text=BACK,
callback_data=AdminPanelCallback(action="bans").pack(), callback_data=AdminPanelCallback(action="bans").pack(),
) )
+2 -4
View File
@@ -186,9 +186,7 @@ async def choose_tariffs(
await callback.message.edit_text("❌ Для этой группы нет доступных тарифов.") await callback.message.edit_text("❌ Для этой группы нет доступных тарифов.")
return return
servers_q = await session.execute( servers_q = await session.execute(select(Server.id).where(Server.server_name.in_(selected_servers)))
select(Server.id).where(Server.server_name.in_(selected_servers))
)
server_ids = [row[0] for row in servers_q.fetchall()] server_ids = [row[0] for row in servers_q.fetchall()]
current_bindings_q = await session.execute( current_bindings_q = await session.execute(
@@ -244,7 +242,7 @@ async def toggle_tariff_selection(
) )
@router.callback_query(AdminClusterCallback.filter(F.action == "apply_tariffs"),IsAdminFilter()) @router.callback_query(AdminClusterCallback.filter(F.action == "apply_tariffs"), IsAdminFilter())
async def apply_tariffs( async def apply_tariffs(
callback: CallbackQuery, callback_data: AdminClusterCallback, session: AsyncSession, state: FSMContext callback: CallbackQuery, callback_data: AdminClusterCallback, session: AsyncSession, state: FSMContext
): ):
+10 -9
View File
@@ -4,6 +4,7 @@ from aiogram.utils.keyboard import InlineKeyboardBuilder
from ..panel.keyboard import AdminPanelCallback, build_admin_back_btn from ..panel.keyboard import AdminPanelCallback, build_admin_back_btn
from ..servers.keyboard import AdminServerCallback from ..servers.keyboard import AdminServerCallback
from handlers.buttons import BACK
class AdminClusterCallback(CallbackData, prefix="admin_cluster"): class AdminClusterCallback(CallbackData, prefix="admin_cluster"):
@@ -65,7 +66,7 @@ def build_manage_cluster_kb(cluster_servers: list, cluster_name: str) -> InlineK
) )
builder.row( builder.row(
InlineKeyboardButton( InlineKeyboardButton(
text="🔙 Назад", text=BACK,
callback_data=AdminClusterCallback(action="manage", data=cluster_name).pack(), callback_data=AdminClusterCallback(action="manage", data=cluster_name).pack(),
) )
) )
@@ -97,7 +98,7 @@ def build_attach_tariff_kb(cluster_name: str) -> InlineKeyboardMarkup:
) )
builder.row( builder.row(
InlineKeyboardButton( InlineKeyboardButton(
text="🔙 Назад", text=BACK,
callback_data=AdminClusterCallback(action="manage", data=cluster_name).pack(), callback_data=AdminClusterCallback(action="manage", data=cluster_name).pack(),
) )
) )
@@ -114,7 +115,7 @@ def build_legacy_reset_kb(cluster_name: str) -> InlineKeyboardMarkup:
) )
builder.row( builder.row(
InlineKeyboardButton( InlineKeyboardButton(
text="🔙 Назад", text=BACK,
callback_data=AdminClusterCallback(action="attach_tariff_menu", data=cluster_name).pack(), callback_data=AdminClusterCallback(action="attach_tariff_menu", data=cluster_name).pack(),
) )
) )
@@ -157,7 +158,7 @@ def build_select_subgroup_servers_kb(
) )
builder.row( builder.row(
InlineKeyboardButton( InlineKeyboardButton(
text="🔙 Назад", text=BACK,
callback_data=AdminClusterCallback(action="attach_tariff_menu", data=cluster_name).pack(), callback_data=AdminClusterCallback(action="attach_tariff_menu", data=cluster_name).pack(),
) )
) )
@@ -273,7 +274,7 @@ def build_cluster_management_kb(cluster_name: str) -> InlineKeyboardMarkup:
callback_data=AdminClusterCallback(action="set_tariff", data=cluster_name).pack(), callback_data=AdminClusterCallback(action="set_tariff", data=cluster_name).pack(),
) )
) )
builder.row(InlineKeyboardButton(text="🔙 Назад", callback_data=AdminPanelCallback(action="clusters").pack())) builder.row(InlineKeyboardButton(text=BACK, callback_data=AdminPanelCallback(action="clusters").pack()))
return builder.as_markup() return builder.as_markup()
@@ -321,7 +322,7 @@ def build_tariff_group_selection_kb(cluster_name: str, groups: list[tuple[int, s
) )
builder.row( builder.row(
InlineKeyboardButton( InlineKeyboardButton(
text="⬅️ Назад", text=BACK,
callback_data=AdminClusterCallback(action="manage", data=cluster_name).pack(), callback_data=AdminClusterCallback(action="manage", data=cluster_name).pack(),
) )
) )
@@ -363,7 +364,7 @@ def build_select_group_servers_kb(cluster_name: str, cluster_servers: list, sele
) )
builder.row( builder.row(
InlineKeyboardButton( InlineKeyboardButton(
text="🔙 Назад", text=BACK,
callback_data=AdminClusterCallback(action="manage", data=cluster_name).pack(), callback_data=AdminClusterCallback(action="manage", data=cluster_name).pack(),
) )
) )
@@ -383,7 +384,7 @@ def build_tariff_group_selection_for_servers_kb(
) )
builder.row( builder.row(
InlineKeyboardButton( InlineKeyboardButton(
text="⬅️ Назад", text=BACK,
callback_data=AdminClusterCallback(action="set_group", data=cluster_name).pack(), callback_data=AdminClusterCallback(action="set_group", data=cluster_name).pack(),
) )
) )
@@ -402,7 +403,7 @@ def build_availability_kb(cluster_name: str) -> InlineKeyboardMarkup:
], ],
[ [
InlineKeyboardButton( InlineKeyboardButton(
text="⬅️ Назад", callback_data=AdminClusterCallback(action="manage", data=cluster_name).pack() text=BACK, callback_data=AdminClusterCallback(action="manage", data=cluster_name).pack()
) )
], ],
] ]
+46 -56
View File
@@ -64,9 +64,7 @@ async def handle_coupons_create(callback_query: CallbackQuery, state: FSMContext
await state.set_state(AdminCouponsState.waiting_for_coupon_type) await state.set_state(AdminCouponsState.waiting_for_coupon_type)
async def show_coupon_audience_step(callback_query: CallbackQuery, state: FSMContext, coupon_type: str): async def show_coupon_audience_step(callback_query: CallbackQuery, state: FSMContext):
await state.update_data(coupon_type=coupon_type)
text = "🎯 <b>Кому доступен купон?</b>" text = "🎯 <b>Кому доступен купон?</b>"
kb = InlineKeyboardBuilder() kb = InlineKeyboardBuilder()
kb.button(text="👤 Всем", callback_data="coupon_audience_all") kb.button(text="👤 Всем", callback_data="coupon_audience_all")
@@ -80,52 +78,34 @@ async def show_coupon_audience_step(callback_query: CallbackQuery, state: FSMCon
@router.callback_query(F.data == "coupon_type_balance", IsAdminFilter()) @router.callback_query(F.data == "coupon_type_balance", IsAdminFilter())
async def handle_balance_coupon_selection(callback_query: CallbackQuery, state: FSMContext): async def handle_balance_coupon_selection(callback_query: CallbackQuery, state: FSMContext):
await show_coupon_audience_step(callback_query, state, "balance") await state.update_data(coupon_type="balance")
await show_coupon_audience_step(callback_query, state)
@router.callback_query(F.data == "coupon_type_days", IsAdminFilter()) @router.callback_query(F.data == "coupon_type_days", IsAdminFilter())
async def handle_days_coupon_selection(callback_query: CallbackQuery, state: FSMContext): async def handle_days_coupon_selection(callback_query: CallbackQuery, state: FSMContext):
await show_coupon_audience_step(callback_query, state, "days") await state.update_data(coupon_type="days", new_users_only=False)
@router.callback_query(F.data == "coupon_type_percent", IsAdminFilter())
async def handle_percent_coupon_selection(callback_query: CallbackQuery, state: FSMContext):
await show_coupon_audience_step(callback_query, state, "percent")
@router.callback_query(F.data.in_(("coupon_audience_all", "coupon_audience_new")), IsAdminFilter())
async def handle_coupon_audience(callback_query: CallbackQuery, state: FSMContext):
data = await state.get_data()
coupon_type = data.get("coupon_type")
if coupon_type not in ("balance", "days", "percent"):
await callback_query.answer("Ошибка: тип купона не найден", show_alert=True)
return
await state.update_data(new_users_only=callback_query.data == "coupon_audience_new")
kb = InlineKeyboardBuilder() kb = InlineKeyboardBuilder()
kb.button(text=BACK, callback_data=AdminPanelCallback(action="coupons").pack()) kb.button(text=BACK, callback_data=AdminPanelCallback(action="coupons").pack())
kb.adjust(1) kb.adjust(1)
if coupon_type == "balance": text = (
text = ( "🎫 <b>Введите данные для создания купона в формате:</b>\n\n"
"🎫 <b>Введите данные для создания купона в формате:</b>\n\n" "📝 <i>код</i> ⏳ <i>дни</i> 🔢 <i>лимит</i>\n\n"
"📝 <i>код</i> 💰 <i>сумма</i> 🔢 <i>лимит</i>\n\n" "Пример: <b>'DAYS10 10 50'</b>\n\n"
"Пример: <b>'COUPON1 50 5'</b>\n\n" )
) await callback_query.message.edit_text(text=text, reply_markup=kb.as_markup())
await callback_query.message.edit_text(text=text, reply_markup=kb.as_markup()) await state.set_state(AdminCouponsState.waiting_for_days_data)
await state.set_state(AdminCouponsState.waiting_for_balance_data)
return
if coupon_type == "days":
text = ( @router.callback_query(F.data == "coupon_type_percent", IsAdminFilter())
"🎫 <b>Введите данные для создания купона в формате:</b>\n\n" async def handle_percent_coupon_selection(callback_query: CallbackQuery, state: FSMContext):
"📝 <i>код</i> ⏳ <i>дни</i> 🔢 <i>лимит</i>\n\n" await state.update_data(coupon_type="percent", new_users_only=False)
"Пример: <b>'DAYS10 10 50'</b>\n\n"
) kb = InlineKeyboardBuilder()
await callback_query.message.edit_text(text=text, reply_markup=kb.as_markup()) kb.button(text=BACK, callback_data=AdminPanelCallback(action="coupons").pack())
await state.set_state(AdminCouponsState.waiting_for_days_data) kb.adjust(1)
return
text = ( text = (
"🎫 <b>Введите данные для создания купона в формате:</b>\n\n" "🎫 <b>Введите данные для создания купона в формате:</b>\n\n"
@@ -137,6 +117,29 @@ async def handle_coupon_audience(callback_query: CallbackQuery, state: FSMContex
await state.set_state(AdminCouponsState.waiting_for_percent_data) await state.set_state(AdminCouponsState.waiting_for_percent_data)
@router.callback_query(F.data.in_(("coupon_audience_all", "coupon_audience_new")), IsAdminFilter())
async def handle_coupon_audience(callback_query: CallbackQuery, state: FSMContext):
data = await state.get_data()
coupon_type = data.get("coupon_type")
if coupon_type != "balance":
await callback_query.answer("Ошибка: режим доступен только для купонов на баланс", show_alert=True)
return
await state.update_data(new_users_only=callback_query.data == "coupon_audience_new")
kb = InlineKeyboardBuilder()
kb.button(text=BACK, callback_data=AdminPanelCallback(action="coupons").pack())
kb.adjust(1)
text = (
"🎫 <b>Введите данные для создания купона в формате:</b>\n\n"
"📝 <i>код</i> 💰 <i>сумма</i> 🔢 <i>лимит</i>\n\n"
"Пример: <b>'COUPON1 50 5'</b>\n\n"
)
await callback_query.message.edit_text(text=text, reply_markup=kb.as_markup())
await state.set_state(AdminCouponsState.waiting_for_balance_data)
@router.message(AdminCouponsState.waiting_for_balance_data, IsAdminFilter()) @router.message(AdminCouponsState.waiting_for_balance_data, IsAdminFilter())
async def handle_balance_coupon_input(message: Message, state: FSMContext, session: Any): async def handle_balance_coupon_input(message: Message, state: FSMContext, session: Any):
text = message.text.strip() text = message.text.strip()
@@ -239,16 +242,13 @@ async def handle_days_coupon_input(message: Message, state: FSMContext, session:
return return
try: try:
data = await state.get_data()
new_users_only = bool(data.get("new_users_only"))
ok = await create_coupon( ok = await create_coupon(
session, session,
coupon_code, coupon_code,
0, 0,
usage_limit, usage_limit,
days=days, days=days,
new_users_only=new_users_only, new_users_only=False,
percent=None, percent=None,
) )
if not ok: if not ok:
@@ -256,13 +256,11 @@ async def handle_days_coupon_input(message: Message, state: FSMContext, session:
return return
coupon_link = f"https://t.me/{USERNAME_BOT}?start=coupons_{coupon_code}" coupon_link = f"https://t.me/{USERNAME_BOT}?start=coupons_{coupon_code}"
audience_txt = "🆕 Только новым" if new_users_only else "👤 Всем"
text = ( text = (
f"✅ Купон <b>{coupon_code}</b> создан!\n" f"✅ Купон <b>{coupon_code}</b> создан!\n"
f"⏳ <b>{format_days(days)}</b>\n" f"⏳ <b>{format_days(days)}</b>\n"
f"🔢 Лимит: <b>{usage_limit} раз</b>\n" f"🔢 Лимит: <b>{usage_limit} раз</b>\n"
f"🎯 Доступ: <b>{audience_txt}</b>\n"
f"🔗 <b>Ссылка:</b> <code>{coupon_link}</code>\n" f"🔗 <b>Ссылка:</b> <code>{coupon_link}</code>\n"
) )
@@ -311,29 +309,21 @@ async def handle_percent_coupon_input(message: Message, state: FSMContext, sessi
return return
try: try:
data = await state.get_data()
new_users_only = bool(data.get("new_users_only"))
ok = await create_coupon( ok = await create_coupon(
session, session,
coupon_code, coupon_code,
0, 0,
usage_limit, usage_limit,
days=None, days=None,
new_users_only=new_users_only, new_users_only=False,
percent=percent, percent=percent,
) )
if not ok: if not ok:
await message.answer("❌ Купон с таким кодом уже существует.", reply_markup=kb.as_markup()) await message.answer("❌ Купон с таким кодом уже существует.", reply_markup=kb.as_markup())
return return
audience_txt = "🆕 Только новым" if new_users_only else "👤 Всем"
text = ( text = (
f"✅ Купон <b>{coupon_code}</b> создан!\n" f"✅ Купон <b>{coupon_code}</b> создан!\n📉 Скидка: <b>{percent}%</b>\n🔢 Лимит: <b>{usage_limit} раз</b>\n"
f"📉 Скидка: <b>{percent}%</b>\n"
f"🔢 Лимит: <b>{usage_limit} раз</b>\n"
f"🎯 Доступ: <b>{audience_txt}</b>\n"
) )
kb = InlineKeyboardBuilder() kb = InlineKeyboardBuilder()
@@ -431,7 +421,7 @@ async def update_coupons_list(message, session: Any, page: int = 1):
await message.edit_text(text=text, reply_markup=kb) await message.edit_text(text=text, reply_markup=kb)
@router.inline_query(F.query.startswith("coupon_"),) @router.inline_query(F.query.startswith("coupon_"))
async def inline_coupon_handler(inline_query: InlineQuery, session: Any): async def inline_coupon_handler(inline_query: InlineQuery, session: Any):
if not INLINE_MODE: if not INLINE_MODE:
return return
+7 -6
View File
@@ -16,6 +16,7 @@ from logger import logger
from ..panel.keyboard import AdminPanelCallback from ..panel.keyboard import AdminPanelCallback
from .keyboard import build_admin_gifts_kb, build_gifts_list_kb from .keyboard import build_admin_gifts_kb, build_gifts_list_kb
from handlers.buttons import BACK
router = Router() router = Router()
@@ -41,7 +42,7 @@ async def admin_create_gift_step1(callback: CallbackQuery, session: AsyncSession
if not tariffs: if not tariffs:
builder = InlineKeyboardBuilder() builder = InlineKeyboardBuilder()
builder.button(text="🔙 Назад", callback_data=AdminPanelCallback(action="gifts").pack()) builder.button(text=BACK, callback_data=AdminPanelCallback(action="gifts").pack())
await callback.message.edit_text("❌ Нет активных тарифов в группе 'gifts'.", reply_markup=builder.as_markup()) await callback.message.edit_text("❌ Нет активных тарифов в группе 'gifts'.", reply_markup=builder.as_markup())
return return
@@ -74,7 +75,7 @@ async def admin_create_gift_step1(callback: CallbackQuery, session: AsyncSession
) )
) )
builder.row(types.InlineKeyboardButton(text="🔙 Назад", callback_data=AdminPanelCallback(action="gifts").pack())) builder.row(types.InlineKeyboardButton(text=BACK, callback_data=AdminPanelCallback(action="gifts").pack()))
await callback.message.edit_text("🎁 Выберите тариф для подарка:", reply_markup=builder.as_markup()) await callback.message.edit_text("🎁 Выберите тариф для подарка:", reply_markup=builder.as_markup())
@@ -109,7 +110,7 @@ async def admin_gift_show_tariffs_in_subgroup(callback: CallbackQuery, session:
) )
) )
builder.row(types.InlineKeyboardButton(text="⬅️ Назад", callback_data="admin_gift_create")) builder.row(types.InlineKeyboardButton(text=BACK, callback_data="admin_gift_create"))
await edit_or_send_message( await edit_or_send_message(
target_message=callback.message, target_message=callback.message,
@@ -129,7 +130,7 @@ async def handle_tariff_selection(callback: CallbackQuery, state: FSMContext):
await state.set_state(GiftCreationState.waiting_for_limit_input_or_unlimited) await state.set_state(GiftCreationState.waiting_for_limit_input_or_unlimited)
kb = InlineKeyboardBuilder() kb = InlineKeyboardBuilder()
kb.button(text="🔙 Назад", callback_data="admin_gift_create") kb.button(text=BACK, callback_data="admin_gift_create")
await callback.message.edit_text( await callback.message.edit_text(
"🔢 Введите максимальное количество активаций подарка:", reply_markup=kb.as_markup() "🔢 Введите максимальное количество активаций подарка:", reply_markup=kb.as_markup()
) )
@@ -184,7 +185,7 @@ async def show_gift_list(callback: CallbackQuery, session: AsyncSession, page: i
if not gifts: if not gifts:
builder = InlineKeyboardBuilder() builder = InlineKeyboardBuilder()
builder.button(text="🔙 Назад", callback_data=AdminPanelCallback(action="gifts").pack()) builder.button(text=BACK, callback_data=AdminPanelCallback(action="gifts").pack())
await callback.message.edit_text("❌ Подарки не найдены.", reply_markup=builder.as_markup()) await callback.message.edit_text("❌ Подарки не найдены.", reply_markup=builder.as_markup())
return return
@@ -229,7 +230,7 @@ async def view_gift(callback: CallbackQuery, session: AsyncSession):
builder = InlineKeyboardBuilder() builder = InlineKeyboardBuilder()
builder.button(text="🗑 Удалить", callback_data=f"gift_delete|{gift_id}") builder.button(text="🗑 Удалить", callback_data=f"gift_delete|{gift_id}")
builder.button(text="🔙 Назад", callback_data="admin_gifts_all") builder.button(text=BACK, callback_data="admin_gifts_all")
await callback.message.edit_text(text, reply_markup=builder.as_markup()) await callback.message.edit_text(text, reply_markup=builder.as_markup())
+2 -2
View File
@@ -27,7 +27,7 @@ def build_gift_tariffs_kb(tariffs: list[Tariff]) -> InlineKeyboardMarkup:
text=f"{tariff.name}{tariff.duration_days // 30} мес.", text=f"{tariff.name}{tariff.duration_days // 30} мес.",
callback_data=f"admin_gift_confirm|{tariff.id}", callback_data=f"admin_gift_confirm|{tariff.id}",
) )
builder.button(text="🔙 Назад", callback_data=AdminPanelCallback(action="gifts").pack()) builder.button(text=BACK, callback_data=AdminPanelCallback(action="gifts").pack())
builder.adjust(1) builder.adjust(1)
return builder.as_markup() return builder.as_markup()
@@ -61,6 +61,6 @@ def build_gifts_list_kb(gifts: list[Gift], page: int, total: int) -> InlineKeybo
if nav: if nav:
builder.row(*nav) builder.row(*nav)
builder.row(InlineKeyboardButton(text="🔙 Назад", callback_data=AdminPanelCallback(action="gifts").pack())) builder.row(InlineKeyboardButton(text=BACK, callback_data=AdminPanelCallback(action="gifts").pack()))
return builder.as_markup() return builder.as_markup()
+8 -7
View File
@@ -2,6 +2,7 @@ from aiogram.types import InlineKeyboardMarkup
from aiogram.utils.keyboard import InlineKeyboardBuilder from aiogram.utils.keyboard import InlineKeyboardBuilder
from core.bootstrap import MANAGEMENT_CONFIG from core.bootstrap import MANAGEMENT_CONFIG
from handlers.buttons import BACK
from ..panel.keyboard import AdminPanelCallback, build_admin_back_btn from ..panel.keyboard import AdminPanelCallback, build_admin_back_btn
@@ -74,7 +75,7 @@ def build_database_kb() -> InlineKeyboardMarkup:
def build_back_to_db_menu() -> InlineKeyboardMarkup: def build_back_to_db_menu() -> InlineKeyboardMarkup:
builder = InlineKeyboardBuilder() builder = InlineKeyboardBuilder()
builder.button(text="⬅️ Назад", callback_data=AdminPanelCallback(action="back_to_db_menu").pack()) builder.button(text=BACK, callback_data=AdminPanelCallback(action="back_to_db_menu").pack())
builder.adjust(1) builder.adjust(1)
return builder.as_markup() return builder.as_markup()
@@ -84,7 +85,7 @@ def build_export_db_sources_kb() -> InlineKeyboardMarkup:
builder.button(text="🌀 Remnawave", callback_data=AdminPanelCallback(action="export_remnawave").pack()) builder.button(text="🌀 Remnawave", callback_data=AdminPanelCallback(action="export_remnawave").pack())
builder.button(text="🧩 3x-ui", callback_data=AdminPanelCallback(action="request_3xui_file").pack()) builder.button(text="🧩 3x-ui", callback_data=AdminPanelCallback(action="request_3xui_file").pack())
builder.button(text="🔙 Назад", callback_data=AdminPanelCallback(action="back_to_db_menu").pack()) builder.button(text=BACK, callback_data=AdminPanelCallback(action="back_to_db_menu").pack())
builder.adjust(1) builder.adjust(1)
return builder.as_markup() return builder.as_markup()
@@ -115,7 +116,7 @@ def build_single_admin_menu(tg_id: int, role: str = "moderator") -> InlineKeyboa
text="🎟 Выпустить токен", callback_data=AdminPanelCallback(action=f"generate_token|{tg_id}").pack() text="🎟 Выпустить токен", callback_data=AdminPanelCallback(action=f"generate_token|{tg_id}").pack()
) )
builder.button(text="🔙 Назад", callback_data=AdminPanelCallback(action="admins").pack()) builder.button(text=BACK, callback_data=AdminPanelCallback(action="admins").pack())
builder.adjust(1) builder.adjust(1)
return builder.as_markup() return builder.as_markup()
@@ -125,14 +126,14 @@ def build_role_selection_kb(tg_id: int) -> InlineKeyboardMarkup:
builder = InlineKeyboardBuilder() builder = InlineKeyboardBuilder()
builder.button(text="👑 superadmin", callback_data=AdminPanelCallback(action=f"set_role|{tg_id}|superadmin").pack()) builder.button(text="👑 superadmin", callback_data=AdminPanelCallback(action=f"set_role|{tg_id}|superadmin").pack())
builder.button(text="🛡 moderator", callback_data=AdminPanelCallback(action=f"set_role|{tg_id}|moderator").pack()) builder.button(text="🛡 moderator", callback_data=AdminPanelCallback(action=f"set_role|{tg_id}|moderator").pack())
builder.button(text="🔙 Назад", callback_data=AdminPanelCallback(action=f"admin_menu|{tg_id}").pack()) builder.button(text=BACK, callback_data=AdminPanelCallback(action=f"admin_menu|{tg_id}").pack())
builder.adjust(1) builder.adjust(1)
return builder.as_markup() return builder.as_markup()
def build_admin_back_kb_to_admins() -> InlineKeyboardMarkup: def build_admin_back_kb_to_admins() -> InlineKeyboardMarkup:
builder = InlineKeyboardBuilder() builder = InlineKeyboardBuilder()
builder.button(text="🔙 Назад", callback_data=AdminPanelCallback(action="admins").pack()) builder.button(text=BACK, callback_data=AdminPanelCallback(action="admins").pack())
builder.adjust(1) builder.adjust(1)
return builder.as_markup() return builder.as_markup()
@@ -140,7 +141,7 @@ def build_admin_back_kb_to_admins() -> InlineKeyboardMarkup:
def build_token_result_kb(token: str) -> InlineKeyboardMarkup: def build_token_result_kb(token: str) -> InlineKeyboardMarkup:
builder = InlineKeyboardBuilder() builder = InlineKeyboardBuilder()
builder.button(text="📋 Скопировать токен", switch_inline_query_current_chat=token) builder.button(text="📋 Скопировать токен", switch_inline_query_current_chat=token)
builder.button(text="🔙 Назад", callback_data=AdminPanelCallback(action="admins").pack()) builder.button(text=BACK, callback_data=AdminPanelCallback(action="admins").pack())
builder.adjust(1) builder.adjust(1)
return builder.as_markup() return builder.as_markup()
@@ -150,6 +151,6 @@ def build_post_import_kb() -> InlineKeyboardMarkup:
builder.button( builder.button(
text="🔁 Перевыпустить подписки", callback_data=AdminPanelCallback(action="resync_after_import").pack() text="🔁 Перевыпустить подписки", callback_data=AdminPanelCallback(action="resync_after_import").pack()
) )
builder.button(text="🔙 Назад", callback_data=AdminPanelCallback(action="back_to_db_menu").pack()) builder.button(text=BACK, callback_data=AdminPanelCallback(action="back_to_db_menu").pack())
builder.adjust(1) builder.adjust(1)
return builder.as_markup() return builder.as_markup()
+1 -1
View File
@@ -29,7 +29,7 @@ def build_modules_kb(page: int, total_pages: int, items: list[tuple[str, str | N
if page > 1: if page > 1:
nav.append( nav.append(
InlineKeyboardButton( InlineKeyboardButton(
text="⬅️ Назад", text=BACK,
callback_data=AdminPanelCallback(action="modules", page=page - 1).pack(), callback_data=AdminPanelCallback(action="modules", page=page - 1).pack(),
) )
) )
+4 -6
View File
@@ -37,7 +37,7 @@ def build_manage_server_kb(server_name: str, cluster_name: str, enabled: bool) -
) )
builder.button( builder.button(
text="🔙 Назад", text=BACK,
callback_data=f"cluster_servers|{cluster_name}", callback_data=f"cluster_servers|{cluster_name}",
) )
@@ -70,9 +70,7 @@ def build_edit_server_fields_kb(server_name: str, server_data: dict) -> InlineKe
builder.row(InlineKeyboardButton(text="⚙️ Тип панели", callback_data=f"select_panel_type|{server_name}")) builder.row(InlineKeyboardButton(text="⚙️ Тип панели", callback_data=f"select_panel_type|{server_name}"))
builder.row( builder.row(
InlineKeyboardButton( InlineKeyboardButton(text=BACK, callback_data=AdminServerCallback(action="manage", data=server_name).pack())
text="⬅️ Назад", callback_data=AdminServerCallback(action="manage", data=server_name).pack()
)
) )
return builder.as_markup() return builder.as_markup()
@@ -83,7 +81,7 @@ def build_panel_type_selection_kb(server_name: str) -> InlineKeyboardMarkup:
inline_keyboard=[ inline_keyboard=[
[InlineKeyboardButton(text="🌐 3x-ui", callback_data=f"set_panel_type|{server_name}|3x-ui")], [InlineKeyboardButton(text="🌐 3x-ui", callback_data=f"set_panel_type|{server_name}|3x-ui")],
[InlineKeyboardButton(text="🌀 remnawave", callback_data=f"set_panel_type|{server_name}|remnawave")], [InlineKeyboardButton(text="🌀 remnawave", callback_data=f"set_panel_type|{server_name}|remnawave")],
[InlineKeyboardButton(text="⬅️ Назад", callback_data=f"edit_server|{server_name}")], [InlineKeyboardButton(text=BACK, callback_data=f"edit_server|{server_name}")],
] ]
) )
@@ -94,7 +92,7 @@ def build_cluster_selection_kb(server_name: str, clusters: list[str]) -> InlineK
for cluster in clusters: for cluster in clusters:
builder.row(InlineKeyboardButton(text=cluster, callback_data=f"set_cluster|{server_name}|{cluster}")) builder.row(InlineKeyboardButton(text=cluster, callback_data=f"set_cluster|{server_name}|{cluster}"))
builder.row(InlineKeyboardButton(text="⬅️ Назад", callback_data=f"edit_server|{server_name}")) builder.row(InlineKeyboardButton(text=BACK, callback_data=f"edit_server|{server_name}"))
return builder.as_markup() return builder.as_markup()
+9 -6
View File
@@ -2,6 +2,7 @@ from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
from aiogram.utils.keyboard import InlineKeyboardBuilder from aiogram.utils.keyboard import InlineKeyboardBuilder
from core.settings.money_config import get_currency_mode from core.settings.money_config import get_currency_mode
from handlers.buttons import BACK
from ..panel.keyboard import AdminPanelCallback, build_admin_back_btn from ..panel.keyboard import AdminPanelCallback, build_admin_back_btn
from .settings_config import ( from .settings_config import (
@@ -44,7 +45,7 @@ def build_toggle_section_keyboard(
builder.row( builder.row(
InlineKeyboardButton( InlineKeyboardButton(
text="Назад", text=BACK,
callback_data=AdminPanelCallback(action=back_action).pack(), callback_data=AdminPanelCallback(action=back_action).pack(),
) )
) )
@@ -122,7 +123,8 @@ def build_providers_order_kb(sorted_names: list[str]) -> InlineKeyboardMarkup:
InlineKeyboardButton( InlineKeyboardButton(
text="⬆️", text="⬆️",
callback_data=AdminPanelCallback( callback_data=AdminPanelCallback(
action="settings_order_up", page=pos, action="settings_order_up",
page=pos,
).pack(), ).pack(),
), ),
InlineKeyboardButton( InlineKeyboardButton(
@@ -134,7 +136,8 @@ def build_providers_order_kb(sorted_names: list[str]) -> InlineKeyboardMarkup:
InlineKeyboardButton( InlineKeyboardButton(
text="⬇️", text="⬇️",
callback_data=AdminPanelCallback( callback_data=AdminPanelCallback(
action="settings_order_down", page=pos, action="settings_order_down",
page=pos,
).pack(), ).pack(),
), ),
) )
@@ -147,7 +150,7 @@ def build_providers_order_kb(sorted_names: list[str]) -> InlineKeyboardMarkup:
) )
builder.row( builder.row(
InlineKeyboardButton( InlineKeyboardButton(
text="Назад", text=BACK,
callback_data=AdminPanelCallback(action="settings_cashboxes").pack(), callback_data=AdminPanelCallback(action="settings_cashboxes").pack(),
) )
) )
@@ -192,7 +195,7 @@ def build_settings_notifications_intervals_kb(notifications_state: dict[str, obj
builder.row( builder.row(
InlineKeyboardButton( InlineKeyboardButton(
text="Назад", text=BACK,
callback_data=AdminPanelCallback(action="settings_notifications").pack(), callback_data=AdminPanelCallback(action="settings_notifications").pack(),
) )
) )
@@ -261,7 +264,7 @@ def build_settings_money_kb(money_state: dict[str, object]) -> InlineKeyboardMar
builder.row( builder.row(
InlineKeyboardButton( InlineKeyboardButton(
text="Назад", text=BACK,
callback_data=AdminPanelCallback(action="settings").pack(), callback_data=AdminPanelCallback(action="settings").pack(),
) )
) )
@@ -68,6 +68,7 @@ async def toggle_cashbox_setting(
) )
await callback.answer("Настройка обновлена") await callback.answer("Настройка обновлена")
@router.callback_query(AdminPanelCallback.filter(F.action == "settings_providers_order")) @router.callback_query(AdminPanelCallback.filter(F.action == "settings_providers_order"))
async def open_providers_order_menu(callback: CallbackQuery, session: AsyncSession) -> None: async def open_providers_order_menu(callback: CallbackQuery, session: AsyncSession) -> None:
sorted_names = _get_sorted_provider_names() sorted_names = _get_sorted_provider_names()
+2 -1
View File
@@ -6,6 +6,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
from core.bootstrap import MONEY_CONFIG, update_money_config from core.bootstrap import MONEY_CONFIG, update_money_config
from filters.admin import IsAdminFilter from filters.admin import IsAdminFilter
from handlers.buttons import BACK
from ..panel.keyboard import AdminPanelCallback from ..panel.keyboard import AdminPanelCallback
from .keyboard import MONEY_FIELDS, build_settings_money_kb from .keyboard import MONEY_FIELDS, build_settings_money_kb
@@ -182,7 +183,7 @@ async def open_currency_mode_menu(callback: CallbackQuery) -> None:
], ],
[ [
InlineKeyboardButton( InlineKeyboardButton(
text="Назад", text=BACK,
callback_data=AdminPanelCallback(action="settings_money").pack(), callback_data=AdminPanelCallback(action="settings_money").pack(),
) )
], ],
+3 -2
View File
@@ -4,6 +4,7 @@ from aiogram import F, Router
from aiogram.types import CallbackQuery, InlineKeyboardButton, InlineKeyboardMarkup from aiogram.types import CallbackQuery, InlineKeyboardButton, InlineKeyboardMarkup
from aiogram.utils.keyboard import InlineKeyboardBuilder from aiogram.utils.keyboard import InlineKeyboardBuilder
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from handlers.buttons import BACK
from core.settings.tariffs_config import TARIFFS_CONFIG, update_tariffs_config from core.settings.tariffs_config import TARIFFS_CONFIG, update_tariffs_config
from filters.admin import IsAdminFilter from filters.admin import IsAdminFilter
@@ -61,7 +62,7 @@ def build_tariffs_settings_kb() -> InlineKeyboardMarkup:
builder.row( builder.row(
InlineKeyboardButton( InlineKeyboardButton(
text="Назад", text=BACK,
callback_data=AdminPanelCallback(action="settings").pack(), callback_data=AdminPanelCallback(action="settings").pack(),
) )
) )
@@ -137,7 +138,7 @@ def build_tariffs_packs_kb() -> InlineKeyboardMarkup:
builder.row( builder.row(
InlineKeyboardButton( InlineKeyboardButton(
text="⬅️ Назад", text=BACK,
callback_data=AdminPanelCallback(action="settings_tariffs").pack(), callback_data=AdminPanelCallback(action="settings_tariffs").pack(),
) )
) )
+8 -11
View File
@@ -3,6 +3,7 @@ from collections import defaultdict
from aiogram.filters.callback_data import CallbackData from aiogram.filters.callback_data import CallbackData
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
from aiogram.utils.keyboard import InlineKeyboardBuilder from aiogram.utils.keyboard import InlineKeyboardBuilder
from handlers.buttons import BACK
from database.tariffs import create_subgroup_hash from database.tariffs import create_subgroup_hash
@@ -33,7 +34,7 @@ def build_tariff_menu_kb() -> InlineKeyboardMarkup:
callback_data=AdminTariffCallback(action="arrange").pack(), callback_data=AdminTariffCallback(action="arrange").pack(),
) )
) )
builder.row(InlineKeyboardButton(text="⬅️ Назад", callback_data=AdminPanelCallback(action="admin").pack())) builder.row(InlineKeyboardButton(text=BACK, callback_data=AdminPanelCallback(action="admin").pack()))
return builder.as_markup() return builder.as_markup()
@@ -60,7 +61,7 @@ def build_tariff_arrangement_groups_kb(groups: list[str]) -> InlineKeyboardMarku
builder.row( builder.row(
InlineKeyboardButton( InlineKeyboardButton(
text="⬅️ Назад", text=BACK,
callback_data=AdminTariffCallback(action="list").pack(), callback_data=AdminTariffCallback(action="list").pack(),
) )
) )
@@ -116,7 +117,7 @@ def build_tariffs_arrangement_kb(group_code: str, tariffs: list) -> InlineKeyboa
builder.row( builder.row(
InlineKeyboardButton( InlineKeyboardButton(
text="⬅️ Назад", text=BACK,
callback_data=AdminPanelCallback(action="tariffs").pack(), callback_data=AdminPanelCallback(action="tariffs").pack(),
) )
) )
@@ -146,7 +147,7 @@ def build_tariff_groups_kb(groups: list[str]) -> InlineKeyboardMarkup:
row = [] row = []
builder.row( builder.row(
InlineKeyboardButton( InlineKeyboardButton(
text="⬅️ Назад", text=BACK,
callback_data=AdminPanelCallback(action="tariffs").pack(), callback_data=AdminPanelCallback(action="tariffs").pack(),
) )
) )
@@ -196,7 +197,7 @@ def build_tariff_list_kb(tariffs: list[dict]) -> InlineKeyboardMarkup:
builder.row( builder.row(
InlineKeyboardButton( InlineKeyboardButton(
text="⬅️ Назад", text=BACK,
callback_data=AdminTariffCallback(action="list").pack(), callback_data=AdminTariffCallback(action="list").pack(),
) )
) )
@@ -251,7 +252,7 @@ def build_single_tariff_kb(
], ],
[ [
InlineKeyboardButton( InlineKeyboardButton(
text="⬅️ Назад", text=BACK,
callback_data=AdminTariffCallback(action=f"group|{group_code}").pack() callback_data=AdminTariffCallback(action=f"group|{group_code}").pack()
if group_code if group_code
else AdminTariffCallback(action="list").pack(), else AdminTariffCallback(action="list").pack(),
@@ -297,10 +298,6 @@ def build_edit_tariff_fields_kb(tariff_id: int) -> InlineKeyboardMarkup:
) )
], ],
[InlineKeyboardButton(text="🔘 Активность", callback_data=f"toggle_active|{tariff_id}")], [InlineKeyboardButton(text="🔘 Активность", callback_data=f"toggle_active|{tariff_id}")],
[ [InlineKeyboardButton(text=BACK, callback_data=AdminTariffCallback(action=f"view|{tariff_id}").pack())],
InlineKeyboardButton(
text="⬅️ Назад", callback_data=AdminTariffCallback(action=f"view|{tariff_id}").pack()
)
],
] ]
) )
+3 -2
View File
@@ -18,6 +18,7 @@ from database.models import Gift, Key, Server, Tariff
from filters.admin import IsAdminFilter from filters.admin import IsAdminFilter
from ..panel.keyboard import AdminPanelCallback from ..panel.keyboard import AdminPanelCallback
from handlers.buttons import BACK
from . import router from . import router
from .keyboard import ( from .keyboard import (
AdminTariffCallback, AdminTariffCallback,
@@ -331,7 +332,7 @@ async def confirm_tariff_deletion(callback: CallbackQuery, callback_data: AdminT
inline_keyboard=[ inline_keyboard=[
[ [
InlineKeyboardButton( InlineKeyboardButton(
text="⬅️ Назад", callback_data=AdminTariffCallback(action=f"view|{tariff_id}").pack() text=BACK, callback_data=AdminTariffCallback(action=f"view|{tariff_id}").pack()
) )
] ]
] ]
@@ -465,7 +466,7 @@ async def ask_new_value(callback: CallbackQuery, state: FSMContext):
], ],
[ [
InlineKeyboardButton( InlineKeyboardButton(
text="⬅️ Назад", text=BACK,
callback_data=AdminTariffCallback(action=f"view|{tariff_id}").pack(), callback_data=AdminTariffCallback(action=f"view|{tariff_id}").pack(),
) )
], ],
+6 -5
View File
@@ -9,6 +9,7 @@ from aiogram.types import (
Message, Message,
) )
from aiogram.utils.keyboard import InlineKeyboardBuilder from aiogram.utils.keyboard import InlineKeyboardBuilder
from handlers.buttons import BACK
from sqlalchemy import select, update from sqlalchemy import select, update
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
@@ -40,7 +41,7 @@ async def start_subgrouping(callback: CallbackQuery, state: FSMContext, session:
inline_keyboard=[ inline_keyboard=[
[ [
InlineKeyboardButton( InlineKeyboardButton(
text="⬅️ Назад", callback_data=AdminTariffCallback(action=f"group|{group_code}").pack() text=BACK, callback_data=AdminTariffCallback(action=f"group|{group_code}").pack()
) )
] ]
] ]
@@ -211,7 +212,7 @@ async def view_subgroup_tariffs(callback: CallbackQuery, session: AsyncSession):
builder.row( builder.row(
InlineKeyboardButton( InlineKeyboardButton(
text="⬅️ Назад", text=BACK,
callback_data=AdminTariffCallback(action=f"group|{group_code}").pack(), callback_data=AdminTariffCallback(action=f"group|{group_code}").pack(),
) )
) )
@@ -296,7 +297,7 @@ async def save_new_subgroup_title(message: Message, state: FSMContext, session:
inline_keyboard=[ inline_keyboard=[
[ [
InlineKeyboardButton( InlineKeyboardButton(
text="⬅️ Назад", callback_data=AdminTariffCallback(action=f"group|{group_code}").pack() text=BACK, callback_data=AdminTariffCallback(action=f"group|{group_code}").pack()
) )
] ]
] ]
@@ -355,7 +356,7 @@ async def perform_subgroup_deletion(callback: CallbackQuery, state: FSMContext,
inline_keyboard=[ inline_keyboard=[
[ [
InlineKeyboardButton( InlineKeyboardButton(
text="⬅️ Назад", callback_data=AdminTariffCallback(action=f"group|{group_code}").pack() text=BACK, callback_data=AdminTariffCallback(action=f"group|{group_code}").pack()
) )
] ]
] ]
@@ -387,7 +388,7 @@ async def start_edit_subgroup_tariffs(callback: CallbackQuery, state: FSMContext
"❌ Нет доступных тарифов для редактирования.", "❌ Нет доступных тарифов для редактирования.",
reply_markup=InlineKeyboardMarkup( reply_markup=InlineKeyboardMarkup(
inline_keyboard=[ inline_keyboard=[
[InlineKeyboardButton(text="⬅️ Назад", callback_data=f"view_subgroup|{subgroup_hash}|{group_code}")] [InlineKeyboardButton(text=BACK, callback_data=f"view_subgroup|{subgroup_hash}|{group_code}")]
] ]
), ),
) )
+3 -2
View File
@@ -5,6 +5,7 @@ from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
from aiogram.utils.keyboard import InlineKeyboardBuilder from aiogram.utils.keyboard import InlineKeyboardBuilder
from sqlalchemy import select from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from handlers.buttons import BACK
from config import HWID_RESET_BUTTON from config import HWID_RESET_BUTTON
from database import get_clusters from database import get_clusters
@@ -349,7 +350,7 @@ def build_hwid_menu_kb(email: str, tg_id: int) -> InlineKeyboardMarkup:
callback_data=AdminUserEditorCallback(action="users_hwid_reset", data=email, tg_id=tg_id).pack(), callback_data=AdminUserEditorCallback(action="users_hwid_reset", data=email, tg_id=tg_id).pack(),
) )
builder.button( builder.button(
text="🔙 Назад", text=BACK,
callback_data=AdminUserEditorCallback(action="users_key_edit", data=email, tg_id=tg_id).pack(), callback_data=AdminUserEditorCallback(action="users_key_edit", data=email, tg_id=tg_id).pack(),
) )
builder.adjust(1) builder.adjust(1)
@@ -431,7 +432,7 @@ def build_user_ban_type_kb(tg_id: int) -> InlineKeyboardMarkup:
builder.row( builder.row(
InlineKeyboardButton( InlineKeyboardButton(
text="⬅️ Назад", text=BACK,
callback_data=AdminUserEditorCallback(action="users_editor", tg_id=tg_id, edit=True).pack(), callback_data=AdminUserEditorCallback(action="users_editor", tg_id=tg_id, edit=True).pack(),
) )
) )
+4 -3
View File
@@ -7,6 +7,7 @@ from aiogram.utils.keyboard import InlineKeyboardBuilder
from sqlalchemy import delete from sqlalchemy import delete
from sqlalchemy.dialects.postgresql import insert as pg_insert from sqlalchemy.dialects.postgresql import insert as pg_insert
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from handlers.buttons import BACK
from database.models import ManualBan from database.models import ManualBan
from filters.admin import IsAdminFilter from filters.admin import IsAdminFilter
@@ -41,7 +42,7 @@ async def handle_ban_forever_start(callback: CallbackQuery, callback_data: Admin
await state.update_data(tg_id=callback_data.tg_id) await state.update_data(tg_id=callback_data.tg_id)
kb = InlineKeyboardBuilder() kb = InlineKeyboardBuilder()
kb.row(build_editor_btn("⬅️ Назад", tg_id=callback_data.tg_id, edit=True)) kb.row(build_editor_btn(BACK, tg_id=callback_data.tg_id, edit=True))
await callback.message.edit_text( await callback.message.edit_text(
text="✏️ Введите причину <b>постоянной блокировки</b> (или <code>-</code>, чтобы пропустить):", text="✏️ Введите причину <b>постоянной блокировки</b> (или <code>-</code>, чтобы пропустить):",
@@ -97,7 +98,7 @@ async def handle_ban_temporary(callback: CallbackQuery, callback_data: AdminUser
await state.update_data(tg_id=callback_data.tg_id) await state.update_data(tg_id=callback_data.tg_id)
kb = InlineKeyboardBuilder() kb = InlineKeyboardBuilder()
kb.row(build_editor_btn("⬅️ Назад", tg_id=callback_data.tg_id, edit=True)) kb.row(build_editor_btn(BACK, tg_id=callback_data.tg_id, edit=True))
await callback.message.edit_text( await callback.message.edit_text(
text="✏️ Введите причину <b>временной блокировки</b> (или <code>-</code>, чтобы пропустить):", text="✏️ Введите причину <b>временной блокировки</b> (или <code>-</code>, чтобы пропустить):",
@@ -114,7 +115,7 @@ async def handle_ban_reason_input(message: Message, state: FSMContext):
tg_id = user_data.get("tg_id") tg_id = user_data.get("tg_id")
kb = InlineKeyboardBuilder() kb = InlineKeyboardBuilder()
kb.row(build_editor_btn("⬅️ Назад", tg_id=tg_id, edit=True)) kb.row(build_editor_btn(BACK, tg_id=tg_id, edit=True))
await message.answer( await message.answer(
"⏳ Введите срок блокировки в днях (0 — навсегда):", "⏳ Введите срок блокировки в днях (0 — навсегда):",
+12 -12
View File
@@ -3,7 +3,7 @@ import time
import uuid import uuid
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
from core.settings.tariffs_config import normalize_tariff_config from handlers.buttons import BACK
import pytz import pytz
@@ -419,7 +419,7 @@ async def confirm_admin_key_reissue(callback_query: CallbackQuery, session: Asyn
) )
builder.row( builder.row(
InlineKeyboardButton( InlineKeyboardButton(
text="🔙 Назад", text=BACK,
callback_data=AdminUserEditorCallback( callback_data=AdminUserEditorCallback(
action="users_key_edit", action="users_key_edit",
tg_id=tg_id, tg_id=tg_id,
@@ -450,7 +450,7 @@ async def confirm_admin_key_reissue(callback_query: CallbackQuery, session: Asyn
) )
builder.row( builder.row(
InlineKeyboardButton( InlineKeyboardButton(
text="Назад", text=BACK,
callback_data=f"users_key_edit|{email}", callback_data=f"users_key_edit|{email}",
) )
) )
@@ -503,7 +503,7 @@ async def admin_reissue_country(callback_query: CallbackQuery, session: AsyncSes
) )
builder.row( builder.row(
InlineKeyboardButton( InlineKeyboardButton(
text="🔙 Назад", text=BACK,
callback_data=AdminUserEditorCallback( callback_data=AdminUserEditorCallback(
action="users_key_edit", action="users_key_edit",
tg_id=tg_id, tg_id=tg_id,
@@ -588,7 +588,7 @@ async def handle_recreate_key_start(
) )
builder.row( builder.row(
InlineKeyboardButton( InlineKeyboardButton(
text="🔙 Назад", text=BACK,
callback_data=AdminUserEditorCallback(action="users_key_edit", tg_id=tg_id, data=email).pack(), callback_data=AdminUserEditorCallback(action="users_key_edit", tg_id=tg_id, data=email).pack(),
) )
) )
@@ -735,7 +735,7 @@ async def handle_recreate_key_confirm(
builder = InlineKeyboardBuilder() builder = InlineKeyboardBuilder()
builder.row( builder.row(
InlineKeyboardButton( InlineKeyboardButton(
text="🔙 Назад", text=BACK,
callback_data=AdminUserEditorCallback( callback_data=AdminUserEditorCallback(
action="users_key_edit", action="users_key_edit",
tg_id=tg_id, tg_id=tg_id,
@@ -1478,7 +1478,7 @@ async def render_config_menu(callback_query: CallbackQuery, state: FSMContext, s
builder.row(InlineKeyboardButton(text="💾 Сохранить", callback_data="cfg_save")) builder.row(InlineKeyboardButton(text="💾 Сохранить", callback_data="cfg_save"))
builder.row( builder.row(
InlineKeyboardButton( InlineKeyboardButton(
text="🔙 Назад", text=BACK,
callback_data=AdminUserEditorCallback(action="users_key_edit", data=email, tg_id=tg_id).pack(), callback_data=AdminUserEditorCallback(action="users_key_edit", data=email, tg_id=tg_id).pack(),
) )
) )
@@ -1499,7 +1499,7 @@ async def handle_cfg_edit_base(callback_query: CallbackQuery, state: FSMContext,
builder.row(InlineKeyboardButton(text="📱 Устройства", callback_data="cfg_base_devices")) builder.row(InlineKeyboardButton(text="📱 Устройства", callback_data="cfg_base_devices"))
if traffic_options: if traffic_options:
builder.row(InlineKeyboardButton(text="📊 Трафик", callback_data="cfg_base_traffic")) builder.row(InlineKeyboardButton(text="📊 Трафик", callback_data="cfg_base_traffic"))
builder.row(InlineKeyboardButton(text="🔙 Назад", callback_data="cfg_back_menu")) builder.row(InlineKeyboardButton(text=BACK, callback_data="cfg_back_menu"))
await callback_query.message.edit_text( await callback_query.message.edit_text(
"<b>📦 Редактирование базы тарифа</b>\n\nВыберите параметр:", "<b>📦 Редактирование базы тарифа</b>\n\nВыберите параметр:",
@@ -1519,7 +1519,7 @@ async def handle_cfg_edit_addon(callback_query: CallbackQuery, state: FSMContext
builder.row(InlineKeyboardButton(text="📱 Устройства", callback_data="cfg_addon_devices")) builder.row(InlineKeyboardButton(text="📱 Устройства", callback_data="cfg_addon_devices"))
if traffic_options: if traffic_options:
builder.row(InlineKeyboardButton(text="📊 Трафик", callback_data="cfg_addon_traffic")) builder.row(InlineKeyboardButton(text="📊 Трафик", callback_data="cfg_addon_traffic"))
builder.row(InlineKeyboardButton(text="🔙 Назад", callback_data="cfg_back_menu")) builder.row(InlineKeyboardButton(text=BACK, callback_data="cfg_back_menu"))
await callback_query.message.edit_text( await callback_query.message.edit_text(
"<b>➕ Редактирование докупки</b>\n\nВыберите параметр:", "<b>➕ Редактирование докупки</b>\n\nВыберите параметр:",
@@ -1544,7 +1544,7 @@ async def handle_cfg_base_devices(callback_query: CallbackQuery, state: FSMConte
mark = "" if int(opt) == int(base_devices) else "" mark = "" if int(opt) == int(base_devices) else ""
builder.button(text=f"{opt} устр.{mark}", callback_data=f"cfg_set_base_dev:{opt}") builder.button(text=f"{opt} устр.{mark}", callback_data=f"cfg_set_base_dev:{opt}")
builder.adjust(3) builder.adjust(3)
builder.row(InlineKeyboardButton(text="🔙 Назад", callback_data="cfg_back_menu")) builder.row(InlineKeyboardButton(text=BACK, callback_data="cfg_back_menu"))
await state.set_state(UserEditorState.config_select_base) await state.set_state(UserEditorState.config_select_base)
await state.update_data(cfg_param="devices") await state.update_data(cfg_param="devices")
@@ -1568,7 +1568,7 @@ async def handle_cfg_base_traffic(callback_query: CallbackQuery, state: FSMConte
label = "безлимит" if opt == 0 else f"{opt} ГБ" label = "безлимит" if opt == 0 else f"{opt} ГБ"
builder.button(text=f"{label}{mark}", callback_data=f"cfg_set_base_traf:{opt}") builder.button(text=f"{label}{mark}", callback_data=f"cfg_set_base_traf:{opt}")
builder.adjust(2) builder.adjust(2)
builder.row(InlineKeyboardButton(text="🔙 Назад", callback_data="cfg_back_menu")) builder.row(InlineKeyboardButton(text=BACK, callback_data="cfg_back_menu"))
await state.set_state(UserEditorState.config_select_base) await state.set_state(UserEditorState.config_select_base)
await state.update_data(cfg_param="traffic") await state.update_data(cfg_param="traffic")
@@ -1698,7 +1698,7 @@ async def handle_cfg_input_addon(message: Message, state: FSMContext, session: A
builder.row(InlineKeyboardButton(text="💾 Сохранить", callback_data="cfg_save")) builder.row(InlineKeyboardButton(text="💾 Сохранить", callback_data="cfg_save"))
builder.row( builder.row(
InlineKeyboardButton( InlineKeyboardButton(
text="🔙 Назад", text=BACK,
callback_data=AdminUserEditorCallback(action="users_key_edit", data=email, tg_id=tg_id).pack(), callback_data=AdminUserEditorCallback(action="users_key_edit", data=email, tg_id=tg_id).pack(),
) )
) )
+7 -6
View File
@@ -7,6 +7,7 @@ from aiogram.types import CallbackQuery, InlineKeyboardButton
from aiogram.utils.keyboard import InlineKeyboardBuilder from aiogram.utils.keyboard import InlineKeyboardBuilder
from sqlalchemy import select, update from sqlalchemy import select, update
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from handlers.buttons import BACK
from core.settings.tariffs_config import normalize_tariff_config from core.settings.tariffs_config import normalize_tariff_config
from database import get_tariff_by_id from database import get_tariff_by_id
@@ -70,7 +71,7 @@ async def handle_user_choose_tariff_group(
builder = InlineKeyboardBuilder() builder = InlineKeyboardBuilder()
for group_code in groups: for group_code in groups:
builder.button(text=group_code, callback_data=f"group:{group_code}") builder.button(text=group_code, callback_data=f"group:{group_code}")
builder.button(text="🔙 Назад", callback_data="back:renew") builder.button(text=BACK, callback_data="back:renew")
builder.adjust(1) builder.adjust(1)
await callback_query.message.edit_text( await callback_query.message.edit_text(
@@ -101,7 +102,7 @@ async def handle_user_choose_tariff(
builder = InlineKeyboardBuilder() builder = InlineKeyboardBuilder()
for tariff in tariffs: for tariff in tariffs:
builder.button(text=f"{tariff.name} {int(tariff.price_rub)}", callback_data=f"confirm:{tariff.id}") builder.button(text=f"{tariff.name} {int(tariff.price_rub)}", callback_data=f"confirm:{tariff.id}")
builder.button(text="🔙 Назад", callback_data="back:group") builder.button(text=BACK, callback_data="back:group")
builder.adjust(1) builder.adjust(1)
await callback_query.message.edit_text( await callback_query.message.edit_text(
@@ -271,7 +272,7 @@ async def handle_user_renew_confirm(
builder.row(b) builder.row(b)
builder.row(InlineKeyboardButton(text="✅ Применить", callback_data=f"cfg_renew_apply|{tariff_id}")) builder.row(InlineKeyboardButton(text="✅ Применить", callback_data=f"cfg_renew_apply|{tariff_id}"))
builder.row(InlineKeyboardButton(text="🔙 Назад", callback_data="back:group")) builder.row(InlineKeyboardButton(text=BACK, callback_data="back:group"))
devices_label = ( devices_label = (
"Безлимит устройств" "Безлимит устройств"
@@ -456,7 +457,7 @@ async def handle_cfg_renew_devices(callback_query: CallbackQuery, state: FSMCont
builder.row(b) builder.row(b)
builder.row(InlineKeyboardButton(text="✅ Применить", callback_data=f"cfg_renew_apply|{tariff_id}")) builder.row(InlineKeyboardButton(text="✅ Применить", callback_data=f"cfg_renew_apply|{tariff_id}"))
builder.row(InlineKeyboardButton(text="🔙 Назад", callback_data="back:group")) builder.row(InlineKeyboardButton(text=BACK, callback_data="back:group"))
devices_label = "Безлимит устройств" if selected_devices <= 0 else f"{selected_devices} устройств" devices_label = "Безлимит устройств" if selected_devices <= 0 else f"{selected_devices} устройств"
traffic_label = ( traffic_label = (
@@ -573,7 +574,7 @@ async def handle_cfg_renew_traffic(callback_query: CallbackQuery, state: FSMCont
builder.row(b) builder.row(b)
builder.row(InlineKeyboardButton(text="✅ Применить", callback_data=f"cfg_renew_apply|{tariff_id}")) builder.row(InlineKeyboardButton(text="✅ Применить", callback_data=f"cfg_renew_apply|{tariff_id}"))
builder.row(InlineKeyboardButton(text="🔙 Назад", callback_data="back:group")) builder.row(InlineKeyboardButton(text=BACK, callback_data="back:group"))
devices_label = ( devices_label = (
"Безлимит устройств" "Безлимит устройств"
@@ -708,7 +709,7 @@ async def handle_back_to_group(
builder = InlineKeyboardBuilder() builder = InlineKeyboardBuilder()
for group_code in groups: for group_code in groups:
builder.button(text=group_code, callback_data=f"group:{group_code}") builder.button(text=group_code, callback_data=f"group:{group_code}")
builder.button(text="🔙 Назад", callback_data="back:renew") builder.button(text=BACK, callback_data="back:renew")
builder.adjust(1) builder.adjust(1)
await callback_query.message.edit_text( await callback_query.message.edit_text(
+1
View File
@@ -21,6 +21,7 @@ TRIAL_SUB = "🎁 Пробная подписка"
TRIAL_BONUS = "🚀 Активировать пробный период" TRIAL_BONUS = "🚀 Активировать пробный период"
MY_SUB = "🔐 Моя подписка" MY_SUB = "🔐 Моя подписка"
RENEW_SUB = "🔄 Обновить подписку" RENEW_SUB = "🔄 Обновить подписку"
ADMIN_BTN = "📊 Администратор"
ADDONS_BUTTON_DEVICES = "➕ Добавить устройства" ADDONS_BUTTON_DEVICES = "➕ Добавить устройства"
ADDONS_BUTTON_TRAFFIC = " Добавить трафик" ADDONS_BUTTON_TRAFFIC = " Добавить трафик"
+6 -2
View File
@@ -238,8 +238,12 @@ async def handle_key_extension(
tariff_gb = int(tariff["traffic_limit"]) if tariff and tariff.get("traffic_limit") else 0 tariff_gb = int(tariff["traffic_limit"]) if tariff and tariff.get("traffic_limit") else 0
tariff_devices = int(tariff["device_limit"]) if tariff and tariff.get("device_limit") else 0 tariff_devices = int(tariff["device_limit"]) if tariff and tariff.get("device_limit") else 0
total_gb = int(key.current_traffic_limit) if getattr(key, "current_traffic_limit", None) is not None else tariff_gb total_gb = (
device_limit = int(key.current_device_limit) if getattr(key, "current_device_limit", None) is not None else tariff_devices int(key.current_traffic_limit) if getattr(key, "current_traffic_limit", None) is not None else tariff_gb
)
device_limit = (
int(key.current_device_limit) if getattr(key, "current_device_limit", None) is not None else tariff_devices
)
key_subgroup = None key_subgroup = None
if tariff: if tariff:
+2 -2
View File
@@ -11,7 +11,7 @@ from aiogram.types import (
from aiogram.utils.keyboard import InlineKeyboardBuilder from aiogram.utils.keyboard import InlineKeyboardBuilder
from config import RUB_TO_XTR, STARS_BOT_URL from config import RUB_TO_XTR, STARS_BOT_URL
from handlers.buttons import BACK, MAIN_MENU from handlers.buttons import BACK, MAIN_MENU, STARS_BOT
from logger import logger from logger import logger
from .utils import edit_or_send_message from .utils import edit_or_send_message
@@ -31,7 +31,7 @@ async def process_donate(callback_query: CallbackQuery, state: FSMContext):
await state.clear() await state.clear()
builder = InlineKeyboardBuilder() builder = InlineKeyboardBuilder()
builder.row(InlineKeyboardButton(text="🤖 Бот для покупки звезд", url=STARS_BOT_URL)) builder.row(InlineKeyboardButton(text=STARS_BOT, url=STARS_BOT_URL))
builder.row( builder.row(
InlineKeyboardButton( InlineKeyboardButton(
text="💰 Ввести сумму доната", text="💰 Ввести сумму доната",
+14 -10
View File
@@ -167,17 +167,20 @@ async def process_macos_menu(callback_query: CallbackQuery, session: Any):
@router.callback_query(F.data.startswith("connect_tv|")) @router.callback_query(F.data.startswith("connect_tv|"))
async def process_connect_tv(callback_query: CallbackQuery, session: Any): async def process_connect_tv(callback_query: CallbackQuery, session: Any):
key_name = callback_query.data.split("|")[1] key_name = callback_query.data.split("|", 1)[1]
record = await get_key_details(session, key_name) record = await get_key_details(session, key_name)
final_link = None final_link = None
is_full_remnawave = False is_full_remnawave = False
use_webapp = False use_webapp = False
is_remnawave_webapp = False
if record: if record:
server_name = record["server_id"] server_name = record.get("server_id")
final_link = record.get("key") or record.get("remnawave_link") final_link = record.get("key") or record.get("remnawave_link")
is_full_remnawave = await is_full_remnawave_cluster(server_name, session)
if server_name:
is_full_remnawave = await is_full_remnawave_cluster(server_name, session)
remnawave_webapp_enabled = bool(MODES_CONFIG.get("REMNAWAVE_WEBAPP_ENABLED", REMNAWAVE_WEBAPP)) remnawave_webapp_enabled = bool(MODES_CONFIG.get("REMNAWAVE_WEBAPP_ENABLED", REMNAWAVE_WEBAPP))
happ_cryptolink_enabled = bool(MODES_CONFIG.get("HAPP_CRYPTOLINK_ENABLED", HAPP_CRYPTOLINK)) happ_cryptolink_enabled = bool(MODES_CONFIG.get("HAPP_CRYPTOLINK_ENABLED", HAPP_CRYPTOLINK))
@@ -191,22 +194,23 @@ async def process_connect_tv(callback_query: CallbackQuery, session: Any):
) )
is_remnawave_webapp = bool(is_full_remnawave and final_link and use_webapp and not happ_cryptolink_enabled) is_remnawave_webapp = bool(is_full_remnawave and final_link and use_webapp and not happ_cryptolink_enabled)
else:
is_remnawave_webapp = False
if is_remnawave_webapp: if not final_link:
back_callback = f"view_key|{key_name}" await callback_query.answer("❌ Ссылка подписки не найдена", show_alert=True)
else: return
back_callback = f"connect_device|{key_name}"
back_callback = f"view_key|{key_name}" if is_remnawave_webapp else f"connect_device|{key_name}"
builder = InlineKeyboardBuilder() builder = InlineKeyboardBuilder()
builder.row(InlineKeyboardButton(text=TV_CONTINUE, callback_data=f"continue_tv|{key_name}")) builder.row(InlineKeyboardButton(text=TV_CONTINUE, callback_data=f"continue_tv|{key_name}"))
builder.row(InlineKeyboardButton(text=BACK, callback_data=back_callback)) builder.row(InlineKeyboardButton(text=BACK, callback_data=back_callback))
builder.row(InlineKeyboardButton(text=MAIN_MENU, callback_data="profile")) builder.row(InlineKeyboardButton(text=MAIN_MENU, callback_data="profile"))
text = CONNECT_TV_TEXT.format(subscription_link=final_link)
await edit_or_send_message( await edit_or_send_message(
target_message=callback_query.message, target_message=callback_query.message,
text=CONNECT_TV_TEXT, text=text,
reply_markup=builder.as_markup(), reply_markup=builder.as_markup(),
media_path=None, media_path=None,
disable_web_page_preview=True, disable_web_page_preview=True,
+2 -2
View File
@@ -28,7 +28,7 @@ from database.models import Admin
from database.notifications import check_hot_lead_discount from database.notifications import check_hot_lead_discount
from database.tariffs import create_subgroup_hash, find_subgroup_by_hash, get_tariffs from database.tariffs import create_subgroup_hash, find_subgroup_by_hash, get_tariffs
from handlers.admin.panel.keyboard import AdminPanelCallback from handlers.admin.panel.keyboard import AdminPanelCallback
from handlers.buttons import MAIN_MENU from handlers.buttons import MAIN_MENU, BACK
from handlers.texts import ( from handlers.texts import (
CREATING_CONNECTION_MSG, CREATING_CONNECTION_MSG,
DISCOUNT_OFFER_MESSAGE, DISCOUNT_OFFER_MESSAGE,
@@ -362,7 +362,7 @@ async def show_tariffs_in_subgroup_user(callback: CallbackQuery, state: FSMConte
callback_prefix="select_tariff_plan", callback_prefix="select_tariff_plan",
) )
builder.row(InlineKeyboardButton(text="⬅️ Назад", callback_data="back_to_tariff_group_list")) builder.row(InlineKeyboardButton(text=BACK, callback_data="back_to_tariff_group_list"))
builder.row(InlineKeyboardButton(text=MAIN_MENU, callback_data="profile")) builder.row(InlineKeyboardButton(text=MAIN_MENU, callback_data="profile"))
await edit_or_send_message( await edit_or_send_message(
+1 -1
View File
@@ -368,7 +368,7 @@ async def show_tariffs_in_renew_subgroup(callback: CallbackQuery, state: FSMCont
callback_prefix="renew_plan", callback_prefix="renew_plan",
) )
builder.row(InlineKeyboardButton(text="⬅️ Назад", callback_data=f"renew_key|{key_name}")) builder.row(InlineKeyboardButton(text=BACK, callback_data=f"renew_key|{key_name}"))
builder.row(InlineKeyboardButton(text=MAIN_MENU, callback_data="profile")) builder.row(InlineKeyboardButton(text=MAIN_MENU, callback_data="profile"))
hook_builder = InlineKeyboardBuilder() hook_builder = InlineKeyboardBuilder()
+1
View File
@@ -74,6 +74,7 @@ async def renew_on_remnawave(
if not await remna.login(REMNAWAVE_LOGIN, REMNAWAVE_PASSWORD): if not await remna.login(REMNAWAVE_LOGIN, REMNAWAVE_PASSWORD):
logger.error(f"{PANEL_REMNA} Не удалось войти в Remnawave API") logger.error(f"{PANEL_REMNA} Не удалось войти в Remnawave API")
return False return False
hwid_device_limit = int(hwid_device_limit or 0)
if old_device_limit is not None and hwid_device_limit < old_device_limit: if old_device_limit is not None and hwid_device_limit < old_device_limit:
try: try:
+7 -2
View File
@@ -19,7 +19,12 @@ from handlers.payments.currency_rates import format_for_user
from handlers.payments.providers import get_providers_with_hooks from handlers.payments.providers import get_providers_with_hooks
from handlers.payments.stars.handlers import process_callback_pay_stars from handlers.payments.stars.handlers import process_callback_pay_stars
from handlers.payments.tribute.handlers import process_callback_pay_tribute from handlers.payments.tribute.handlers import process_callback_pay_tribute
from handlers.texts import FAST_PAY_CHOOSE_CURRENCY, BALANCE_MANAGEMENT_TEXT, PAYMENT_METHODS_MSG from handlers.texts import (
FAST_PAY_CHOOSE_CURRENCY,
BALANCE_MANAGEMENT_TEXT,
PAYMENT_METHODS_MSG,
BALANCE_HISTORY_HEADER,
)
from hooks.hook_buttons import insert_hook_buttons from hooks.hook_buttons import insert_hook_buttons
from hooks.hooks import run_hooks from hooks.hooks import run_hooks
@@ -211,7 +216,7 @@ async def balance_history_handler(callback_query: CallbackQuery, session: Any):
if records: if records:
language_code = getattr(callback_query.from_user, "language_code", None) language_code = getattr(callback_query.from_user, "language_code", None)
history_text = "<b>💳 История операций:</b>\n\n<blockquote>" history_text = f"{BALANCE_HISTORY_HEADER}</b>\n\n<blockquote>"
for record in records: for record in records:
amount_rub = record["amount"] or 0 amount_rub = record["amount"] or 0
formatted_amount = await format_for_user(session, callback_query.from_user.id, amount_rub, language_code) formatted_amount = await format_for_user(session, callback_query.from_user.id, amount_rub, language_code)
+1 -3
View File
@@ -79,9 +79,7 @@ def _get_effective_order(name: str, cfg: dict[str, Any]) -> int:
def _sort_providers(providers: dict[str, dict[str, Any]]) -> dict[str, dict[str, Any]]: def _sort_providers(providers: dict[str, dict[str, Any]]) -> dict[str, dict[str, Any]]:
"""Сортирует провайдеров по полю 'order' (меньше = выше).""" """Сортирует провайдеров по полю 'order' (меньше = выше)."""
return dict( return dict(sorted(providers.items(), key=lambda item: _get_effective_order(item[0], item[1])))
sorted(providers.items(), key=lambda item: _get_effective_order(item[0], item[1]))
)
def sort_provider_names(names: list[str], providers_map: dict[str, dict[str, Any]]) -> list[str]: def sort_provider_names(names: list[str], providers_map: dict[str, dict[str, Any]]) -> list[str]:
+2 -1
View File
@@ -28,6 +28,7 @@ from handlers.buttons import (
MY_SUB, MY_SUB,
MY_SUBS, MY_SUBS,
TRIAL_SUB, TRIAL_SUB,
ADMIN_BTN,
) )
from handlers.payments.currency_rates import format_for_user from handlers.payments.currency_rates import format_for_user
from handlers.texts import ADD_SUBSCRIPTION_HINT from handlers.texts import ADD_SUBSCRIPTION_HINT
@@ -135,7 +136,7 @@ async def process_callback_view_profile(
if admin: if admin:
builder.row( builder.row(
InlineKeyboardButton( InlineKeyboardButton(
text="📊 Администратор", text=ADMIN_BTN,
callback_data=AdminPanelCallback(action="admin").pack(), callback_data=AdminPanelCallback(action="admin").pack(),
) )
) )
+2 -1
View File
@@ -39,6 +39,7 @@ from handlers.buttons import (
SUB_CHANELL_DONE, SUB_CHANELL_DONE,
SUPPORT, SUPPORT,
TRIAL_SUB, TRIAL_SUB,
ADMIN_BTN,
) )
from handlers.captcha import generate_captcha from handlers.captcha import generate_captcha
from handlers.coupons import activate_coupon from handlers.coupons import activate_coupon
@@ -313,7 +314,7 @@ async def show_start_menu(
kb.row(InlineKeyboardButton(text=SUPPORT, url=SUPPORT_CHAT_URL)) kb.row(InlineKeyboardButton(text=SUPPORT, url=SUPPORT_CHAT_URL))
if admin: if admin:
kb.row(InlineKeyboardButton(text="📊 Администратор", callback_data=AdminPanelCallback(action="admin").pack())) kb.row(InlineKeyboardButton(text=ADMIN_BTN, callback_data=AdminPanelCallback(action="admin").pack()))
try: try:
module_buttons = await run_hooks("start_menu", chat_id=message.chat.id, session=session) module_buttons = await run_hooks("start_menu", chat_id=message.chat.id, session=session)