from aiogram import F, Router
from aiogram.fsm.context import FSMContext
from aiogram.fsm.state import State, StatesGroup
from aiogram.types import CallbackQuery, Message
from sqlalchemy import delete, select, update
from sqlalchemy.ext.asyncio import AsyncSession
from config import USERNAME_BOT
from database import create_tracking_source, get_tracking_source_stats
from database.models import TrackingSource, User
from filters.admin import IsAdminFilter
from logger import logger
from ..panel.keyboard import AdminPanelCallback
from .keyboard import (
AdminAdsCallback,
build_ads_delete_confirm_kb,
build_ads_kb,
build_ads_list_kb,
build_ads_stats_kb,
build_cancel_input_kb,
)
router = Router()
class AdminAdsState(StatesGroup):
waiting_for_new_name = State()
waiting_for_new_code = State()
@router.callback_query(AdminPanelCallback.filter(F.action == "ads"), IsAdminFilter())
async def handle_ads_menu(callback_query: CallbackQuery):
await callback_query.message.edit_text(
text="π ΠΠ½Π°Π»ΠΈΡΠΈΠΊΠ° ΡΠ΅ΠΊΠ»Π°ΠΌΡ:", reply_markup=build_ads_kb()
)
@router.callback_query(AdminAdsCallback.filter(F.action == "create"), IsAdminFilter())
async def handle_ads_create(callback_query: CallbackQuery, state: FSMContext):
await state.set_state(AdminAdsState.waiting_for_new_name)
await callback_query.message.edit_text(
"π ΠΠ²Π΅Π΄ΠΈΡΠ΅ Π½Π°Π·Π²Π°Π½ΠΈΠ΅ Π½ΠΎΠ²ΠΎΠΉ ΡΡΡΠ»ΠΊΠΈ:", reply_markup=build_cancel_input_kb()
)
@router.message(AdminAdsState.waiting_for_new_name, IsAdminFilter())
async def handle_ads_name_input(message: Message, state: FSMContext):
name = message.text.strip()
await state.update_data(name=name)
await state.set_state(AdminAdsState.waiting_for_new_code)
await message.answer(
f"π ΠΠ²Π΅Π΄ΠΈΡΠ΅ ΠΊΠΎΠ΄ ΡΡΡΠ»ΠΊΠΈ Π΄Π»Ρ: {name}.",
reply_markup=build_cancel_input_kb(),
)
@router.message(AdminAdsState.waiting_for_new_code, IsAdminFilter())
async def handle_ads_code_input(
message: Message, state: FSMContext, session: AsyncSession
):
code = message.text.strip()
data = await state.get_data()
name = data["name"]
code_with_prefix = f"utm_{code}"
try:
await create_tracking_source(
name=name,
code=code_with_prefix,
type_="utm",
created_by=message.from_user.id,
session=session,
)
stats = await get_tracking_source_stats(session, code_with_prefix)
if not stats:
await message.answer("β ΠΡΡΠΎΡΠ½ΠΈΠΊ Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½ ΠΈΠ»ΠΈ Π½Π΅ ΡΠΎΠ΄Π΅ΡΠΆΠΈΡ Π΄Π°Π½Π½ΡΡ
.")
return
msg = format_ads_stats(stats, USERNAME_BOT)
await message.answer(
text=msg,
reply_markup=build_ads_stats_kb(code_with_prefix),
)
except Exception as e:
logger.error(f"ΠΡΠΈΠ±ΠΊΠ° ΠΏΡΠΈ ΡΠΎΠ·Π΄Π°Π½ΠΈΠΈ ΡΡΡΠ»ΠΊΠΈ: {e}", exc_info=True)
await message.answer("β ΠΡΠΎΠΈΠ·ΠΎΡΠ»Π° ΠΎΡΠΈΠ±ΠΊΠ° ΠΏΡΠΈ ΡΠΎΠ·Π΄Π°Π½ΠΈΠΈ ΡΡΡΠ»ΠΊΠΈ.")
finally:
await state.clear()
@router.callback_query(AdminAdsCallback.filter(F.action == "list"), IsAdminFilter())
async def handle_ads_list(callback_query: CallbackQuery, session: AsyncSession):
try:
result = await session.execute(
select(TrackingSource).order_by(TrackingSource.created_at.desc())
)
ads = result.scalars().all()
reply_markup = build_ads_list_kb(ads, current_page=1, total_pages=1)
await callback_query.message.edit_text(
"π ΠΡΠ±Π΅ΡΠΈΡΠ΅ ΡΡΡΠ»ΠΊΡ Π΄Π»Ρ ΠΏΡΠΎΡΠΌΠΎΡΡΠ° ΡΡΠ°ΡΠΈΡΡΠΈΠΊΠΈ:", reply_markup=reply_markup
)
except Exception as e:
logger.error(f"ΠΡΠΈΠ±ΠΊΠ° ΠΏΡΠΈ ΠΏΠΎΠ»ΡΡΠ΅Π½ΠΈΠΈ ΡΠΏΠΈΡΠΊΠ° UTM: {e}", exc_info=True)
await callback_query.message.edit_text(
"β ΠΡΠΎΠΈΠ·ΠΎΡΠ»Π° ΠΎΡΠΈΠ±ΠΊΠ° ΠΏΡΠΈ ΠΏΠΎΠ»ΡΡΠ΅Π½ΠΈΠΈ ΡΠΏΠΈΡΠΊΠ°."
)
@router.callback_query(AdminAdsCallback.filter(F.action == "view"), IsAdminFilter())
async def handle_ads_view(
callback_query: CallbackQuery,
callback_data: AdminAdsCallback,
session: AsyncSession,
):
code = callback_data.code
try:
stats = await get_tracking_source_stats(session, code)
if not stats:
await callback_query.message.edit_text(
"β ΠΡΡΠΎΡΠ½ΠΈΠΊ Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½ ΠΈΠ»ΠΈ Π½Π΅ ΡΠΎΠ΄Π΅ΡΠΆΠΈΡ Π΄Π°Π½Π½ΡΡ
."
)
return
msg = format_ads_stats(stats, USERNAME_BOT)
await callback_query.message.edit_text(
text=msg, reply_markup=build_ads_stats_kb(code)
)
except Exception as e:
logger.error(f"ΠΡΠΈΠ±ΠΊΠ° ΠΏΡΠΈ ΠΏΡΠΎΡΠΌΠΎΡΡΠ΅ ΡΡΠ°ΡΠΈΡΡΠΈΠΊΠΈ: {e}", exc_info=True)
await callback_query.message.edit_text("β ΠΡΠΈΠ±ΠΊΠ° ΠΏΡΠΈ ΠΏΠΎΠ»ΡΡΠ΅Π½ΠΈΠΈ ΡΡΠ°ΡΠΈΡΡΠΈΠΊΠΈ.")
@router.callback_query(
AdminAdsCallback.filter(F.action == "delete_confirm"), IsAdminFilter()
)
async def handle_ads_delete_confirm(
callback_query: CallbackQuery, callback_data: AdminAdsCallback
):
code = callback_data.code
await callback_query.message.edit_text(
text=f"ΠΡ ΡΠ²Π΅ΡΠ΅Π½Ρ, ΡΡΠΎ Ρ
ΠΎΡΠΈΡΠ΅ ΡΠ΄Π°Π»ΠΈΡΡ ΡΡΡΠ»ΠΊΡ {code}?",
reply_markup=build_ads_delete_confirm_kb(code),
)
@router.callback_query(AdminAdsCallback.filter(F.action == "delete"), IsAdminFilter())
async def handle_ads_delete(
callback_query: CallbackQuery,
callback_data: AdminAdsCallback,
session: AsyncSession,
):
code = callback_data.code
try:
await session.execute(
update(User).where(User.source_code == code).values(source_code=None)
)
await session.execute(delete(TrackingSource).where(TrackingSource.code == code))
await session.commit()
await callback_query.message.edit_text(
f"ποΈ Π‘ΡΡΠ»ΠΊΠ° {code} ΡΠ΄Π°Π»Π΅Π½Π°.",
reply_markup=build_ads_kb(),
)
except Exception as e:
logger.error(f"ΠΡΠΈΠ±ΠΊΠ° ΠΏΡΠΈ ΡΠ΄Π°Π»Π΅Π½ΠΈΠΈ ΠΌΠ΅ΡΠΊΠΈ {code}: {e}", exc_info=True)
await callback_query.message.edit_text("β ΠΠ΅ ΡΠ΄Π°Π»ΠΎΡΡ ΡΠ΄Π°Π»ΠΈΡΡ ΡΡΡΠ»ΠΊΡ.")
@router.callback_query(
AdminAdsCallback.filter(F.action == "cancel_input"), IsAdminFilter()
)
async def handle_ads_cancel_input(callback_query: CallbackQuery, state: FSMContext):
await state.clear()
await callback_query.message.edit_text(
text="π ΠΠ½Π°Π»ΠΈΡΠΈΠΊΠ° ΡΠ΅ΠΊΠ»Π°ΠΌΡ:", reply_markup=build_ads_kb()
)
def format_ads_stats(stats: dict, username_bot: str) -> str:
return (
f"π Π‘ΡΠ°ΡΠΈΡΡΠΈΠΊΠ° ΠΏΠΎ ΡΠ΅ΠΊΠ»Π°ΠΌΠ½ΠΎΠΉ ΡΡΡΠ»ΠΊΠ΅\n\n"
f"π ΠΠ°Π·Π²Π°Π½ΠΈΠ΅: {stats['name']}\n"
f"π Π‘ΡΡΠ»ΠΊΠ°: https://t.me/{username_bot}?start={stats['code']}\n"
f"π Π‘ΠΎΠ·Π΄Π°Π½Π°: {stats['created_at'].strftime('%d.%m.%Y %H:%M')}\n\n"
f"π‘ ΠΠΊΡΠΈΠ²Π½ΠΎΡΡΡ:\n"
f"β π Π Π΅Π³ΠΈΡΡΡΠ°ΡΠΈΠΉ: {stats.get('registrations', 0)}\n"
f"β π§ͺ Π’ΡΠΈΠ°Π»ΠΎΠ²: {stats.get('trials', 0)}\n\n"
f"π° Π€ΠΈΠ½Π°Π½ΡΠΎΠ²Π°Ρ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΡ:\n"
f"β π³ ΠΠΎΠΊΡΠΏΠΎΠΊ: {stats.get('payments', 0)}\n"
f"β πΈ Π‘ΡΠΌΠΌΠ°: {round(stats.get('total_amount', 0), 2)} β½\n\n"
f"ΠΡΠΎΡΠΌΠΎΡΡ ΡΡΠ°ΡΠΈΡΡΠΈΠΊΠΈ ΠΈ ΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΠ΅ ΡΠ΅ΠΊΠ»Π°ΠΌΠ½ΡΠΌΠΈ ΡΡΡΠ»ΠΊΠ°ΠΌΠΈ."
)