This commit is contained in:
Zakhar Izmaylov
2024-11-20 09:43:22 +03:00
8 changed files with 150 additions and 28 deletions
+1
View File
@@ -3,6 +3,7 @@ from aiogram.fsm.storage.memory import MemoryStorage
from config import API_TOKEN, CRYPTO_BOT_ENABLE, FREEKASSA_ENABLE, ROBOKASSA_ENABLE, STARS_ENABLE, YOOKASSA_ENABLE
from middlewares.admin import AdminMiddleware
from middlewares.database import DatabaseMiddleware
from middlewares.logging import LoggingMiddleware
from middlewares.user import UserMiddleware
from middlewares.database import DatabaseMiddleware
+6 -6
View File
@@ -142,19 +142,19 @@ 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."
f"Checking if key name '{key_name}' already exists for user {tg_id} in the database."
)
existing_key = await conn.fetchrow(
"SELECT * FROM keys WHERE email = $1", key_name.lower()
"SELECT * FROM keys WHERE email = $1 AND tg_id = $2",
key_name.lower(),
tg_id,
)
if existing_key:
await message.bot.send_message(
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 for user {tg_id}.")
await state.set_state(Form.waiting_for_key_name)
return
finally:
@@ -200,7 +200,7 @@ async def handle_key_name_input(message: Message, state: FSMContext):
logger.info(f"User {tg_id} balance deducted for key creation.")
expiry_timestamp = int(expiry_time.timestamp() * 1000)
public_link = f"{PUBLIC_LINK}{email}"
public_link = f"{PUBLIC_LINK}{email}/{tg_id}"
logger.info(f"Generated public link for the key: {public_link}")
+1 -1
View File
@@ -283,7 +283,7 @@ async def process_callback_update_subscription(callback_query: types.CallbackQue
if record:
expiry_time = record["expiry_time"]
client_id = record["client_id"]
public_link = f"{PUBLIC_LINK}{email}"
public_link = f"{PUBLIC_LINK}{email}/{tg_id}"
try:
await conn.execute(
+135 -17
View File
@@ -1,53 +1,171 @@
import base64
from datetime import datetime
import aiohttp
import asyncpg
from aiohttp import web
from config import CLUSTERS
from config import CLUSTERS, DATABASE_URL, TRANSITION_DATE_STR
from logger import logger
async def fetch_url_content(url):
async def fetch_url_content(url, tg_id):
try:
logger.debug(f"Получение URL: {url}")
logger.info(f"Получение URL: {url} для tg_id: {tg_id}")
async with aiohttp.ClientSession() as session:
async with session.get(url, ssl=False) as response:
if response.status == 200:
content = await response.text()
logger.debug(f"Успешно получен контент с {url}")
logger.info(f"Успешно получен контент с {url} для tg_id: {tg_id}")
return base64.b64decode(content).decode("utf-8").split("\n")
else:
logger.error(
f"Не удалось получить {url}, статус: {response.status}"
f"Не удалось получить {url} для tg_id: {tg_id}, статус: {response.status}"
)
return []
except Exception as e:
logger.error(f"Ошибка при получении {url}: {e}")
logger.error(f"Ошибка при получении {url} для tg_id: {tg_id}: {e}")
return []
async def combine_unique_lines(urls, query_string):
async def combine_unique_lines(urls, tg_id, query_string):
all_lines = []
logger.debug(f"Начинаем объединение подписок для запроса: {query_string}")
logger.info(
f"Начинаем объединение подписок для tg_id: {tg_id}, запрос: {query_string}"
)
urls_with_query = [f"{url}?{query_string}" for url in urls]
logger.debug(f"Составлены URL-адреса: {urls_with_query}")
logger.info(f"Составлены URL-адреса: {urls_with_query}")
for url in urls_with_query:
lines = await fetch_url_content(url)
lines = await fetch_url_content(url, tg_id)
all_lines.extend(lines)
all_lines = list(set(filter(None, all_lines)))
logger.debug(
f"Объединено {len(all_lines)} строк после фильтрации и удаления дубликатов"
logger.info(
f"Объединено {len(all_lines)} строк после фильтрации и удаления дубликатов для tg_id: {tg_id}"
)
return all_lines
async def handle_subscription(request):
email = request.match_info["email"]
logger.info(f"Получен запрос на подписку для email: {email}")
transition_date = datetime.strptime(TRANSITION_DATE_STR, "%Y-%m-%d %H:%M:%S")
transition_timestamp_ms = int(transition_date.timestamp() * 1000)
transition_timestamp_ms_adjusted = transition_timestamp_ms - (3 * 60 * 60 * 1000)
logger.info(
f"Время перехода (с поправкой на часовой пояс): {transition_timestamp_ms_adjusted}"
)
async def handle_old_subscription(request):
email = request.match_info.get("email")
if not email:
logger.warning("Получен запрос без email")
return web.Response(
text="❌ Неверные параметры запроса. Требуется email.",
status=400,
)
logger.info(f"Обработка запроса для старого клиента с email: {email}")
conn = await asyncpg.connect(DATABASE_URL)
try:
key_info = await conn.fetchrow(
"SELECT created_at FROM keys WHERE email = $1", email
)
if not key_info:
logger.warning(f"Клиент с email {email} не найден в базе.")
return web.Response(
text="❌ Клиент с таким email не найден.",
status=404,
)
created_at_ms = key_info["created_at"]
logger.info(f"Значение created_at для клиента с email {email}: {created_at_ms}")
created_at_datetime = datetime.utcfromtimestamp(created_at_ms / 1000)
logger.info(
f"Время создания клиента в формате datetime (UTC): {created_at_datetime}"
)
logger.info(
f"Время перехода (с поправкой на часовой пояс): {transition_timestamp_ms_adjusted}"
)
if created_at_ms >= transition_timestamp_ms_adjusted:
logger.info(f"Клиент с email {email} является новым.")
return web.Response(
text="❌ Эта ссылка устарела. Пожалуйста, обновите ссылку.",
status=400,
)
urls = []
for cluster in CLUSTERS.values():
for server in cluster.values():
server_subscription_url = f"{server['SUBSCRIPTION']}/{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")
headers = {
"Content-Type": "text/plain; charset=utf-8",
"Content-Disposition": "inline",
"profile-update-interval": "7",
"profile-title": email,
}
logger.info(f"Возвращаем объединенные подписки для email: {email}")
return web.Response(text=base64_encoded, headers=headers)
finally:
await conn.close()
async def handle_new_subscription(request):
email = request.match_info.get("email")
tg_id = request.match_info.get("tg_id")
if not email or not tg_id:
logger.warning("Получен запрос с отсутствующими параметрами email или tg_id")
return web.Response(
text="❌ Неверные параметры запроса. Требуются email и tg_id.",
status=400,
)
logger.info(f"Обработка запроса для нового клиента: email={email}, tg_id={tg_id}")
conn = await asyncpg.connect(DATABASE_URL)
try:
client_data = await conn.fetchrow(
"SELECT tg_id FROM keys WHERE email = $1", email
)
if not client_data:
logger.warning(f"Клиент с email {email} не найден в базе.")
return web.Response(
text="❌ Клиент с таким email не найден.",
status=404,
)
stored_tg_id = client_data["tg_id"]
if str(tg_id) != str(stored_tg_id):
logger.warning(f"Неверный tg_id для клиента с email {email}.")
return web.Response(
text="❌ Неверные данные. Получите свой ключ в боте.",
status=403,
)
finally:
await conn.close()
urls = []
for cluster in CLUSTERS.values():
@@ -56,9 +174,9 @@ async def handle_subscription(request):
urls.append(server_subscription_url)
query_string = request.query_string
logger.debug(f"Извлечен query string: {query_string}")
logger.info(f"Извлечен query string: {query_string}")
combined_subscriptions = await combine_unique_lines(urls, query_string)
combined_subscriptions = await combine_unique_lines(urls, tg_id, query_string)
base64_encoded = base64.b64encode(
"\n".join(combined_subscriptions).encode("utf-8")
+1 -1
View File
@@ -18,7 +18,7 @@ async def create_trial_key(tg_id: int):
client_id = str(uuid.uuid4())
email = generate_random_email()
public_link = f"{PUBLIC_LINK}{email}"
public_link = f"{PUBLIC_LINK}{email}/{tg_id}"
instructions = INSTRUCTIONS
result = {"key": public_link, "instructions": instructions}
+1 -1
View File
@@ -71,7 +71,7 @@ async def process_callback_pay_yookassa(
),
InlineKeyboardButton(
text=PAYMENT_OPTIONS[i + 1]["text"],
callback_data=f'yookassa_{PAYMENT_OPTIONS[i + 1]["callback_data"]},',
callback_data=f'yookassa_{PAYMENT_OPTIONS[i + 1]["callback_data"]}',
),
)
else:
+1 -1
View File
@@ -137,7 +137,7 @@ async def handle_connect_vpn(callback_query: CallbackQuery, session):
async def handle_about_vpn(callback_query: CallbackQuery):
await callback_query.message.delete()
about_vpn_message = get_about_vpn("3.1.0_Stable")
about_vpn_message = get_about_vpn("3.1.1_Stable")
builder = InlineKeyboardBuilder()
builder.row(
+4 -1
View File
@@ -72,7 +72,10 @@ async def get_least_loaded_cluster() -> str:
logger.warning("No valid clusters found in config, returning 'cluster1'.")
return "cluster1"
least_loaded_cluster = min(cluster_loads, key=cluster_loads.get)
least_loaded_cluster = min(
cluster_loads, key=lambda k: (cluster_loads.get(k, 0), k)
)
logger.info(f"Least loaded cluster selected: {least_loaded_cluster}")
return least_loaded_cluster