redesign/bug fixes/admin trial
This commit is contained in:
@@ -66,14 +66,16 @@ async def extend_client_key(xui, email: str, new_expiry_time: int, client_id: st
|
||||
logger.info(
|
||||
f"Обновление ключа клиента {client.email} с ID {client.id} до нового времени: {new_expiry_time}"
|
||||
)
|
||||
|
||||
|
||||
client.id = client_id
|
||||
client.expiry_time = new_expiry_time
|
||||
|
||||
|
||||
if TOTAL_GB > 0:
|
||||
client.total_gb = TOTAL_GB
|
||||
logger.info(f"Установлен объем трафика для клиента {client.email}: {TOTAL_GB} ГБ")
|
||||
|
||||
logger.info(
|
||||
f"Установлен объем трафика для клиента {client.email}: {TOTAL_GB} ГБ"
|
||||
)
|
||||
|
||||
await xui.client.update(client.id, client)
|
||||
logger.info(
|
||||
f"Ключ клиента {client.email} успешно продлён до {new_expiry_time}."
|
||||
@@ -83,7 +85,6 @@ async def extend_client_key(xui, email: str, new_expiry_time: int, client_id: st
|
||||
logger.error(f"Ошибка при обновлении клиента с email {email}: {e}")
|
||||
|
||||
|
||||
|
||||
async def delete_client(
|
||||
xui,
|
||||
email: str,
|
||||
|
||||
+4
-6
@@ -1,6 +1,7 @@
|
||||
from datetime import datetime
|
||||
from loguru import logger
|
||||
|
||||
import asyncpg
|
||||
from loguru import logger
|
||||
|
||||
from config import DATABASE_URL
|
||||
|
||||
@@ -74,12 +75,11 @@ async def init_db():
|
||||
|
||||
await conn.close()
|
||||
|
||||
|
||||
async def restore_trial(tg_id: int):
|
||||
conn = await asyncpg.connect(DATABASE_URL)
|
||||
try:
|
||||
await conn.execute(
|
||||
"UPDATE connections SET trial = 0 WHERE tg_id = $1", tg_id
|
||||
)
|
||||
await conn.execute("UPDATE connections SET trial = 0 WHERE tg_id = $1", tg_id)
|
||||
except Exception as e:
|
||||
|
||||
logger.error(f"Ошибка при установке значения триала: {e}")
|
||||
@@ -87,8 +87,6 @@ async def restore_trial(tg_id: int):
|
||||
await conn.close()
|
||||
|
||||
|
||||
|
||||
|
||||
async def add_connection(tg_id: int, balance: float = 0.0, trial: int = 0):
|
||||
conn = await asyncpg.connect(DATABASE_URL)
|
||||
await conn.execute(
|
||||
|
||||
@@ -3,12 +3,12 @@ from aiogram import Router, types
|
||||
from aiogram.filters import Command
|
||||
from aiogram.fsm.context import FSMContext
|
||||
from aiogram.fsm.state import State, StatesGroup
|
||||
from handlers.filters.admin import IsAdminFilter
|
||||
from loguru import logger
|
||||
|
||||
from bot import bot
|
||||
from config import DATABASE_URL
|
||||
from database import add_balance_to_client, check_connection_exists
|
||||
from handlers.filters.admin import IsAdminFilter
|
||||
from handlers.texts import TRIAL
|
||||
|
||||
router = Router()
|
||||
|
||||
@@ -3,18 +3,17 @@ from datetime import datetime
|
||||
|
||||
import asyncpg
|
||||
from aiogram import F, Router, types
|
||||
|
||||
from aiogram.filters import Command
|
||||
from aiogram.fsm.context import FSMContext
|
||||
from aiogram.fsm.state import State, StatesGroup
|
||||
from aiogram.types import CallbackQuery, InlineKeyboardButton, Message
|
||||
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
||||
from handlers.filters.admin import IsAdminFilter
|
||||
|
||||
from backup import backup_database
|
||||
from bot import bot
|
||||
from config import DATABASE_URL, ADMIN_ID
|
||||
from config import DATABASE_URL
|
||||
from handlers.admin.admin_commands import send_message_to_all_clients
|
||||
from handlers.filters.admin import IsAdminFilter
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
@@ -7,13 +7,13 @@ from aiogram.fsm.context import FSMContext
|
||||
from aiogram.fsm.state import State, StatesGroup
|
||||
from aiogram.types import CallbackQuery, InlineKeyboardButton
|
||||
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
||||
from handlers.filters.admin import IsAdminFilter
|
||||
from loguru import logger
|
||||
|
||||
from bot import bot
|
||||
from config import DATABASE_URL, SERVERS
|
||||
from database import get_client_id_by_email, update_key_expiry, restore_trial
|
||||
from database import get_client_id_by_email, restore_trial, update_key_expiry
|
||||
from handlers.admin.admin_panel import back_to_admin_menu
|
||||
from handlers.filters.admin import IsAdminFilter
|
||||
from handlers.keys.key_utils import delete_key_from_server, renew_server_key
|
||||
from handlers.utils import sanitize_key_name
|
||||
|
||||
@@ -101,12 +101,13 @@ async def handle_restore_trial(callback_query: types.CallbackQuery):
|
||||
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.row(
|
||||
InlineKeyboardButton(text="🔙 Назад в меню администратора", callback_data="back_to_user_editor")
|
||||
InlineKeyboardButton(
|
||||
text="🔙 Назад в меню администратора", callback_data="back_to_user_editor"
|
||||
)
|
||||
)
|
||||
|
||||
await callback_query.message.edit_text(
|
||||
"✅ Триал успешно восстановлен.",
|
||||
reply_markup=builder.as_markup()
|
||||
"✅ Триал успешно восстановлен.", reply_markup=builder.as_markup()
|
||||
)
|
||||
|
||||
|
||||
@@ -261,7 +262,10 @@ async def handle_key_name_input(message: types.Message, state: FSMContext):
|
||||
if not user_records:
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.row(
|
||||
InlineKeyboardButton(text="🔙 Назад в меню администратора", callback_data="back_to_user_editor")
|
||||
InlineKeyboardButton(
|
||||
text="🔙 Назад в меню администратора",
|
||||
callback_data="back_to_user_editor",
|
||||
)
|
||||
)
|
||||
|
||||
await message.reply(
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import asyncio
|
||||
import uuid
|
||||
from datetime import datetime, timedelta
|
||||
from loguru import logger
|
||||
|
||||
import asyncpg
|
||||
from aiogram import F, Router
|
||||
from aiogram.fsm.context import FSMContext
|
||||
from aiogram.fsm.state import State, StatesGroup
|
||||
from aiogram.types import CallbackQuery, InlineKeyboardButton, InlineKeyboardMarkup, Message
|
||||
from loguru import logger
|
||||
|
||||
from bot import bot, dp
|
||||
from config import DATABASE_URL, PUBLIC_LINK, SERVERS, DOWNLOAD_ANDROID, DOWNLOAD_IOS, CONNECT_ANDROID, CONNECT_IOS
|
||||
from config import CONNECT_ANDROID, CONNECT_IOS, DATABASE_URL, DOWNLOAD_ANDROID, DOWNLOAD_IOS, PUBLIC_LINK, SERVERS
|
||||
from database import add_connection, get_balance, store_key, update_balance
|
||||
from handlers.instructions.instructions import send_instructions
|
||||
from handlers.keys.key_utils import create_key_on_server
|
||||
@@ -107,13 +107,16 @@ async def confirm_create_new_key(callback_query: CallbackQuery, state: FSMContex
|
||||
|
||||
logger.info(f"Balance for user {tg_id} is sufficient. Asking for device name.")
|
||||
|
||||
await callback_query.message.edit_text("🔑 Пожалуйста, введите имя подключаемого устройства:")
|
||||
await callback_query.message.edit_text(
|
||||
"🔑 Пожалуйста, введите имя подключаемого устройства:"
|
||||
)
|
||||
await state.set_state(Form.waiting_for_key_name)
|
||||
logger.info(f"State set to waiting_for_key_name for user {tg_id}")
|
||||
await state.update_data(creating_new_key=True)
|
||||
|
||||
await callback_query.answer()
|
||||
|
||||
|
||||
@dp.callback_query(F.data == "cancel_create_key")
|
||||
async def cancel_create_key(callback_query: CallbackQuery, state: FSMContext):
|
||||
await process_callback_view_profile(callback_query, state)
|
||||
@@ -136,7 +139,9 @@ async def handle_key_name_input(message: Message, state: FSMContext):
|
||||
|
||||
conn = await asyncpg.connect(DATABASE_URL)
|
||||
try:
|
||||
logger.info(f"Checking if key name '{key_name}' already exists in the database.")
|
||||
logger.info(
|
||||
f"Checking if key name '{key_name}' already exists in the database."
|
||||
)
|
||||
existing_key = await conn.fetchrow(
|
||||
"SELECT * FROM keys WHERE email = $1", key_name.lower()
|
||||
)
|
||||
@@ -145,7 +150,9 @@ async def handle_key_name_input(message: Message, state: FSMContext):
|
||||
tg_id,
|
||||
"❌ Упс! Это имя уже используется. Выберите другое уникальное название для ключа.",
|
||||
)
|
||||
logger.warning(f"Key name '{key_name}' already exists in the database for user {tg_id}.")
|
||||
logger.warning(
|
||||
f"Key name '{key_name}' already exists in the database for user {tg_id}."
|
||||
)
|
||||
await state.set_state(Form.waiting_for_key_name)
|
||||
return
|
||||
finally:
|
||||
@@ -206,9 +213,7 @@ async def handle_key_name_input(message: Message, state: FSMContext):
|
||||
url=f"{CONNECT_ANDROID}{public_link}",
|
||||
)
|
||||
|
||||
button_download_ios = InlineKeyboardButton(
|
||||
text="🍏 Скачать", url=DOWNLOAD_IOS
|
||||
)
|
||||
button_download_ios = InlineKeyboardButton(text="🍏 Скачать", url=DOWNLOAD_IOS)
|
||||
button_download_android = InlineKeyboardButton(
|
||||
text="🤖 Скачать",
|
||||
url=DOWNLOAD_ANDROID,
|
||||
@@ -272,7 +277,6 @@ async def handle_key_name_input(message: Message, state: FSMContext):
|
||||
await state.clear()
|
||||
|
||||
|
||||
|
||||
@dp.callback_query(F.data == "instructions")
|
||||
async def handle_instructions(callback_query: CallbackQuery):
|
||||
await send_instructions(callback_query)
|
||||
|
||||
@@ -9,7 +9,7 @@ from aiogram.types import BufferedInputFile
|
||||
from loguru import logger
|
||||
|
||||
from bot import bot
|
||||
from config import DATABASE_URL, PUBLIC_LINK, SERVERS, DOWNLOAD_ANDROID, DOWNLOAD_IOS, CONNECT_ANDROID, CONNECT_IOS
|
||||
from config import CONNECT_ANDROID, CONNECT_IOS, DATABASE_URL, DOWNLOAD_ANDROID, DOWNLOAD_IOS, PUBLIC_LINK, SERVERS
|
||||
from database import delete_key, get_balance, store_key, update_balance, update_key_expiry
|
||||
from handlers.keys.key_utils import delete_key_from_db, delete_key_from_server, renew_server_key, update_key_on_server
|
||||
from handlers.texts import INSUFFICIENT_FUNDS_MSG, KEY_NOT_FOUND_MSG, NO_KEYS, PLAN_SELECTION_MSG, RENEWAL_PLANS, SUCCESS_RENEWAL_MSG, key_message
|
||||
@@ -69,7 +69,9 @@ async def process_callback_view_keys(callback_query: types.CallbackQuery):
|
||||
with open(image_path, "rb") as image_file:
|
||||
await bot.send_photo(
|
||||
chat_id=tg_id,
|
||||
photo=BufferedInputFile(image_file.read(), filename="pic_keys.jpg"),
|
||||
photo=BufferedInputFile(
|
||||
image_file.read(), filename="pic_keys.jpg"
|
||||
),
|
||||
caption=response_message,
|
||||
parse_mode="HTML",
|
||||
reply_markup=inline_keyboard,
|
||||
@@ -108,7 +110,9 @@ async def process_callback_view_keys(callback_query: types.CallbackQuery):
|
||||
with open(image_path, "rb") as image_file:
|
||||
await bot.send_photo(
|
||||
chat_id=tg_id,
|
||||
photo=BufferedInputFile(image_file.read(), filename="pic_keys.jpg"),
|
||||
photo=BufferedInputFile(
|
||||
image_file.read(), filename="pic_keys.jpg"
|
||||
),
|
||||
caption=response_message,
|
||||
parse_mode="HTML",
|
||||
reply_markup=keyboard,
|
||||
@@ -130,7 +134,6 @@ async def process_callback_view_keys(callback_query: types.CallbackQuery):
|
||||
await callback_query.answer()
|
||||
|
||||
|
||||
|
||||
@router.callback_query(F.data.startswith("view_key|"))
|
||||
async def process_callback_view_key(callback_query: types.CallbackQuery):
|
||||
tg_id = callback_query.from_user.id
|
||||
|
||||
+23
-10
@@ -217,7 +217,7 @@ async def notify_24h_keys(
|
||||
async def handle_expired_keys(bot: Bot, conn: asyncpg.Connection, current_time: float):
|
||||
logger.info("Проверка истекших ключей...")
|
||||
|
||||
adjusted_current_time = current_time + (3 * 60 * 60 * 1000)
|
||||
adjusted_current_time = current_time + (3 * 60 * 60 * 1000)
|
||||
expiring_keys = await conn.fetch(
|
||||
"""
|
||||
SELECT tg_id, client_id, expiry_time, email FROM keys
|
||||
@@ -271,22 +271,32 @@ async def handle_expired_keys(bot: Bot, conn: asyncpg.Connection, current_time:
|
||||
)
|
||||
if not success:
|
||||
all_success = False
|
||||
logger.error(f"Не удалось продлить ключ для пользователя {tg_id} на сервере {server_id}.")
|
||||
logger.error(
|
||||
f"Не удалось продлить ключ для пользователя {tg_id} на сервере {server_id}."
|
||||
)
|
||||
|
||||
if all_success:
|
||||
try:
|
||||
await bot.send_message(tg_id, KEY_RENEWED, reply_markup=keyboard)
|
||||
await bot.send_message(
|
||||
tg_id, KEY_RENEWED, reply_markup=keyboard
|
||||
)
|
||||
logger.info(f"Ключ для пользователя {tg_id} успешно продлен.")
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка при отправке уведомления пользователю {tg_id}: {e}")
|
||||
logger.error(
|
||||
f"Ошибка при отправке уведомления пользователю {tg_id}: {e}"
|
||||
)
|
||||
|
||||
else:
|
||||
try:
|
||||
await bot.send_message(tg_id, message_expired, reply_markup=keyboard)
|
||||
await bot.send_message(
|
||||
tg_id, message_expired, reply_markup=keyboard
|
||||
)
|
||||
except Exception as e:
|
||||
if "chat not found" in str(e):
|
||||
logger.warning(f"Чат для клиента {tg_id} не найден. Пропуск отправки сообщения.")
|
||||
|
||||
logger.warning(
|
||||
f"Чат для клиента {tg_id} не найден. Пропуск отправки сообщения."
|
||||
)
|
||||
|
||||
await delete_key(client_id)
|
||||
|
||||
for server_id in SERVERS:
|
||||
@@ -297,12 +307,15 @@ async def handle_expired_keys(bot: Bot, conn: asyncpg.Connection, current_time:
|
||||
)
|
||||
success = await delete_client(xui, email, client_id)
|
||||
if success:
|
||||
logger.info(f"Ключ для клиента {tg_id} успешно удален с сервера {server_id}.")
|
||||
logger.info(
|
||||
f"Ключ для клиента {tg_id} успешно удален с сервера {server_id}."
|
||||
)
|
||||
else:
|
||||
logger.error(f"Не удалось удалить ключ для клиента {tg_id} на сервере {server_id}.")
|
||||
logger.error(
|
||||
f"Не удалось удалить ключ для клиента {tg_id} на сервере {server_id}."
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка при обработке ключа для клиента {tg_id}: {e}")
|
||||
|
||||
await asyncio.sleep(1)
|
||||
|
||||
|
||||
@@ -8,11 +8,9 @@ import requests
|
||||
from aiogram import Router, types
|
||||
from aiogram.fsm.context import FSMContext
|
||||
from aiogram.fsm.state import State, StatesGroup
|
||||
from aiogram.types import InlineKeyboardButton
|
||||
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
|
||||
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
||||
from aiohttp import web
|
||||
from aiogram.types import InlineKeyboardMarkup
|
||||
|
||||
|
||||
from bot import bot
|
||||
from config import FREEKASSA_API_KEY, FREEKASSA_SHOP_ID
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
from aiogram import F, Router, types
|
||||
from aiogram.fsm.context import FSMContext
|
||||
from aiogram.fsm.state import State, StatesGroup
|
||||
from aiogram.types import InlineKeyboardButton
|
||||
from aiogram.types import InlineKeyboardButton, LabeledPrice
|
||||
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
||||
from loguru import logger
|
||||
|
||||
from aiogram.types import LabeledPrice
|
||||
|
||||
from bot import bot
|
||||
from config import RUB_TO_XTR
|
||||
from database import add_connection, check_connection_exists, get_key_count, update_balance
|
||||
|
||||
@@ -63,7 +63,7 @@ async def process_callback_pay_yookassa(
|
||||
|
||||
builder = InlineKeyboardBuilder()
|
||||
|
||||
for i in range(0, 4, 2):
|
||||
for i in range(0, 4, 2):
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=PAYMENT_OPTIONS[i]["text"],
|
||||
@@ -75,7 +75,7 @@ async def process_callback_pay_yookassa(
|
||||
),
|
||||
)
|
||||
|
||||
for i in range(4, len(PAYMENT_OPTIONS)):
|
||||
for i in range(4, len(PAYMENT_OPTIONS)):
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text=PAYMENT_OPTIONS[i]["text"],
|
||||
@@ -107,7 +107,6 @@ async def process_callback_pay_yookassa(
|
||||
await callback_query.answer()
|
||||
|
||||
|
||||
|
||||
@router.callback_query(F.data == "back_to_profile")
|
||||
async def back_to_profile_handler(
|
||||
callback_query: types.CallbackQuery, state: FSMContext
|
||||
|
||||
+3
-3
@@ -109,7 +109,9 @@ async def invite_handler(callback_query: types.CallbackQuery):
|
||||
with open(image_path, "rb") as image_file:
|
||||
await bot.send_photo(
|
||||
chat_id=chat_id,
|
||||
photo=BufferedInputFile(image_file.read(), filename="pic_invite.jpg"),
|
||||
photo=BufferedInputFile(
|
||||
image_file.read(), filename="pic_invite.jpg"
|
||||
),
|
||||
caption=invite_message,
|
||||
parse_mode="HTML",
|
||||
reply_markup=builder.as_markup(),
|
||||
@@ -132,8 +134,6 @@ async def invite_handler(callback_query: types.CallbackQuery):
|
||||
await callback_query.answer()
|
||||
|
||||
|
||||
|
||||
|
||||
@router.callback_query(F.data == "view_profile")
|
||||
async def view_profile_handler(callback_query: types.CallbackQuery, state: FSMContext):
|
||||
await process_callback_view_profile(callback_query, state)
|
||||
|
||||
Reference in New Issue
Block a user