Fix typing

This commit is contained in:
Zakhar Izmaylov
2024-11-24 01:19:00 +03:00
parent fe59470256
commit 0867ebdd61
15 changed files with 210 additions and 251 deletions
+58 -17
View File
@@ -1,4 +1,5 @@
from datetime import datetime
from typing import Any
import asyncpg
@@ -177,19 +178,64 @@ async def delete_coupon_from_db(coupon_code: str):
await conn.close()
async def restore_trial(tg_id: int):
conn = await asyncpg.connect(DATABASE_URL)
async def restore_trial(tg_id: int, session: Any):
"""
Восстанавливает возможность использования триального периода для пользователя.
Args:
tg_id (int): Telegram ID пользователя
session (Any): Сессия базы данных
Returns:
bool: True, если триал успешно восстановлен, False в случае ошибки
"""
try:
await conn.execute("UPDATE connections SET trial = 0 WHERE tg_id = $1", tg_id)
response = await session.execute(
"""
INSERT INTO connections (tg_id, trial)
VALUES ($1, 0)
ON CONFLICT (tg_id)
DO UPDATE SET trial = 0
""",
tg_id,
)
logger.info(response)
return True
except Exception as e:
logger.error(f"Ошибка при установке значения триала: {e}")
finally:
await conn.close()
return False
async def add_connection(tg_id: int, balance: float = 0.0, trial: int = 0):
conn = await asyncpg.connect(DATABASE_URL)
await conn.execute(
async def use_trial(tg_id: int, session: Any):
"""
Отмечает использование триального периода для пользователя.
Args:
tg_id (int): Telegram ID пользователя
session (Any): Сессия базы данных
Returns:
bool: True, если триал успешно использован, False в случае ошибки
"""
try:
response = await session.execute(
"""
INSERT INTO connections (tg_id, trial)
VALUES ($1, 1)
ON CONFLICT (tg_id)
DO UPDATE SET trial = 1
""",
tg_id,
)
logger.info(response)
return True
except Exception as e:
logger.error(f"Ошибка при использовании триала: {e}")
return False
async def add_connection(tg_id: int, balance: float = 0.0, trial: int = 0, session: Any = None):
await session.execute(
"""
INSERT INTO connections (tg_id, balance, trial)
VALUES ($1, $2, $3)
@@ -198,7 +244,6 @@ async def add_connection(tg_id: int, balance: float = 0.0, trial: int = 0):
balance,
trial,
)
await conn.close()
async def check_connection_exists(tg_id: int):
@@ -298,10 +343,8 @@ async def update_balance(tg_id: int, amount: float):
await conn.close()
async def get_trial(tg_id: int) -> int:
conn = await asyncpg.connect(DATABASE_URL)
trial = await conn.fetchval("SELECT trial FROM connections WHERE tg_id = $1", tg_id)
await conn.close()
async def get_trial(tg_id: int, session: Any) -> int:
trial = await session.fetchval("SELECT trial FROM connections WHERE tg_id = $1", tg_id)
return trial if trial is not None else 0
@@ -316,9 +359,8 @@ async def get_all_users(conn):
return await conn.fetch("SELECT tg_id FROM connections")
async def add_referral(referred_tg_id: int, referrer_tg_id: int):
conn = await asyncpg.connect(DATABASE_URL)
await conn.execute(
async def add_referral(referred_tg_id: int, referrer_tg_id: int, session: Any):
await session.execute(
"""
INSERT INTO referrals (referred_tg_id, referrer_tg_id)
VALUES ($1, $2)
@@ -326,7 +368,6 @@ async def add_referral(referred_tg_id: int, referrer_tg_id: int):
referred_tg_id,
referrer_tg_id,
)
await conn.close()
async def handle_referral_on_balance_update(tg_id: int, amount: float):
+1 -1
View File
@@ -40,7 +40,7 @@ async def handle_admin_message(message: types.Message, state: FSMContext):
builder.row(InlineKeyboardButton(text="📢 Массовая рассылка", callback_data="send_to_alls"))
builder.row(InlineKeyboardButton(text="💾 Создать резервную копию", callback_data="backups"))
builder.row(InlineKeyboardButton(text="🔄 Перезагрузить бота", callback_data="restart_bot"))
builder.row(InlineKeyboardButton(text="⬅️ Вернуться в профиль", callback_data="profile"))
builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile"))
await message.answer("🤖 Панель администратора", reply_markup=builder.as_markup())
+2 -2
View File
@@ -140,10 +140,10 @@ async def handle_tg_id_input(message: types.Message, state: FSMContext, session:
@router.callback_query(F.data.startswith("restore_trial_"), IsAdminFilter())
async def handle_restore_trial(callback_query: types.CallbackQuery):
async def handle_restore_trial(callback_query: types.CallbackQuery, session: Any):
tg_id = int(callback_query.data.split("_")[2])
await restore_trial(tg_id)
await restore_trial(tg_id, session)
builder = InlineKeyboardBuilder()
builder.row(InlineKeyboardButton(text="🔙 Назад в меню администратора", callback_data="admin"))
+1 -1
View File
@@ -20,7 +20,7 @@ router = Router()
@router.callback_query(F.data == "activate_coupon")
async def handle_activate_coupon(callback_query: types.CallbackQuery, state: FSMContext):
builder = InlineKeyboardBuilder()
builder.row(InlineKeyboardButton(text="⬅️ Вернуться в профиль", callback_data="profile"))
builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile"))
await callback_query.message.answer(
"<b>🎫 Введите код купона:</b>\n\n"
+2 -2
View File
@@ -29,7 +29,7 @@ async def process_donate(callback_query: types.CallbackQuery, state: FSMContext)
callback_data="enter_custom_donate_amount",
)
)
builder.row(InlineKeyboardButton(text="⬅️ Назад", callback_data="profile"))
builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile"))
await callback_query.message.answer(
text="🌟 Поддержите наш проект! 💪\n\n"
@@ -89,7 +89,7 @@ async def on_successful_donate(message: types.Message, state: FSMContext):
try:
amount = float(message.successful_payment.invoice_payload.split("_")[0])
builder = InlineKeyboardBuilder()
builder.row(InlineKeyboardButton(text="Вернуться в профиль", callback_data="profile"))
builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile"))
await message.answer(
text=f"🙏 Спасибо за донат {amount} рублей! Ваша поддержка очень важна для нас. 💖",
reply_markup=builder.as_markup(),
+3 -3
View File
@@ -22,11 +22,11 @@ async def send_instructions(callback_query: types.CallbackQuery):
builder = InlineKeyboardBuilder()
builder.row(InlineKeyboardButton(text="💬 Поддержка", url=SUPPORT_CHAT_URL))
builder.row(
InlineKeyboardButton(text="⬅️ Вернуться в профиль", callback_data="profile"),
InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile"),
)
with open(image_path, "rb") as image_from_buffer:
await callback_query.answer_photo(
await callback_query.message.answer_photo(
BufferedInputFile(image_from_buffer.read(), filename="instructions.jpg"),
caption=instructions_message,
reply_markup=builder.as_markup(),
@@ -59,6 +59,6 @@ async def process_connect_pc(callback_query: types.CallbackQuery, session: Any):
builder = InlineKeyboardBuilder()
builder.row(InlineKeyboardButton(text="💻 Подключить Windows", url=f"{CONNECT_WINDOWS}{key}"))
builder.row(InlineKeyboardButton(text="🆘 Поддержка", url=f"{SUPPORT_CHAT_URL}"))
builder.row(InlineKeyboardButton(text="🔙 Назад в профиль", callback_data="profile"))
builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile"))
await callback_query.message.answer(instruction_message, reply_markup=builder.as_markup())
+25 -40
View File
@@ -6,7 +6,8 @@ import uuid
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 aiogram.types import CallbackQuery, InlineKeyboardButton, Message
from aiogram.utils.keyboard import InlineKeyboardBuilder
from config import CONNECT_ANDROID, CONNECT_IOS, DOWNLOAD_ANDROID, DOWNLOAD_IOS, PUBLIC_LINK, SUPPORT_CHAT_URL
from database import add_connection, get_balance, store_key, update_balance
@@ -41,19 +42,15 @@ async def select_server(callback_query: CallbackQuery, state: FSMContext, sessio
trial_status = existing_connection["trial"] if existing_connection else 0
if trial_status == 1:
builder = InlineKeyboardBuilder()
builder.row(
InlineKeyboardButton(text="✅ Да, подключить новое устройство", callback_data="confirm_create_new_key")
)
builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile"))
await callback_query.message.answer(
text=KEY,
reply_markup=InlineKeyboardMarkup(
inline_keyboard=[
[
InlineKeyboardButton(
text="✅ Да, подключить новое устройство",
callback_data="confirm_create_new_key",
)
],
[InlineKeyboardButton(text="↩️ Назад", callback_data="profile")],
]
),
reply_markup=builder.as_markup(),
)
await state.update_data(creating_new_key=True)
else:
@@ -69,9 +66,9 @@ async def confirm_create_new_key(callback_query: CallbackQuery, state: FSMContex
balance = await get_balance(tg_id)
if balance < RENEWAL_PLANS["1"]["price"]:
replenish_button = InlineKeyboardButton(text="Перейти в профиль", callback_data="profile")
keyboard = InlineKeyboardMarkup(inline_keyboard=[[replenish_button]])
await callback_query.message.edit_text(NULL_BALANCE, reply_markup=keyboard)
builder = InlineKeyboardBuilder()
builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile"))
await callback_query.message.edit_text(NULL_BALANCE, reply_markup=builder.as_markup())
await state.clear()
return
@@ -125,11 +122,11 @@ async def handle_key_name_input(message: Message, state: FSMContext, session: An
else:
balance = await get_balance(tg_id)
if balance < RENEWAL_PLANS["1"]["price"]:
replenish_button = InlineKeyboardButton(text="Перейти в профиль", callback_data="profile")
keyboard = InlineKeyboardMarkup(inline_keyboard=[[replenish_button]])
builder = InlineKeyboardBuilder()
builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile"))
await message.answer(
"💳 Недостаточно средств для создания подписки на новое устройство. Пополните баланс в личном кабинете.",
reply_markup=keyboard,
reply_markup=builder.as_markup(),
)
logger.warning(f"User {tg_id} has insufficient funds for key creation.")
await state.clear()
@@ -144,29 +141,17 @@ async def handle_key_name_input(message: Message, state: FSMContext, session: An
logger.info(f"Generated public link for the key: {public_link}")
button_support = InlineKeyboardButton(text="💬 Поддержка", url=SUPPORT_CHAT_URL)
button_profile = InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile")
button_iphone = InlineKeyboardButton(text="🍏 Подключить", url=f"{CONNECT_IOS}{public_link}")
button_android = InlineKeyboardButton(
text="🤖 Подключить",
url=f"{CONNECT_ANDROID}{public_link}",
builder = InlineKeyboardBuilder()
builder.row(InlineKeyboardButton(text="💬 Поддержка", url=SUPPORT_CHAT_URL))
builder.row(
InlineKeyboardButton(text="🍏 Скачать для iOS", url=DOWNLOAD_IOS),
InlineKeyboardButton(text="🤖 Скачать для Android", url=DOWNLOAD_ANDROID),
)
button_download_ios = InlineKeyboardButton(text="🍏 Скачать", url=DOWNLOAD_IOS)
button_download_android = InlineKeyboardButton(
text="🤖 Скачать",
url=DOWNLOAD_ANDROID,
)
keyboard = InlineKeyboardMarkup(
inline_keyboard=[
[button_support],
[button_download_ios, button_download_android],
[button_iphone, button_android],
[button_profile],
]
builder.row(
InlineKeyboardButton(text="🍏 Подключить на iOS", url=f"{CONNECT_IOS}{public_link}"),
InlineKeyboardButton(text="🤖 Подключить на Android", url=f"{CONNECT_ANDROID}{public_link}"),
)
builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile"))
remaining_time = expiry_time - current_time
days = remaining_time.days
@@ -174,7 +159,7 @@ async def handle_key_name_input(message: Message, state: FSMContext, session: An
logger.info(f"Sending key message to user {tg_id} with the public link.")
await message.answer(key_message, reply_markup=keyboard)
await message.answer(key_message, reply_markup=builder.as_markup())
try:
least_loaded_cluster = await get_least_loaded_cluster()
+63 -77
View File
@@ -5,7 +5,8 @@ import os
from typing import Any
from aiogram import F, Router, types
from aiogram.types import BufferedInputFile
from aiogram.types import BufferedInputFile, InlineKeyboardButton
from aiogram.utils.keyboard import InlineKeyboardBuilder
from config import CLUSTERS, CONNECT_ANDROID, CONNECT_IOS, DOWNLOAD_ANDROID, DOWNLOAD_IOS, PUBLIC_LINK, TOTAL_GB
from database import delete_key, get_balance, store_key, update_balance, update_key_expiry
@@ -44,20 +45,14 @@ async def process_callback_view_keys(callback_query: types.CallbackQuery, sessio
)
if records:
buttons = []
builder = InlineKeyboardBuilder()
for record in records:
key_name = record["email"]
button = types.InlineKeyboardButton(
text=f"🔑 {key_name}",
callback_data=f"view_key|{key_name}",
)
builder.row(InlineKeyboardButton(text=f"🔑 {key_name}", callback_data=f"view_key|{key_name}"))
buttons.append([button])
builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile"))
back_button = types.InlineKeyboardButton(text="🔙 Назад", callback_data="profile")
buttons.append([back_button])
inline_keyboard = types.InlineKeyboardMarkup(inline_keyboard=buttons)
inline_keyboard = builder.as_markup()
response_message = (
"<b>🔑 Список ваших устройств</b>\n\n" "<i>👇 Выберите устройство для управления подпиской:</i>"
)
@@ -78,10 +73,11 @@ async def process_callback_view_keys(callback_query: types.CallbackQuery, sessio
else:
response_message = NO_KEYS
create_key_button = types.InlineKeyboardButton(text=" Создать подписку", callback_data="create_key")
back_button = types.InlineKeyboardButton(text="🔙 Назад", callback_data="profile")
builder = InlineKeyboardBuilder()
builder.row(InlineKeyboardButton(text=" Создать подписку", callback_data="create_key"))
builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile"))
keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[create_key_button], [back_button]])
keyboard = builder.as_markup()
image_path = os.path.join("img", "pic_keys.jpg")
@@ -135,38 +131,33 @@ async def process_callback_view_key(callback_query: types.CallbackQuery, session
formatted_expiry_date = expiry_date.strftime("%d %B %Y года")
response_message = key_message(key, formatted_expiry_date, days_left_message, server_name)
download_android_button = types.InlineKeyboardButton(text="🤖 Скачать", url=DOWNLOAD_ANDROID)
download_iphone_button = types.InlineKeyboardButton(text="🍏 Скачать", url=DOWNLOAD_IOS)
builder = InlineKeyboardBuilder()
connect_iphone_button = types.InlineKeyboardButton(text="🍏 Подключить", url=f"{CONNECT_IOS}{key}")
connect_android_button = types.InlineKeyboardButton(text="🤖 Подключить", url=f"{CONNECT_ANDROID}{key}")
connect_pc_button = types.InlineKeyboardButton(
text="💻 Windows/Linux",
callback_data=f"connect_pc|{key_name}",
builder.row(
InlineKeyboardButton(text="🍏 Скачать для iOS", url=DOWNLOAD_IOS),
InlineKeyboardButton(text="🤖 Скачать для Android", url=DOWNLOAD_ANDROID),
)
renew_button = types.InlineKeyboardButton(text="⏳ Продлить", callback_data=f"renew_key|{key_name}")
delete_button = types.InlineKeyboardButton(text="❌ Удалить", callback_data=f"delete_key|{key_name}")
back_button = types.InlineKeyboardButton(text="🔙 Назад в профиль", callback_data="profile")
builder.row(
InlineKeyboardButton(text="🍏 Подключить на iOS", url=f"{CONNECT_IOS}{key}"),
InlineKeyboardButton(text="🤖 Подключить на Android", url=f"{CONNECT_ANDROID}{key}"),
)
inline_keyboard = [
[download_iphone_button, download_android_button],
[connect_iphone_button, connect_android_button],
[connect_pc_button],
[renew_button, delete_button],
]
builder.row(InlineKeyboardButton(text="💻 Windows/Linux", callback_data=f"connect_pc|{key_name}"))
builder.row(
InlineKeyboardButton(text="⏳ Продлить", callback_data=f"renew_key|{key_name}"),
InlineKeyboardButton(text="❌ Удалить", callback_data=f"delete_key|{key_name}"),
)
if not key.startswith(PUBLIC_LINK):
update_subscription_button = types.InlineKeyboardButton(
text="🔄 Обновить подписку",
callback_data=f"update_subscription|{key_name}",
builder.row(
InlineKeyboardButton(text="🔄 Обновить подписку", callback_data=f"update_subscription|{key_name}")
)
inline_keyboard.append([update_subscription_button])
inline_keyboard.append([back_button])
builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile"))
keyboard = types.InlineKeyboardMarkup(inline_keyboard=inline_keyboard)
keyboard = builder.as_markup()
image_path = os.path.join("img", "pic_view.jpg")
@@ -251,12 +242,12 @@ async def process_callback_update_subscription(callback_query: types.CallbackQue
server_id=least_loaded_cluster_id,
)
response_message = f"Ваша подписка {email} обновлена!"
back_button = types.InlineKeyboardButton(text="🔙 Назад в профиль", callback_data="profile")
keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[back_button]])
builder = InlineKeyboardBuilder()
builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile"))
await callback_query.message.answer(
response_message,
reply_markup=keyboard,
reply_markup=builder.as_markup(),
)
else:
await callback_query.message.answer(
@@ -309,36 +300,31 @@ async def process_callback_renew_key(callback_query: types.CallbackQuery, sessio
client_id = record["client_id"]
expiry_time = record["expiry_time"]
keyboard = types.InlineKeyboardMarkup(
inline_keyboard=[
[
types.InlineKeyboardButton(
text=f'📅 1 месяц ({RENEWAL_PLANS["1"]["price"]} руб.)',
callback_data=f"renew_plan|1|{client_id}",
)
],
[
types.InlineKeyboardButton(
text=f'📅 3 месяца ({RENEWAL_PLANS["3"]["price"]} руб.)',
callback_data=f"renew_plan|3|{client_id}",
)
],
[
types.InlineKeyboardButton(
text=f'📅 6 месяцев ({RENEWAL_PLANS["6"]["price"]} руб.)',
callback_data=f"renew_plan|6|{client_id}",
)
],
[
types.InlineKeyboardButton(
text=f'📅 12 месяцев ({RENEWAL_PLANS["12"]["price"]} руб.)',
callback_data=f"renew_plan|12|{client_id}",
)
],
[types.InlineKeyboardButton(text="🔙 Назад", callback_data="profile")],
]
builder = InlineKeyboardBuilder()
builder.row(
InlineKeyboardButton(
text=f'📅 1 месяц ({RENEWAL_PLANS["1"]["price"]} руб.)',
callback_data=f"renew_plan|1|{client_id}",
)
)
builder.row(
InlineKeyboardButton(
text=f'📅 3 месяца ({RENEWAL_PLANS["3"]["price"]} руб.)',
callback_data=f"renew_plan|3|{client_id}",
)
)
builder.row(
InlineKeyboardButton(
text=f'📅 6 месяцев ({RENEWAL_PLANS["6"]["price"]} руб.)',
callback_data=f"renew_plan|6|{client_id}",
)
)
builder.row(
InlineKeyboardButton(
text=f'📅 12 месяцев ({RENEWAL_PLANS["12"]["price"]} руб.)',
callback_data=f"renew_plan|12|{client_id}",
)
)
balance = await get_balance(tg_id)
response_message = PLAN_SELECTION_MSG.format(
@@ -348,7 +334,7 @@ async def process_callback_renew_key(callback_query: types.CallbackQuery, sessio
await callback_query.message.answer(
text=response_message,
reply_markup=keyboard,
reply_markup=builder.as_markup(),
)
else:
await callback_query.message.answer("<b>Ключ не найден.</b>")
@@ -434,21 +420,21 @@ async def process_callback_renew_plan(callback_query: types.CallbackQuery, sessi
balance = await get_balance(tg_id)
if balance < cost:
replenish_button = types.InlineKeyboardButton(text="Пополнить баланс", callback_data="pay")
view_profile = types.InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile")
keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[replenish_button], [view_profile]])
builder = InlineKeyboardBuilder()
builder.row(InlineKeyboardButton(text="Пополнить баланс", callback_data="pay"))
builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile"))
await callback_query.message.answer(
INSUFFICIENT_FUNDS_MSG,
reply_markup=keyboard,
reply_markup=builder.as_markup(),
)
return
response_message = SUCCESS_RENEWAL_MSG.format(months=RENEWAL_PLANS[plan]["months"])
back_button = types.InlineKeyboardButton(text="Назад", callback_data="profile")
keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[back_button]])
builder = InlineKeyboardBuilder()
builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile"))
await callback_query.message.answer(response_message, reply_markup=keyboard)
await callback_query.message.answer(response_message, reply_markup=builder.as_markup())
async def renew_key_on_servers():
tasks = []
+33 -87
View File
@@ -1,107 +1,53 @@
import asyncio
from datetime import datetime, timedelta
from typing import Any
import uuid
import asyncpg
from py3xui import AsyncApi
from client import add_client
from config import ADMIN_PASSWORD, ADMIN_USERNAME, CLUSTERS, DATABASE_URL, PUBLIC_LINK, TRIAL_TIME
from database import store_key
from config import ADMIN_PASSWORD, ADMIN_USERNAME, CLUSTERS, PUBLIC_LINK, TRIAL_TIME
from database import store_key, use_trial
from handlers.texts import INSTRUCTIONS
from handlers.utils import generate_random_email, get_least_loaded_cluster
async def create_trial_key(tg_id: int):
conn = await asyncpg.connect(DATABASE_URL)
try:
client_id = str(uuid.uuid4())
email = generate_random_email()
async def create_trial_key(tg_id: int, session: Any):
client_id = str(uuid.uuid4())
email = generate_random_email()
public_link = f"{PUBLIC_LINK}{email}/{tg_id}"
instructions = INSTRUCTIONS
result = {"key": public_link, "instructions": instructions}
current_time = datetime.utcnow()
expiry_time = current_time + timedelta(days=TRIAL_TIME, hours=3)
expiry_timestamp = int(expiry_time.timestamp() * 1000)
public_link = f"{PUBLIC_LINK}{email}/{tg_id}"
instructions = INSTRUCTIONS
least_loaded_cluster = await get_least_loaded_cluster()
for server_id, server in CLUSTERS[least_loaded_cluster].items():
xui = AsyncApi(
CLUSTERS[least_loaded_cluster][server_id]["API_URL"],
username=ADMIN_USERNAME,
password=ADMIN_PASSWORD,
)
result = {"key": public_link, "instructions": instructions}
asyncio.create_task(generate_and_store_keys(tg_id, client_id, email, public_link))
return result
finally:
await conn.close()
async def generate_and_store_keys(tg_id: int, client_id: str, email: str, public_link: str):
conn = await asyncpg.connect(DATABASE_URL)
try:
current_time = datetime.utcnow()
expiry_time = current_time + timedelta(days=TRIAL_TIME, hours=3)
expiry_timestamp = int(expiry_time.timestamp() * 1000)
least_loaded_cluster = await get_least_loaded_cluster()
tasks = []
for server_id, server in CLUSTERS[least_loaded_cluster].items():
task = create_key_on_server(
least_loaded_cluster,
server_id,
client_id,
email,
tg_id,
expiry_timestamp,
)
tasks.append(task)
await asyncio.gather(*tasks)
await store_key(
tg_id,
await add_client(
xui,
client_id,
email,
expiry_timestamp,
public_link,
server_id=least_loaded_cluster,
)
await conn.execute(
"""
INSERT INTO connections (tg_id, trial)
VALUES ($1, 1)
ON CONFLICT (tg_id)
DO UPDATE SET trial = 1
""",
tg_id,
limit_ip=1,
total_gb=0,
expiry_time=expiry_timestamp,
enable=True,
flow="xtls-rprx-vision",
)
finally:
await conn.close()
async def create_key_on_server(
cluster_id: str,
server_id: str,
client_id: str,
email: str,
tg_id: int,
expiry_timestamp: int,
):
"""Создает ключ на сервере в указанном кластере и возвращает результат."""
xui = AsyncApi(
CLUSTERS[cluster_id][server_id]["API_URL"],
username=ADMIN_USERNAME,
password=ADMIN_PASSWORD,
)
response = await add_client(
xui,
await store_key(
tg_id,
client_id,
email,
tg_id,
limit_ip=1,
total_gb=0,
expiry_time=expiry_timestamp,
enable=True,
flow="xtls-rprx-vision",
expiry_timestamp,
public_link,
server_id=least_loaded_cluster,
)
return response
await use_trial(tg_id, session)
return result
+1 -1
View File
@@ -48,7 +48,7 @@ async def handle_pay(callback_query: CallbackQuery):
)
builder.row(InlineKeyboardButton(text="🎟️ Активировать купон", callback_data="activate_coupon"))
builder.row(InlineKeyboardButton(text="⬅️ Вернуться в профиль", callback_data="profile"))
builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile"))
await callback_query.message.answer(
"💸 <b>Выберите удобный способ пополнения баланса:</b>\n\n"
+1 -1
View File
@@ -8,7 +8,7 @@ from logger import logger
async def send_payment_success_notification(user_id: int, amount: float):
try:
builder = InlineKeyboardBuilder()
builder.row(InlineKeyboardButton(text="Перейти в профиль", callback_data="profile"))
builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile"))
await bot.send_message(
chat_id=user_id,
text=f"Ваш баланс успешно пополнен на {amount} рублей. Спасибо за оплату!",
+1 -1
View File
@@ -60,7 +60,7 @@ async def process_callback_pay_yookassa(callback_query: types.CallbackQuery, sta
callback_data="enter_custom_amount_yookassa",
)
)
builder.row(InlineKeyboardButton(text="⬅️ Назад", callback_data="profile"))
builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile"))
key_count = await get_key_count(tg_id)
+2 -2
View File
@@ -66,7 +66,7 @@ async def process_callback_view_profile(callback_query: types.CallbackQuery, sta
@router.callback_query(F.data == "view_tariffs")
async def view_tariffs_handler(callback_query: types.CallbackQuery):
builder = InlineKeyboardBuilder()
builder.row(InlineKeyboardButton(text="⬅️ Вернуться в профиль", callback_data="profile"))
builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile"))
await callback_query.message.answer(
"<b>🚀 Доступные тарифы VPN:</b>\n\n"
@@ -94,7 +94,7 @@ async def invite_handler(callback_query: types.CallbackQuery):
image_path = os.path.join("img", "pic_invite.jpg")
builder = InlineKeyboardBuilder()
builder.row(InlineKeyboardButton(text="⬅️ Вернуться в профиль", callback_data="profile"))
builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile"))
if os.path.isfile(image_path):
with open(image_path, "rb") as image_file:
await callback_query.message.answer_photo(
+11 -9
View File
@@ -8,30 +8,32 @@ from aiogram.types import BufferedInputFile, CallbackQuery, InlineKeyboardButton
from aiogram.utils.keyboard import InlineKeyboardBuilder
from config import CHANNEL_URL, CONNECT_ANDROID, CONNECT_IOS, DOWNLOAD_ANDROID, DOWNLOAD_IOS, SUPPORT_CHAT_URL
from database import add_connection, add_referral, check_connection_exists, get_trial, restore_trial
from database import add_connection, add_referral, check_connection_exists, get_trial, use_trial
from handlers.keys.trial_key import create_trial_key
from handlers.texts import INSTRUCTIONS_TRIAL, WELCOME_TEXT, get_about_vpn
from logger import logger
router = Router()
@router.callback_query(F.data == "start")
async def handle_start_callback_query(callback_query: CallbackQuery, state: FSMContext):
await start_command(callback_query.message, state)
async def handle_start_callback_query(callback_query: CallbackQuery, state: FSMContext, session: Any, admin: bool):
await start_command(callback_query.message, state, session, admin)
@router.message(Command("start"))
async def start_command(message: Message, admin: bool):
async def start_command(message: Message, state: FSMContext, session: Any, admin: bool):
if message.text:
try:
referrer_tg_id = int(message.text.split("referral_")[1])
await add_referral(message.from_user.id, referrer_tg_id)
except (ValueError,IndexError):
await add_referral(message.from_user.id, referrer_tg_id, session)
except (ValueError, IndexError):
pass
connection_exists = await check_connection_exists(message.from_user.id)
if not connection_exists:
await add_connection(message.from_user.id)
trial_status = await get_trial(message.from_user.id)
await add_connection(message.from_user.id, session)
trial_status = await get_trial(message.from_user.id, session)
logger.info(f'trial_status {trial_status}')
image_path = os.path.join("img", "pic.jpg")
builder = InlineKeyboardBuilder()
@@ -71,7 +73,7 @@ async def handle_connect_vpn(callback_query: CallbackQuery, session: Any):
if "error" in trial_key_info:
await callback_query.message.answer(trial_key_info["error"])
else:
await restore_trial(user_id)
await use_trial(user_id, session)
key_message = (
f"🔑 <b>Ваш персональный ключ доступа:</b>\n"
+6 -7
View File
@@ -14,10 +14,9 @@ class DatabaseMiddleware(BaseMiddleware):
event: TelegramObject,
data: Dict[str, Any],
) -> Any:
async with await asyncpg.create_pool(DATABASE_URL) as pool:
async with pool.acquire() as session:
data["session"] = session
try:
return await handler(event, data)
finally:
await pool.release(session)
conn = await asyncpg.connect(DATABASE_URL)
try:
data["session"] = conn
return await handler(event, data)
finally:
await conn.close()