Files
Telegram-bot-old/handlers/profile.py

370 lines
14 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.

# Профиль. последнее изменение 24.11.2025
from aiogram import Router, types
from aiogram.types import CallbackQuery
import logging
from datetime import datetime
from aiogram.enums.parse_mode import ParseMode
import locale
from instences.config import BASE_URL_FASTAPI
import aiohttp
from keyboard.keyboards import (
account_keyboard,
popup_keyboard,
tranhist_keyboard,
confirm_popup_keyboard,
guide_keyboard,
balance_keyboard,
payment_methods_keyboard,
)
locale.setlocale(locale.LC_TIME, "ru_RU.UTF-8")
router = Router()
logger = logging.getLogger(__name__)
async def call_api(method, endpoint, data=None,base_url = BASE_URL_FASTAPI):
"""
Выполняет HTTP-запрос к FastAPI.
"""
url = f"{base_url}{endpoint}"
logger.info(f"Инициализация запроса: {method} {url} с данными {data}")
try:
async with aiohttp.ClientSession() as session:
async with session.request(
method,
url,
json=data,
headers={"Content-Type": "application/json"}
) as response:
logger.info(
f"Получен ответ от {url}: статус {response.status}"
)
if response.status in {200, 201}:
result = await response.json()
logger.debug(f"Ответ JSON: {result}")
return result
if response.status == 404:
logger.debug(f"Код {response.status}, возвращаю ничего")
return None
logger.error(
f"Ошибка в запросе: статус {response.status}, причина {response.reason}"
)
return "ERROR"
except Exception as e:
logger.exception(f"Исключение при выполнении запроса к {url}: {e}")
return "ERROR"
@router.callback_query(lambda callback: callback.data == "profile")
async def profile_callback_handler(callback: CallbackQuery):
"""
Профиль пользователя.
Логика работы с API сохранена, изменён только текст/визуал.
"""
try:
user_data = await call_api("GET", f"/user/{callback.from_user.id}")
if not user_data:
await callback.message.answer(
"Произошла ошибка, попробуйте позже или свяжитесь с администрацией."
)
await callback.answer()
return
# Последняя подписка пользователя
sub_data = await call_api(
"GET", f"/subscription/{user_data['telegram_id']}/last"
)
if sub_data == "ERROR" or not isinstance(sub_data, dict):
sub_data = None
username = callback.from_user.username or "-"
balance = user_data.get("balance", 0)
# Статус подписки: Активна / Нет активных
sub_status = "⚫ Нет активных"
if sub_data:
expiry_date = sub_data.get("expiry_date")
if expiry_date:
try:
is_expired = datetime.fromisoformat(
expiry_date) < datetime.now()
except ValueError:
is_expired = True
else:
is_expired = True
if not is_expired:
sub_status = "🟢 Активна"
text = (
"🥚 <b>Профиль</b>\n\n"
f"Пользователь: @{username}\n"
f"Баланс: {balance}\n"
f"Статус подписки: {sub_status}\n\n"
"Выберите действие:"
)
await callback.message.edit_text(
text,
parse_mode=ParseMode.HTML,
reply_markup=account_keyboard(),
)
except Exception as e:
logger.exception(f"Ошибка в обработчике профиля: {e}")
await callback.message.answer("Произошла ошибка. Попробуйте позже.")
finally:
await callback.answer()
@router.callback_query(lambda callback: callback.data == "balance")
async def balance_callback_handler(callback: CallbackQuery):
"""
Обработчик callback_query для баланса.
"""
user_data = await call_api("GET", f"/user/{callback.from_user.id}")
if not user_data:
await callback.message.answer(
"Произошла ошибка, попробуйте позже или свяжитесь с администрацией."
)
await callback.answer()
return
await callback.message.edit_text(
f"💰 Текущий баланс: {user_data['balance']}\n\n"
"Выбери сумму, на которую хочешь пополнить счёт.",
reply_markup=balance_keyboard(),
)
await callback.answer()
@router.callback_query(lambda callback: callback.data == "popup")
async def popup_callback_handler(callback: CallbackQuery):
"""
Обработчик callback_query для выбора суммы пополнения.
"""
user = await call_api("GET", f"/user/{callback.from_user.id}")
if not user:
await callback.message.answer(
"Произошла ошибка, попробуйте позже или свяжитесь с администрацией."
)
await callback.answer()
return
await callback.message.edit_text(
"Выбери сумму для пополнения баланса.",
reply_markup=popup_keyboard(),
)
await callback.answer()
@router.callback_query(lambda callback: callback.data == "tranhist")
async def tranhist_callback_handler(callback: CallbackQuery):
"""
Обработчик callback_query для истории транзакций.
(Логику и формат Markdown_V2 не трогаем, чтобы не поймать новые баги)
"""
user_data = await call_api("GET", f"/user/{callback.from_user.id}")
if not user_data:
await callback.message.edit_text("Вы еще не зарегистрированы.")
await callback.answer()
return
try:
transactions = await call_api(
"GET", f"/user/{user_data['telegram_id']}/transactions"
)
if not transactions:
await callback.message.edit_text(
"У вас нет транзакций.", reply_markup=tranhist_keyboard()
)
await callback.answer()
return
result = "Ваши транзакции:```\n"
for count, tran in enumerate(transactions, start=1):
dt = datetime.fromisoformat(tran["created_at"]).strftime(
"%d.%m.%Y %H:%M:%S"
)
result += f"{count}. Сумма: {tran['amount']}, Дата: {dt}\n"
if len(result) > 4000:
result += "...\nСлишком много транзакций для отображения."
break
result += "```"
await callback.message.edit_text(
result,
parse_mode=ParseMode.MARKDOWN_V2,
reply_markup=tranhist_keyboard(),
)
except Exception as e:
logger.error(f"Ошибка обработки транзакций: {e}")
await callback.message.edit_text(
"Произошла ошибка. Попробуйте позже."
)
finally:
await callback.answer()
@router.callback_query(lambda callback: callback.data.startswith("popup:"))
async def popup_confirm_callback_handler(callback: CallbackQuery):
"""
После выбора суммы показываем варианты оплаты.
Реального списания пока нет — только выбор метода.
"""
try:
data = callback.data.split(":")
amount_str = data[1]
amount = int(float(amount_str))
except Exception:
await callback.message.answer("Некорректная сумма пополнения.")
await callback.answer()
return
text = (
f"💰 Сумма пополнения: {amount}\n\n"
"Выбери способ оплаты:"
)
await callback.message.edit_text(
text=text,
reply_markup=payment_methods_keyboard(amount),
)
await callback.answer()
@router.callback_query(lambda callback: callback.data.startswith("method_stars_"))
async def method_stars_handler(callback: CallbackQuery):
"""
Заглушка: оплата через Telegram Stars.
"""
amount = callback.data.split("_")[-1]
await callback.message.edit_text(
f"⭐ Оплата через Telegram Stars на {amount} ₽ пока в разработке.\n\n"
"Позже сюда подвяжем реальный платёж.",
)
await callback.answer()
@router.callback_query(lambda callback: callback.data.startswith("method_ykassa_"))
async def method_ykassa_handler(callback: CallbackQuery):
"""
Обработчик оплаты через YooKassa.
"""
amount = callback.data.split("_")[-1]
# Формируем URL с query parameters вместо JSON body
endpoint = f"/billing/payments/init?user_id={callback.from_user.id}&amount={float(amount)}&provider=yookassa"
logger.info(f"Отправка запроса на инициализацию платежа: {endpoint}")
# Отправляем POST запрос с пустым телом (параметры в URL)
result = await call_api("POST", endpoint, None, "http://billing:8000")
if result == "ERROR":
await callback.message.edit_text(
"❌ Произошла ошибка при создании платежа. Попробуйте позже."
)
await callback.answer()
return
# Проверяем успешность операции
if not result.get("success", False):
error_msg = result.get("error", "Неизвестная ошибка")
await callback.message.edit_text(
f"❌ Ошибка: {error_msg}"
)
await callback.answer()
return
payment_url = result.get("confirmation_url", "#")
payment_id = result.get("payment_id", "")
await callback.message.edit_text(
f"💵 <b>Оплата через YooKassa</b>\n\n"
f"💰 Сумма: <code>{amount}</code> руб\n"
f"📋 ID платежа: <code>{payment_id}</code>\n\n"
f"➡️ <a href='{payment_url}'>Перейти к оплате</a>\n\n"
f"<i>После оплаты нажмите кнопку 'Проверить оплату'</i>",
parse_mode="HTML",
disable_web_page_preview=True,
reply_markup=types.InlineKeyboardMarkup(
inline_keyboard=[[
types.InlineKeyboardButton(
text="🔄 Проверить оплату",
callback_data=f"check_payment:{payment_id}"
)]
]
)
)
await callback.answer()
@router.callback_query(lambda callback: callback.data.startswith("method_crypto_"))
async def method_crypto_handler(callback: CallbackQuery):
"""
Заглушка: оплата через CryptoBot.
"""
amount = callback.data.split("_")[-1]
endpoint = f"/billing/payments/init?user_id={callback.from_user.id}&amount={float(amount)}&provider=cryptobot"
logger.info(f"Отправка запроса на инициализацию платежа: {endpoint}")
# Отправляем POST запрос с пустым телом (параметры в URL)
result = await call_api("POST", endpoint, None, "http://billing:8000")
if result == "ERROR":
await callback.message.edit_text(
"❌ Произошла ошибка при создании платежа. Попробуйте позже."
)
await callback.answer()
return
# Проверяем успешность операции
if not result.get("success", False):
error_msg = result.get("error", "Неизвестная ошибка")
await callback.message.edit_text(
f"❌ Ошибка: {error_msg}"
)
await callback.answer()
return
payment_url = result.get("confirmation_url", "#")
payment_id = result.get("payment_id", "")
await callback.message.edit_text(
f"💵 <b>Оплата через Сryptobot</b>\n\n"
f"💰 Сумма: <code>{amount}</code> руб\n"
f"📋 ID платежа: <code>{payment_id}</code>\n\n"
f"➡️ <a href='{payment_url}'>Перейти к оплате</a>\n\n"
f"<i>После оплаты нажмите кнопку 'Проверить оплату'</i>",
parse_mode="HTML",
disable_web_page_preview=True,
reply_markup=types.InlineKeyboardMarkup(
inline_keyboard=[[
types.InlineKeyboardButton(
text="🔄 Проверить оплату",
callback_data=f"check_payment:{payment_id}"
)]
]
)
)
await callback.answer()
@router.callback_query(lambda callback: callback.data == "guide")
async def guide_callback_handler(callback: CallbackQuery):
"""
Обработчик callback_query для руководства.
"""
await callback.message.edit_text(
"Выбери платформу, для которой нужно руководство по подключению:",
reply_markup=guide_keyboard(),
)
await callback.answer()