sub_name/delete user

This commit is contained in:
Vladless
2024-12-13 23:01:58 +03:00
parent 035ef47619
commit 217bb457a1
5 changed files with 102 additions and 49 deletions
+15 -6
View File
@@ -915,12 +915,12 @@ async def get_tg_id_by_client_id(client_id: str):
async def upsert_user(
tg_id: int,
username: str = None,
first_name: str = None,
last_name: str = None,
language_code: str = None,
is_bot: bool = False,
tg_id: int,
username: str = None,
first_name: str = None,
last_name: str = None,
language_code: str = None,
is_bot: bool = False,
):
"""
Обновляет или вставляет информацию о пользователе в базу данных.
@@ -1117,3 +1117,12 @@ async def get_servers_from_db():
)
return servers
async def delete_user_data(session: Any, tg_id: int):
await session.execute("DELETE FROM gifts WHERE sender_tg_id = $1 OR recipient_tg_id = $1", tg_id)
await session.execute("DELETE FROM payments WHERE tg_id = $1", tg_id)
await session.execute("DELETE FROM users WHERE tg_id = $1", tg_id)
await session.execute("DELETE FROM connections WHERE tg_id = $1", tg_id)
await session.execute("DELETE FROM keys WHERE tg_id = $1", tg_id)
await session.execute("DELETE FROM referrals WHERE referrer_tg_id = $1", tg_id)
-1
View File
@@ -22,7 +22,6 @@ router.include_routers(
donate_router,
coupons_router,
notifications_router,
payments_router,
keys_router,
instructions_router,
+57 -17
View File
@@ -5,11 +5,11 @@ from typing import Any
from aiogram import F, Router, types
from aiogram.fsm.context import FSMContext
from aiogram.fsm.state import State, StatesGroup
from aiogram.types import CallbackQuery, InlineKeyboardButton
from aiogram.types import CallbackQuery, InlineKeyboardButton, InlineKeyboardMarkup
from aiogram.utils.keyboard import InlineKeyboardBuilder
from config import TOTAL_GB
from database import get_client_id_by_email, get_servers_from_db, restore_trial, update_key_expiry
from database import get_client_id_by_email, get_servers_from_db, restore_trial, update_key_expiry, delete_user_data
from filters.admin import IsAdminFilter
from handlers.keys.key_utils import delete_key_from_cluster, delete_key_from_db, renew_key_in_cluster
from handlers.utils import sanitize_key_name
@@ -86,7 +86,7 @@ async def handle_username_input(message: types.Message, state: FSMContext, sessi
callback_data=f"restore_trial_{tg_id}",
)
)
builder.row(InlineKeyboardButton(text="❌ Удалить клиента", callback_data=f"confirm_delete_user_{tg_id}"))
builder.row(InlineKeyboardButton(text="🔙 Назад", callback_data="user_editor"))
user_info = (
@@ -499,20 +499,9 @@ async def handle_user_info(callback_query: types.CallbackQuery, state: FSMContex
for (email,) in key_records:
builder.row(InlineKeyboardButton(text=f"🔑 {email}", callback_data=f"edit_key_{email}"))
builder.row(
InlineKeyboardButton(
text="📝 Изменить баланс",
callback_data=f"change_balance_{tg_id}",
)
)
builder.row(
InlineKeyboardButton(
text="🔄 Восстановить пробник",
callback_data=f"restore_trial_{tg_id}",
)
)
builder.row(InlineKeyboardButton(text="📝 Изменить баланс", callback_data=f"change_balance_{tg_id}"))
builder.row(InlineKeyboardButton(text="🔄 Восстановить пробник", callback_data=f"restore_trial_{tg_id}"))
builder.row(InlineKeyboardButton(text="❌ Удалить клиента", callback_data=f"confirm_delete_user_{tg_id}"))
builder.row(InlineKeyboardButton(text="🔙 Назад", callback_data="user_editor"))
user_info = (
@@ -523,5 +512,56 @@ async def handle_user_info(callback_query: types.CallbackQuery, state: FSMContex
f"👥 Количество рефералов: <b>{referral_count}</b>\n"
f"🔑 Ключи (для редактирования нажмите на ключ):"
)
await callback_query.message.answer(user_info, reply_markup=builder.as_markup())
await state.set_state(UserEditorState.displaying_user_info)
@router.callback_query(F.data.startswith("confirm_delete_user_"), IsAdminFilter())
async def confirm_delete_user(callback_query: types.CallbackQuery, state: FSMContext, session: Any):
tg_id = int(callback_query.data.split("_")[3])
confirmation_markup = InlineKeyboardMarkup(
row_width=2,
inline_keyboard=[
[InlineKeyboardButton(text="✅ Подтвердить", callback_data=f"delete_user_{tg_id}")],
[InlineKeyboardButton(text="❌ Отменить", callback_data="user_editor")],
],
)
await callback_query.message.answer(
f"Вы уверены, что хотите удалить пользователя с ID {tg_id}?", reply_markup=confirmation_markup
)
@router.callback_query(F.data.startswith("delete_user_"), IsAdminFilter())
async def delete_user(callback_query: types.CallbackQuery, session: Any):
tg_id = int(callback_query.data.split("_")[2])
key_records = await session.fetch("SELECT email, client_id FROM keys WHERE tg_id = $1", tg_id)
async def delete_keys_from_servers():
try:
tasks = []
for email, client_id in key_records:
servers = await get_servers_from_db()
for cluster_id, cluster in servers.items():
tasks.append(delete_key_from_cluster(cluster_id, email, client_id))
await asyncio.gather(*tasks)
except Exception as e:
logger.error(f"Ошибка при удалении ключей с серверов для пользователя {tg_id}: {e}")
await delete_keys_from_servers()
try:
await delete_user_data(session, tg_id)
back_button = InlineKeyboardButton(text="🔙 Назад", callback_data="user_editor")
keyboard = InlineKeyboardMarkup(inline_keyboard=[[back_button]])
await callback_query.message.answer(f"🗑️ Пользователь с ID {tg_id} был удален.", reply_markup=keyboard)
except Exception as e:
logger.error(f"Ошибка при удалении данных из базы данных для пользователя {tg_id}: {e}")
await callback_query.message.answer(
f"❌ Произошла ошибка при удалении пользователя с ID {tg_id}. Попробуйте снова."
)
+22 -13
View File
@@ -8,6 +8,9 @@ import asyncpg
from config import DATABASE_URL, TRANSITION_DATE_STR
from database import get_servers_from_db
from logger import logger
import urllib.parse
from config import PROJECT_NAME, NEWS_MESSAGE
async def fetch_url_content(url, tg_id):
@@ -67,7 +70,7 @@ async def handle_old_subscription(request):
conn = await asyncpg.connect(DATABASE_URL)
try:
key_info = await conn.fetchrow("SELECT created_at FROM keys WHERE email = $1", email)
key_info = await conn.fetchrow("SELECT created_at, cluster_name FROM keys WHERE email = $1", email)
if not key_info:
logger.warning(f"Клиент с email {email} не найден в базе.")
@@ -77,7 +80,8 @@ async def handle_old_subscription(request):
)
created_at_ms = key_info["created_at"]
logger.info(f"Значение created_at для клиента с email {email}: {created_at_ms}")
cluster_name = key_info["cluster_name"]
logger.info(f"Значение created_at для клиента с email {email}: {created_at_ms}, кластер: {cluster_name}")
created_at_datetime = datetime.utcfromtimestamp(created_at_ms / 1000)
logger.info(f"Время создания клиента в формате datetime (UTC): {created_at_datetime}")
@@ -90,22 +94,24 @@ async def handle_old_subscription(request):
)
servers = await get_servers_from_db()
cluster_servers = servers.get(cluster_name, [])
logger.info(f"Сервера в кластере: {cluster_servers}")
urls = []
for cluster_name, cluster_servers in servers.items():
for server in cluster_servers:
server_subscription_url = f"{server['subscription_url']}/{email}"
urls.append(server_subscription_url)
for server in cluster_servers:
server_subscription_url = f"{server['subscription_url']}/{email}"
urls.append(server_subscription_url)
combined_subscriptions = await combine_unique_lines(urls, email, "")
base64_encoded = base64.b64encode("\n".join(combined_subscriptions).encode("utf-8")).decode("utf-8")
encoded_project_name = urllib.parse.quote(f"{PROJECT_NAME}\n{NEWS_MESSAGE}")
headers = {
"Content-Type": "text/plain; charset=utf-8",
"Content-Disposition": "inline",
"profile-update-interval": "7",
"profile-title": email,
"profile-title": encoded_project_name,
}
logger.info(f"Возвращаем объединенные подписки для email: {email}")
@@ -130,7 +136,7 @@ async def handle_new_subscription(request):
conn = await asyncpg.connect(DATABASE_URL)
try:
client_data = await conn.fetchrow("SELECT tg_id FROM keys WHERE email = $1", email)
client_data = await conn.fetchrow("SELECT tg_id, server_id FROM keys WHERE email = $1", email)
if not client_data:
logger.warning(f"Клиент с email {email} не найден в базе.")
@@ -140,6 +146,7 @@ async def handle_new_subscription(request):
)
stored_tg_id = client_data["tg_id"]
cluster_name = client_data["server_id"]
if str(tg_id) != str(stored_tg_id):
logger.warning(f"Неверный tg_id для клиента с email {email}.")
@@ -147,16 +154,17 @@ async def handle_new_subscription(request):
text="❌ Неверные данные. Получите свой ключ в боте.",
status=403,
)
finally:
await conn.close()
servers = await get_servers_from_db()
cluster_servers = servers.get(cluster_name, [])
urls = []
for cluster_name, cluster_servers in servers.items():
for server in cluster_servers:
server_subscription_url = f"{server['subscription_url']}/{email}"
urls.append(server_subscription_url)
for server in cluster_servers:
server_subscription_url = f"{server['subscription_url']}/{email}"
urls.append(server_subscription_url)
query_string = request.query_string
logger.info(f"Извлечен query string: {query_string}")
@@ -165,11 +173,12 @@ async def handle_new_subscription(request):
base64_encoded = base64.b64encode("\n".join(combined_subscriptions).encode("utf-8")).decode("utf-8")
encoded_project_name = urllib.parse.quote(f"{PROJECT_NAME}\n{NEWS_MESSAGE}")
headers = {
"Content-Type": "text/plain; charset=utf-8",
"Content-Disposition": "inline",
"profile-update-interval": "7",
"profile-title": email,
"profile-title": encoded_project_name,
}
logger.info(f"Возвращаем объединенные подписки для email: {email}")
+8 -12
View File
@@ -68,18 +68,15 @@ async def view_tariffs_handler(callback_query: types.CallbackQuery):
builder = InlineKeyboardBuilder()
builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile"))
image_path = os.path.join("img", "tariffs.jpg")
image_path = os.path.join("img", "tariffs.jpg")
tariffs_message = (
"<b>🚀 Доступные тарифы VPN:</b>\n\n"
+ "\n".join(
[
f"{months} {'месяц' if months == '1' else 'месяца' if int(months) in [2, 3, 4] else 'месяцев'}: "
f"{RENEWAL_PLANS[months]['price']} "
f"{'💳' if months == '1' else '🌟' if months == '3' else '🔥' if months == '6' else '🚀'} рублей"
for months in sorted(RENEWAL_PLANS.keys(), key=int)
]
)
tariffs_message = "<b>🚀 Доступные тарифы VPN:</b>\n\n" + "\n".join(
[
f"{months} {'месяц' if months == '1' else 'месяца' if int(months) in [2, 3, 4] else 'месяцев'}: "
f"{RENEWAL_PLANS[months]['price']} "
f"{'💳' if months == '1' else '🌟' if months == '3' else '🔥' if months == '6' else '🚀'} рублей"
for months in sorted(RENEWAL_PLANS.keys(), key=int)
]
)
if os.path.isfile(image_path):
@@ -96,7 +93,6 @@ async def view_tariffs_handler(callback_query: types.CallbackQuery):
)
@router.callback_query(F.data == "invite")
async def invite_handler(callback_query: types.CallbackQuery):
chat_id = callback_query.message.chat.id