fixing a critical rights vulnerability

This commit is contained in:
Vladless
2026-02-05 16:46:45 +03:00
parent 7af380538e
commit 0d3fc00bc9
12 changed files with 60 additions and 49 deletions
+14 -15
View File
@@ -1,7 +1,7 @@
from aiogram import F
from aiogram.fsm.context import FSMContext
from aiogram.types import CallbackQuery
from sqlalchemy import and_, delete, func, select, update
from sqlalchemy import and_, delete, select, update
from sqlalchemy.ext.asyncio import AsyncSession
from database import get_servers
@@ -22,7 +22,6 @@ from .keyboard import (
build_tariff_group_selection_for_servers_kb,
build_tariff_group_selection_kb,
build_tariff_selection_kb,
build_tariff_subgroup_selection_kb,
)
@@ -78,7 +77,7 @@ async def apply_tariff_group(callback: CallbackQuery, callback_data: AdminCluste
await callback.message.edit_text("❌ Произошла ошибка при установке тарифной группы.")
@router.callback_query(AdminClusterCallback.filter(F.action == "set_subgroup"))
@router.callback_query(AdminClusterCallback.filter(F.action == "set_subgroup"), IsAdminFilter())
async def show_servers_for_tariffs(
callback: CallbackQuery, callback_data: AdminClusterCallback, session: AsyncSession, state: FSMContext
):
@@ -105,7 +104,7 @@ async def show_servers_for_tariffs(
)
@router.callback_query(AdminClusterCallback.filter(F.action == "toggle_server_subgroup"))
@router.callback_query(AdminClusterCallback.filter(F.action == "toggle_server_subgroup"), IsAdminFilter())
async def toggle_server_for_tariffs(
callback: CallbackQuery, callback_data: AdminClusterCallback, session: AsyncSession, state: FSMContext
):
@@ -139,7 +138,7 @@ async def toggle_server_for_tariffs(
)
@router.callback_query(AdminClusterCallback.filter(F.action == "reset_subgroup_selection"))
@router.callback_query(AdminClusterCallback.filter(F.action == "reset_subgroup_selection"), IsAdminFilter())
async def reset_tariff_selection(
callback: CallbackQuery, callback_data: AdminClusterCallback, session: AsyncSession, state: FSMContext
):
@@ -156,7 +155,7 @@ async def reset_tariff_selection(
)
@router.callback_query(AdminClusterCallback.filter(F.action == "choose_subgroup"))
@router.callback_query(AdminClusterCallback.filter(F.action == "choose_subgroup"), IsAdminFilter())
async def choose_tariffs(
callback: CallbackQuery, callback_data: AdminClusterCallback, session: AsyncSession, state: FSMContext
):
@@ -207,7 +206,7 @@ async def choose_tariffs(
)
@router.callback_query(AdminClusterCallback.filter(F.action == "toggle_tariff"))
@router.callback_query(AdminClusterCallback.filter(F.action == "toggle_tariff"), IsAdminFilter())
async def toggle_tariff_selection(
callback: CallbackQuery, callback_data: AdminClusterCallback, session: AsyncSession, state: FSMContext
):
@@ -245,7 +244,7 @@ async def toggle_tariff_selection(
)
@router.callback_query(AdminClusterCallback.filter(F.action == "apply_tariffs"))
@router.callback_query(AdminClusterCallback.filter(F.action == "apply_tariffs"),IsAdminFilter())
async def apply_tariffs(
callback: CallbackQuery, callback_data: AdminClusterCallback, session: AsyncSession, state: FSMContext
):
@@ -334,7 +333,7 @@ async def apply_tariffs(
await callback.message.edit_text("❌ Произошла ошибка при назначении тарифов.")
@router.callback_query(AdminClusterCallback.filter(F.action == "reset_cluster_subgroups"))
@router.callback_query(AdminClusterCallback.filter(F.action == "reset_cluster_subgroups"), IsAdminFilter())
async def reset_cluster_subgroups(callback: CallbackQuery, callback_data: AdminClusterCallback, session: AsyncSession):
try:
cluster_name = callback_data.data
@@ -472,7 +471,7 @@ async def handle_attach_tariff_menu(callback: CallbackQuery, session: AsyncSessi
)
@router.callback_query(AdminClusterCallback.filter(F.action == "set_group"))
@router.callback_query(AdminClusterCallback.filter(F.action == "set_group"), IsAdminFilter())
async def show_servers_for_group(
callback: CallbackQuery, callback_data: AdminClusterCallback, session: AsyncSession, state: FSMContext
):
@@ -487,7 +486,7 @@ async def show_servers_for_group(
)
@router.callback_query(AdminClusterCallback.filter(F.action == "toggle_server_group"))
@router.callback_query(AdminClusterCallback.filter(F.action == "toggle_server_group"), IsAdminFilter())
async def toggle_server_for_group(
callback: CallbackQuery, callback_data: AdminClusterCallback, session: AsyncSession, state: FSMContext
):
@@ -521,7 +520,7 @@ async def toggle_server_for_group(
)
@router.callback_query(AdminClusterCallback.filter(F.action == "reset_group_selection"))
@router.callback_query(AdminClusterCallback.filter(F.action == "reset_group_selection"), IsAdminFilter())
async def reset_group_selection(
callback: CallbackQuery, callback_data: AdminClusterCallback, session: AsyncSession, state: FSMContext
):
@@ -535,7 +534,7 @@ async def reset_group_selection(
)
@router.callback_query(AdminClusterCallback.filter(F.action == "choose_group"))
@router.callback_query(AdminClusterCallback.filter(F.action == "choose_group"), IsAdminFilter())
async def choose_group(
callback: CallbackQuery, callback_data: AdminClusterCallback, session: AsyncSession, state: FSMContext
):
@@ -553,7 +552,7 @@ async def choose_group(
)
@router.callback_query(AdminClusterCallback.filter(F.action == "apply_group_to_servers"))
@router.callback_query(AdminClusterCallback.filter(F.action == "apply_group_to_servers"), IsAdminFilter())
async def apply_group_to_servers(
callback: CallbackQuery, callback_data: AdminClusterCallback, session: AsyncSession, state: FSMContext
):
@@ -609,7 +608,7 @@ async def apply_group_to_servers(
await callback.message.edit_text("❌ Произошла ошибка при назначении группы.")
@router.callback_query(AdminClusterCallback.filter(F.action == "reset_cluster_groups"))
@router.callback_query(AdminClusterCallback.filter(F.action == "reset_cluster_groups"), IsAdminFilter())
async def reset_cluster_groups(callback: CallbackQuery, callback_data: AdminClusterCallback, session: AsyncSession):
try:
cluster_name = callback_data.data
+3 -2
View File
@@ -3,6 +3,7 @@ from aiogram.fsm.context import FSMContext
from aiogram.types import CallbackQuery
from sqlalchemy import delete, update
from sqlalchemy.ext.asyncio import AsyncSession
from filters.admin import IsAdminFilter
from config import USE_COUNTRY_SELECTION
from core.bootstrap import MODES_CONFIG
@@ -13,7 +14,7 @@ from ..panel.keyboard import build_admin_back_kb
from .base import router
@router.callback_query(F.data.startswith("transfer_to_server|"))
@router.callback_query(F.data.startswith("transfer_to_server|"), IsAdminFilter())
async def handle_server_transfer(callback_query: CallbackQuery, state: FSMContext, session: AsyncSession):
try:
data = callback_query.data.split("|")
@@ -55,7 +56,7 @@ async def handle_server_transfer(callback_query: CallbackQuery, state: FSMContex
await state.clear()
@router.callback_query(F.data.startswith("transfer_to_cluster|"))
@router.callback_query(F.data.startswith("transfer_to_cluster|"), IsAdminFilter())
async def handle_cluster_transfer(callback_query: CallbackQuery, state: FSMContext, session: AsyncSession):
try:
data = callback_query.data.split("|")
+1 -1
View File
@@ -431,7 +431,7 @@ async def update_coupons_list(message, session: Any, page: int = 1):
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):
if not INLINE_MODE:
return
+11 -10
View File
@@ -8,6 +8,7 @@ from aiogram.utils.keyboard import InlineKeyboardBuilder
from sqlalchemy import delete, func, select
from sqlalchemy.ext.asyncio import AsyncSession
from filters.admin import IsAdminFilter
from database.models import Gift, GiftUsage
from database.tariffs import create_subgroup_hash, find_subgroup_by_hash, get_tariffs
from handlers.utils import edit_or_send_message, format_days, format_months
@@ -25,14 +26,14 @@ class GiftCreationState(StatesGroup):
waiting_for_limit_input_or_unlimited = State()
@router.callback_query(AdminPanelCallback.filter(F.action == "gifts"))
@router.callback_query(AdminPanelCallback.filter(F.action == "gifts"), IsAdminFilter())
async def admin_gift_menu(callback: CallbackQuery):
await callback.message.edit_text(
text="🎁 <b>Подарки</b>\nВыберите, что хотите сделать:", reply_markup=build_admin_gifts_kb()
)
@router.callback_query(F.data == "admin_gift_create")
@router.callback_query(F.data == "admin_gift_create", IsAdminFilter())
async def admin_create_gift_step1(callback: CallbackQuery, session: AsyncSession):
tariffs_data = await get_tariffs(session, group_code="gifts", with_subgroup_weights=True)
tariffs = [t for t in tariffs_data["tariffs"] if t.get("is_active")]
@@ -78,7 +79,7 @@ async def admin_create_gift_step1(callback: CallbackQuery, session: AsyncSession
await callback.message.edit_text("🎁 Выберите тариф для подарка:", reply_markup=builder.as_markup())
@router.callback_query(F.data.startswith("admin_gift_subgroup|"))
@router.callback_query(F.data.startswith("admin_gift_subgroup|"), IsAdminFilter())
async def admin_gift_show_tariffs_in_subgroup(callback: CallbackQuery, session: AsyncSession):
try:
subgroup_hash = callback.data.split("|", 1)[1]
@@ -121,7 +122,7 @@ async def admin_gift_show_tariffs_in_subgroup(callback: CallbackQuery, session:
await callback.message.answer("❌ Произошла ошибка при отображении тарифов.")
@router.callback_query(F.data.startswith("admin_gift_select|"))
@router.callback_query(F.data.startswith("admin_gift_select|"), IsAdminFilter())
async def handle_tariff_selection(callback: CallbackQuery, state: FSMContext):
tariff_id = int(callback.data.split("|")[1])
await state.update_data(tariff_id=tariff_id)
@@ -134,7 +135,7 @@ async def handle_tariff_selection(callback: CallbackQuery, state: FSMContext):
)
@router.callback_query(F.data == "gift_limit_unlimited")
@router.callback_query(F.data == "gift_limit_unlimited", IsAdminFilter())
async def handle_unlimited_gift(callback: CallbackQuery, state: FSMContext, bot: Bot):
from handlers.payments.gift import finalize_gift
@@ -144,7 +145,7 @@ async def handle_unlimited_gift(callback: CallbackQuery, state: FSMContext, bot:
await finalize_gift(callback.message, session, bot, data, is_unlimited=True)
@router.message(GiftCreationState.waiting_for_limit_input_or_unlimited)
@router.message(GiftCreationState.waiting_for_limit_input_or_unlimited, IsAdminFilter())
async def handle_limited_gift_input(message: types.Message, session: AsyncSession, state: FSMContext, bot: Bot):
from handlers.payments.gift import finalize_gift
@@ -162,12 +163,12 @@ async def handle_limited_gift_input(message: types.Message, session: AsyncSessio
await finalize_gift(message, session, bot, data, is_unlimited=False)
@router.callback_query(F.data == "admin_gifts_all")
@router.callback_query(F.data == "admin_gifts_all", IsAdminFilter())
async def show_gifts_page(callback: CallbackQuery, session: AsyncSession):
await show_gift_list(callback, session, page=1)
@router.callback_query(F.data.startswith("gifts_page|"))
@router.callback_query(F.data.startswith("gifts_page|"), IsAdminFilter())
async def paginate_gifts(callback: CallbackQuery, session: AsyncSession):
page = int(callback.data.split("|")[1])
await show_gift_list(callback, session, page)
@@ -195,7 +196,7 @@ async def show_gift_list(callback: CallbackQuery, session: AsyncSession, page: i
)
@router.callback_query(F.data.startswith("gift_view|"))
@router.callback_query(F.data.startswith("gift_view|"), IsAdminFilter())
async def view_gift(callback: CallbackQuery, session: AsyncSession):
gift_id = callback.data.split("|")[1]
@@ -233,7 +234,7 @@ async def view_gift(callback: CallbackQuery, session: AsyncSession):
await callback.message.edit_text(text, reply_markup=builder.as_markup())
@router.callback_query(F.data.startswith("gift_delete|"))
@router.callback_query(F.data.startswith("gift_delete|"), IsAdminFilter())
async def delete_gift(callback: CallbackQuery, session: AsyncSession):
gift_id = callback.data.split("|")[1]
+9 -8
View File
@@ -10,6 +10,7 @@ from sqlalchemy import delete, select
from sqlalchemy.ext.asyncio import AsyncSession
from database.models import Admin
from filters.admin import IsAdminFilter
from . import router
from .keyboard import (
@@ -26,14 +27,14 @@ class AdminState(StatesGroup):
waiting_for_tg_id = State()
@router.callback_query(AdminPanelCallback.filter(F.action == "admins"))
@router.callback_query(AdminPanelCallback.filter(F.action == "admins"), IsAdminFilter())
async def show_admins(callback: CallbackQuery, session: AsyncSession):
result = await session.execute(select(Admin.tg_id, Admin.role))
admins = result.all()
await callback.message.edit_text("👑 <b>Список админов</b>", reply_markup=build_admins_kb(admins))
@router.callback_query(AdminPanelCallback.filter(F.action == "add_admin"))
@router.callback_query(AdminPanelCallback.filter(F.action == "add_admin"), IsAdminFilter())
async def prompt_new_admin(callback: CallbackQuery, state: FSMContext):
await callback.message.edit_text(
"Введите <code>tg_id</code> нового админа:", reply_markup=build_admin_back_kb_to_admins()
@@ -41,7 +42,7 @@ async def prompt_new_admin(callback: CallbackQuery, state: FSMContext):
await state.set_state(AdminState.waiting_for_tg_id)
@router.message(AdminState.waiting_for_tg_id)
@router.message(AdminState.waiting_for_tg_id, IsAdminFilter())
async def save_new_admin(message: Message, session: AsyncSession, state: FSMContext):
try:
tg_id = int(message.text.strip())
@@ -60,7 +61,7 @@ async def save_new_admin(message: Message, session: AsyncSession, state: FSMCont
await state.clear()
@router.callback_query(AdminPanelCallback.filter(F.action.startswith("admin_menu|")))
@router.callback_query(AdminPanelCallback.filter(F.action.startswith("admin_menu|")), IsAdminFilter())
async def open_admin_menu(callback: CallbackQuery, callback_data: AdminPanelCallback, session: AsyncSession):
tg_id = int(callback_data.action.split("|")[1])
@@ -73,7 +74,7 @@ async def open_admin_menu(callback: CallbackQuery, callback_data: AdminPanelCall
)
@router.callback_query(AdminPanelCallback.filter(F.action.startswith("generate_token|")))
@router.callback_query(AdminPanelCallback.filter(F.action.startswith("generate_token|")), IsAdminFilter())
async def generate_token(callback: CallbackQuery, callback_data: AdminPanelCallback, session: AsyncSession):
tg_id = int(callback_data.action.split("|")[1])
@@ -102,7 +103,7 @@ async def generate_token(callback: CallbackQuery, callback_data: AdminPanelCallb
pass
@router.callback_query(AdminPanelCallback.filter(F.action.startswith("edit_role|")))
@router.callback_query(AdminPanelCallback.filter(F.action.startswith("edit_role|")), IsAdminFilter())
async def edit_admin_role(callback: CallbackQuery, callback_data: AdminPanelCallback):
tg_id = int(callback_data.action.split("|")[1])
await callback.message.edit_text(
@@ -110,7 +111,7 @@ async def edit_admin_role(callback: CallbackQuery, callback_data: AdminPanelCall
)
@router.callback_query(AdminPanelCallback.filter(F.action.startswith("set_role|")))
@router.callback_query(AdminPanelCallback.filter(F.action.startswith("set_role|")), IsAdminFilter())
async def set_admin_role(callback: CallbackQuery, callback_data: AdminPanelCallback, session: AsyncSession):
try:
_, tg_id_str, role = callback_data.action.split("|")
@@ -141,7 +142,7 @@ async def set_admin_role(callback: CallbackQuery, callback_data: AdminPanelCallb
)
@router.callback_query(AdminPanelCallback.filter(F.action.startswith("delete_admin|")))
@router.callback_query(AdminPanelCallback.filter(F.action.startswith("delete_admin|")), IsAdminFilter())
async def delete_admin(callback: CallbackQuery, callback_data: AdminPanelCallback, session: AsyncSession):
tg_id = int(callback_data.action.split("|")[1])
+6 -5
View File
@@ -11,6 +11,7 @@ from aiogram.fsm.state import State, StatesGroup
from aiogram.types import CallbackQuery, Message
from config import DB_NAME, DB_PASSWORD, DB_USER, PG_HOST, PG_PORT
from filters.admin import IsAdminFilter
from logger import logger
from . import router
@@ -21,7 +22,7 @@ class DatabaseState(StatesGroup):
waiting_for_backup_file = State()
@router.callback_query(AdminPanelCallback.filter(F.action == "database"))
@router.callback_query(AdminPanelCallback.filter(F.action == "database"), IsAdminFilter())
async def handle_database_menu(callback: CallbackQuery):
await callback.message.edit_text(
text="🗄 <b>Управление базой данных</b>",
@@ -29,7 +30,7 @@ async def handle_database_menu(callback: CallbackQuery):
)
@router.callback_query(AdminPanelCallback.filter(F.action == "restore_db"))
@router.callback_query(AdminPanelCallback.filter(F.action == "restore_db"), IsAdminFilter())
async def prompt_restore_db(callback: CallbackQuery, state: FSMContext):
await callback.message.edit_text(
"📂 Отправьте файл резервной копии (.sql), чтобы восстановить базу данных.\n"
@@ -39,7 +40,7 @@ async def prompt_restore_db(callback: CallbackQuery, state: FSMContext):
await state.set_state(DatabaseState.waiting_for_backup_file)
@router.message(DatabaseState.waiting_for_backup_file)
@router.message(DatabaseState.waiting_for_backup_file, IsAdminFilter())
async def restore_database(message: Message, state: FSMContext, bot: Bot):
document = message.document
@@ -154,7 +155,7 @@ async def restore_database(message: Message, state: FSMContext, bot: Bot):
pass
@router.callback_query(AdminPanelCallback.filter(F.action == "export_db"))
@router.callback_query(AdminPanelCallback.filter(F.action == "export_db"), IsAdminFilter())
async def handle_export_db(callback: CallbackQuery):
await callback.message.edit_text(
"📤 Выберите панель, с которой требуется получить данные:\n\n"
@@ -163,6 +164,6 @@ async def handle_export_db(callback: CallbackQuery):
)
@router.callback_query(AdminPanelCallback.filter(F.action == "back_to_db_menu"))
@router.callback_query(AdminPanelCallback.filter(F.action == "back_to_db_menu"), IsAdminFilter())
async def back_to_database_menu(callback: CallbackQuery):
await callback.message.edit_text("📦 Управление базой данных:", reply_markup=build_database_kb())
+4 -3
View File
@@ -6,6 +6,7 @@ from aiogram.fsm.state import State, StatesGroup
from aiogram.types import CallbackQuery, Message
from aiogram.utils.keyboard import InlineKeyboardBuilder
from filters.admin import IsAdminFilter
from logger import logger
from ..panel.keyboard import build_admin_back_kb
@@ -18,7 +19,7 @@ class FileUploadState(StatesGroup):
waiting_for_file = State()
@router.callback_query(AdminPanelCallback.filter(F.action == "upload_file"))
@router.callback_query(AdminPanelCallback.filter(F.action == "upload_file"), IsAdminFilter())
async def prompt_for_file_upload(callback: CallbackQuery, state: FSMContext):
text = (
"📤 <b>Загрузка файла</b>\n\n"
@@ -41,7 +42,7 @@ async def prompt_for_file_upload(callback: CallbackQuery, state: FSMContext):
await state.set_state(FileUploadState.waiting_for_target)
@router.callback_query(F.data.startswith("upload_target:"), FileUploadState.waiting_for_target)
@router.callback_query(F.data.startswith("upload_target:"), FileUploadState.waiting_for_target, IsAdminFilter())
async def select_upload_target(callback: CallbackQuery, state: FSMContext):
target = callback.data.split(":", 1)[1]
if target not in {"root", "handlers"}:
@@ -61,7 +62,7 @@ async def select_upload_target(callback: CallbackQuery, state: FSMContext):
await state.set_state(FileUploadState.waiting_for_file)
@router.message(FileUploadState.waiting_for_file, F.document)
@router.message(FileUploadState.waiting_for_file, F.document, IsAdminFilter())
async def handle_admin_file_upload(message: Message, state: FSMContext):
document = message.document
file_name = document.file_name
+4 -3
View File
@@ -5,6 +5,7 @@ from aiogram.types import CallbackQuery, Message
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from filters.admin import IsAdminFilter
from database.models import Key
from handlers.keys.operations import update_subscription
from logger import logger
@@ -17,7 +18,7 @@ class Import3xuiStates(StatesGroup):
waiting_for_file = State()
@router.callback_query(AdminPanelCallback.filter(F.action == "request_3xui_file"))
@router.callback_query(AdminPanelCallback.filter(F.action == "request_3xui_file"), IsAdminFilter())
async def prompt_for_3xui_file(callback: CallbackQuery, state: FSMContext):
await callback.message.edit_text(
"📂 Пришлите файл базы данных <code>x-ui.db</code> для восстановления подписок и клиентов.\n\n"
@@ -29,7 +30,7 @@ async def prompt_for_3xui_file(callback: CallbackQuery, state: FSMContext):
await state.set_state(Import3xuiStates.waiting_for_file)
@router.message(Import3xuiStates.waiting_for_file, F.document)
@router.message(Import3xuiStates.waiting_for_file, F.document, IsAdminFilter())
async def handle_3xui_db_upload(message: Message, state: FSMContext, session: AsyncSession):
file = message.document
@@ -64,7 +65,7 @@ async def handle_3xui_db_upload(message: Message, state: FSMContext, session: As
await state.clear()
@router.callback_query(AdminPanelCallback.filter(F.action == "resync_after_import"))
@router.callback_query(AdminPanelCallback.filter(F.action == "resync_after_import"), IsAdminFilter())
async def handle_resync_after_import(callback: CallbackQuery, session: AsyncSession):
await callback.answer("🔁 Начинаю перевыпуск подписок...")
@@ -13,6 +13,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
from config import REMNAWAVE_LOGIN, REMNAWAVE_PASSWORD
from database.models import Key, Server, User
from filters.admin import IsAdminFilter
from logger import logger
from panels.remnawave import RemnawaveAPI
@@ -54,7 +55,7 @@ def extract_tg_id_from_user_payload(user: dict) -> int | None:
return tg_id
@router.callback_query(AdminPanelCallback.filter(F.action == "export_remnawave"))
@router.callback_query(AdminPanelCallback.filter(F.action == "export_remnawave"), IsAdminFilter())
async def show_remnawave_clients(callback: CallbackQuery, session: AsyncSession):
result = await session.execute(select(Server).where(Server.panel_type == "remnawave", Server.enabled.is_(True)))
servers = result.scalars().all()
+1 -1
View File
@@ -28,7 +28,7 @@ async def handle_management(callback_query: CallbackQuery, session: AsyncSession
)
@router.callback_query(AdminPanelCallback.filter(F.action == "toggle_maintenance"))
@router.callback_query(AdminPanelCallback.filter(F.action == "toggle_maintenance"), IsAdminFilter())
async def toggle_maintenance_mode(callback: CallbackQuery, session: AsyncSession):
tg_id = callback.from_user.id
@@ -5,12 +5,15 @@ from aiogram.types import CallbackQuery, InlineKeyboardButton, InlineKeyboardMar
from sqlalchemy.ext.asyncio import AsyncSession
from core.bootstrap import MONEY_CONFIG, update_money_config
from filters.admin import IsAdminFilter
from ..panel.keyboard import AdminPanelCallback
from .keyboard import MONEY_FIELDS, build_settings_money_kb
router = Router()
router.callback_query.filter(IsAdminFilter())
router.message.filter(IsAdminFilter())
class MoneySettingsState(StatesGroup):
@@ -6,11 +6,13 @@ from aiogram.utils.keyboard import InlineKeyboardBuilder
from sqlalchemy.ext.asyncio import AsyncSession
from core.settings.tariffs_config import TARIFFS_CONFIG, update_tariffs_config
from filters.admin import IsAdminFilter
from ..panel.keyboard import AdminPanelCallback
router = Router()
router.callback_query.filter(IsAdminFilter())
PACK_MODES = ["", "traffic", "devices", "all"]