Files
Solo_bot/handlers/keys/subscriptions.py
T
2024-12-19 20:37:41 +03:00

215 lines
7.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import base64
from datetime import datetime
import aiohttp
import asyncpg
from aiohttp import web
from config import DATABASE_URL, PROJECT_NAME, SUB_MESSAGE, TRANSITION_DATE_STR
from database import get_servers_from_db
from logger import logger
async def fetch_url_content(url, tg_id):
try:
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.info(f"Успешно получен контент с {url} для tg_id: {tg_id}")
return base64.b64decode(content).decode("utf-8").split("\n")
else:
logger.error(
f"Не удалось получить {url} для tg_id: {tg_id}, статус: {response.status}"
)
return []
except Exception as e:
logger.error(f"Ошибка при получении {url} для tg_id: {tg_id}: {e}")
return []
async def combine_unique_lines(urls, tg_id, query_string):
all_lines = []
logger.info(
f"Начинаем объединение подписок для tg_id: {tg_id}, запрос: {query_string}"
)
urls_with_query = [f"{url}?{query_string}" for url in urls]
logger.info(f"Составлены URL-адреса: {urls_with_query}")
for url in urls_with_query:
lines = await fetch_url_content(url, tg_id)
all_lines.extend(lines)
all_lines = list(set(filter(None, all_lines)))
logger.info(
f"Объединено {len(all_lines)} строк после фильтрации и удаления дубликатов для tg_id: {tg_id}"
)
return all_lines
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, server_id 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"]
cluster_name = key_info.get("server_id")
if not cluster_name:
logger.warning(f"У клиента с email {email} отсутствует cluster_name.")
return web.Response(
text="❌ Устаревшие данные. Обратитесь в поддержку.",
status=400,
)
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}"
)
if created_at_ms >= transition_timestamp_ms_adjusted:
logger.info(f"Клиент с email {email} является новым.")
return web.Response(
text="❌ Эта ссылка устарела. Пожалуйста, обновите ссылку.",
status=400,
)
servers = await get_servers_from_db()
cluster_servers = servers.get(cluster_name, [])
logger.info(f"Сервера в кластере: {cluster_servers}")
urls = []
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 = f"{PROJECT_NAME} - {SUB_MESSAGE}"
headers = {
"Content-Type": "text/plain; charset=utf-8",
"Content-Disposition": "inline",
"profile-update-interval": "7",
"profile-title": "base64:" + base64.b64encode(
encoded_project_name.encode("utf-8")
).decode("utf-8"),
}
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, server_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"]
cluster_name = client_data["server_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()
servers = await get_servers_from_db()
cluster_servers = servers.get(cluster_name, [])
urls = []
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}")
combined_subscriptions = await combine_unique_lines(urls, tg_id, query_string)
base64_encoded = base64.b64encode(
"\n".join(combined_subscriptions).encode("utf-8")
).decode("utf-8")
encoded_project_name = f"{PROJECT_NAME} - {SUB_MESSAGE}"
headers = {
"Content-Type": "text/plain; charset=utf-8",
"Content-Disposition": "inline",
"profile-update-interval": "7",
"profile-title": "base64:" + base64.b64encode(
encoded_project_name.encode("utf-8")
).decode("utf-8"),
}
logger.info(f"Возвращаем объединенные подписки для email: {email}")
return web.Response(text=base64_encoded, headers=headers)