fixes
Some checks reported errors
continuous-integration/drone Build encountered an error

This commit is contained in:
2025-11-16 12:36:54 +09:00
parent eb3f3807fd
commit 31c4c5382a

View File

@@ -613,6 +613,137 @@ async def process_add_participant(message: Message, state: FSMContext):
)
@admin_router.callback_query(F.data == "admin_remove_participant")
async def remove_participant_start(callback: CallbackQuery):
"""Начало процесса удаления участника"""
if not is_admin(callback.from_user.id):
await callback.answer("❌ Недостаточно прав", show_alert=True)
return
async with async_session_maker() as session:
lotteries = await LotteryService.get_all_lotteries(session, limit=20)
if not lotteries:
await callback.message.edit_text(
"❌ Нет розыгрышей в системе",
reply_markup=InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(text="🔙 Назад", callback_data="admin_participants")]
])
)
return
buttons = []
for lottery in lotteries:
buttons.append([
InlineKeyboardButton(
text=f"{'' if lottery.is_active else '🔴'} {lottery.title}",
callback_data=f"admin_remove_part_from_{lottery.id}"
)
])
buttons.append([InlineKeyboardButton(text="🔙 Назад", callback_data="admin_participants")])
await callback.message.edit_text(
" Удалить участника из розыгрыша\n\nВыберите розыгрыш:",
reply_markup=InlineKeyboardMarkup(inline_keyboard=buttons)
)
@admin_router.callback_query(F.data.startswith("admin_remove_part_from_"))
async def remove_participant_select_lottery(callback: CallbackQuery, state: FSMContext):
"""Выбор розыгрыша для удаления участника"""
if not is_admin(callback.from_user.id):
await callback.answer("❌ Недостаточно прав", show_alert=True)
return
lottery_id = int(callback.data.split("_")[-1])
async with async_session_maker() as session:
lottery = await LotteryService.get_lottery(session, lottery_id)
if not lottery:
await callback.answer("❌ Розыгрыш не найден", show_alert=True)
return
participant_count = await ParticipationService.get_participants_count(session, lottery_id)
await state.update_data(remove_participant_lottery_id=lottery_id)
await state.set_state(AdminStates.remove_participant_user)
await callback.message.edit_text(
f" Удалить участника из розыгрыша\n\n"
f"🎯 Розыгрыш: {lottery.title}\n"
f"👥 Участников: {participant_count}\n\n"
f"Отправьте Telegram ID пользователя для удаления:",
reply_markup=InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(text="❌ Отмена", callback_data="admin_remove_participant")]
])
)
@admin_router.message(StateFilter(AdminStates.remove_participant_user))
async def process_remove_participant(message: Message, state: FSMContext):
"""Обработка удаления участника"""
if not is_admin(message.from_user.id):
await message.answer("❌ Недостаточно прав")
return
data = await state.get_data()
lottery_id = data.get("remove_participant_lottery_id")
try:
telegram_id = int(message.text.strip())
except ValueError:
await message.answer(
"❌ Неверный формат. Отправьте числовой Telegram ID.",
reply_markup=InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(text="❌ Отмена", callback_data="admin_remove_participant")]
])
)
return
async with async_session_maker() as session:
user = await UserService.get_user_by_telegram_id(session, telegram_id)
if not user:
await message.answer(
f"❌ Пользователь с ID {telegram_id} не найден в системе",
reply_markup=InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(text="🔙 Назад", callback_data="admin_participants")]
])
)
await state.clear()
return
lottery = await LotteryService.get_lottery(session, lottery_id)
if not lottery:
await message.answer("❌ Розыгрыш не найден")
await state.clear()
return
removed = await ParticipationService.remove_participant(session, lottery_id, user.id)
await state.clear()
username = f"@{user.username}" if user.username else "Нет username"
if removed:
await message.answer(
f"✅ Участник удалён из розыгрыша!\n\n"
f"👤 {user.first_name} {user.last_name or ''}\n"
f"📱 Username: {username}\n"
f"🆔 ID: {user.telegram_id}\n"
f"🎯 Розыгрыш: {lottery.title}",
reply_markup=InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(text="👥 К управлению участниками", callback_data="admin_participants")]
])
)
else:
await message.answer(
f"⚠️ Пользователь {user.first_name} не участвует в этом розыгрыше",
reply_markup=InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(text="👥 К управлению участниками", callback_data="admin_participants")]
])
)
@admin_router.callback_query(F.data == "admin_list_all_participants")
async def list_all_participants(callback: CallbackQuery):
"""Список всех участников"""
@@ -1541,6 +1672,25 @@ async def start_edit_lottery(callback: CallbackQuery, state: FSMContext):
await callback.message.edit_text(text, reply_markup=InlineKeyboardMarkup(inline_keyboard=buttons))
@admin_router.callback_query(F.data.startswith("admin_edit_"))
async def redirect_to_edit_lottery(callback: CallbackQuery, state: FSMContext):
"""Редирект на редактирование розыгрыша из детального просмотра"""
if not is_admin(callback.from_user.id):
await callback.answer("❌ Недостаточно прав", show_alert=True)
return
# Извлекаем lottery_id из callback_data (формат admin_edit_123)
parts = callback.data.split("_")
if len(parts) == 3: # admin_edit_123
lottery_id = int(parts[2])
# Подменяем callback_data для обработки существующим хэндлером
callback.data = f"admin_edit_lottery_select_{lottery_id}"
await choose_edit_field(callback, state)
else:
# Если формат другой, то это уже правильный callback
await choose_edit_field(callback, state)
@admin_router.callback_query(F.data.startswith("admin_edit_lottery_select_"))
async def choose_edit_field(callback: CallbackQuery, state: FSMContext):
"""Выбор поля для редактирования"""
@@ -2037,6 +2187,360 @@ async def process_winner_user(message: Message, state: FSMContext):
)
@admin_router.callback_query(F.data == "admin_list_winners")
async def list_all_winners(callback: CallbackQuery):
"""Список всех победителей"""
if not is_admin(callback.from_user.id):
await callback.answer("❌ Недостаточно прав", show_alert=True)
return
async with async_session_maker() as session:
# Получаем все розыгрыши с победителями
from sqlalchemy import select
result = await session.execute(
select(Winner)
.options(selectinload(Winner.user), selectinload(Winner.lottery))
.order_by(Winner.created_at.desc())
.limit(50)
)
winners = result.scalars().all()
if not winners:
await callback.message.edit_text(
"📋 Список победителей пуст\n\nПока не было проведено ни одного розыгрыша.",
reply_markup=InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(text="🔙 Назад", callback_data="admin_winners")]
])
)
return
text = "👑 Список победителей\n\n"
# Группируем победителей по розыгрышам
lotteries_dict = {}
for winner in winners:
lottery_id = winner.lottery_id
if lottery_id not in lotteries_dict:
lotteries_dict[lottery_id] = {
'lottery': winner.lottery,
'winners': []
}
lotteries_dict[lottery_id]['winners'].append(winner)
# Выводим информацию
for lottery_id, data in list(lotteries_dict.items())[:10]:
lottery = data['lottery']
winners_list = data['winners']
text += f"🎯 {lottery.title}\n"
text += f"📅 {lottery.created_at.strftime('%d.%m.%Y')}\n"
for winner in sorted(winners_list, key=lambda w: w.place):
username = f"@{winner.user.username}" if winner.user.username else winner.user.first_name
manual_mark = "🔧" if winner.is_manual else "🎲"
text += f" {manual_mark} {winner.place} место: {username} - {winner.prize}\n"
text += "\n"
if len(lotteries_dict) > 10:
text += f"\n... и ещё {len(lotteries_dict) - 10} розыгрышей"
await callback.message.edit_text(
text,
reply_markup=InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(text="🔄 Обновить", callback_data="admin_list_winners")],
[InlineKeyboardButton(text="🔙 Назад", callback_data="admin_winners")]
])
)
@admin_router.callback_query(F.data == "admin_edit_winner")
async def edit_winner_start(callback: CallbackQuery):
"""Начало редактирования победителя"""
if not is_admin(callback.from_user.id):
await callback.answer("❌ Недостаточно прав", show_alert=True)
return
async with async_session_maker() as session:
from sqlalchemy import select
result = await session.execute(
select(Lottery)
.join(Winner)
.distinct()
.order_by(Lottery.created_at.desc())
.limit(20)
)
lotteries_with_winners = result.scalars().all()
if not lotteries_with_winners:
await callback.message.edit_text(
"❌ Нет розыгрышей с победителями для редактирования",
reply_markup=InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(text="🔙 Назад", callback_data="admin_winners")]
])
)
return
text = "📝 Редактировать победителя\n\nВыберите розыгрыш:\n\n"
buttons = []
for lottery in lotteries_with_winners:
buttons.append([
InlineKeyboardButton(
text=f"🎯 {lottery.title}",
callback_data=f"admin_edit_winner_lottery_{lottery.id}"
)
])
buttons.append([InlineKeyboardButton(text="🔙 Назад", callback_data="admin_winners")])
await callback.message.edit_text(
text,
reply_markup=InlineKeyboardMarkup(inline_keyboard=buttons)
)
@admin_router.callback_query(F.data.startswith("admin_edit_winner_lottery_"))
async def edit_winner_select_place(callback: CallbackQuery, state: FSMContext):
"""Выбор места победителя для редактирования"""
if not is_admin(callback.from_user.id):
await callback.answer("❌ Недостаточно прав", show_alert=True)
return
lottery_id = int(callback.data.split("_")[-1])
async with async_session_maker() as session:
lottery = await LotteryService.get_lottery(session, lottery_id)
winners = await LotteryService.get_winners(session, lottery_id)
if not winners:
await callback.answer("❌ Нет победителей для редактирования", show_alert=True)
return
text = f"📝 Редактировать победителя\n\n🎯 {lottery.title}\n\nВыберите место:\n\n"
buttons = []
for winner in winners:
username = f"@{winner.user.username}" if winner.user.username else winner.user.first_name
buttons.append([
InlineKeyboardButton(
text=f"🏆 {winner.place} место: {username} - {winner.prize}",
callback_data=f"admin_edit_winner_id_{winner.id}"
)
])
buttons.append([InlineKeyboardButton(text="🔙 Назад", callback_data="admin_edit_winner")])
await callback.message.edit_text(text, reply_markup=InlineKeyboardMarkup(inline_keyboard=buttons))
@admin_router.callback_query(F.data.startswith("admin_edit_winner_id_"))
async def edit_winner_details(callback: CallbackQuery):
"""Показать детали победителя (пока просто информационное сообщение)"""
if not is_admin(callback.from_user.id):
await callback.answer("❌ Недостаточно прав", show_alert=True)
return
winner_id = int(callback.data.split("_")[-1])
async with async_session_maker() as session:
from sqlalchemy import select
result = await session.execute(
select(Winner)
.options(selectinload(Winner.user), selectinload(Winner.lottery))
.where(Winner.id == winner_id)
)
winner = result.scalar_one_or_none()
if not winner:
await callback.answer("❌ Победитель не найден", show_alert=True)
return
username = f"@{winner.user.username}" if winner.user.username else winner.user.first_name
manual_mark = "🔧 Установлен вручную" if winner.is_manual else "🎲 Выбран случайно"
text = f"📝 Информация о победителе\n\n"
text += f"🎯 Розыгрыш: {winner.lottery.title}\n"
text += f"🏆 Место: {winner.place}\n"
text += f"💰 Приз: {winner.prize}\n"
text += f"👤 Пользователь: {username}\n"
text += f"🆔 ID: {winner.user.telegram_id}\n"
text += f"📊 Тип: {manual_mark}\n"
text += f"📅 Дата: {winner.created_at.strftime('%d.%m.%Y %H:%M')}\n\n"
text += " Редактирование победителей доступно через удаление и повторное добавление."
await callback.message.edit_text(
text,
reply_markup=InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(text="🔙 Назад", callback_data=f"admin_edit_winner_lottery_{winner.lottery_id}")]
])
)
@admin_router.callback_query(F.data == "admin_remove_winner")
async def remove_winner_start(callback: CallbackQuery):
"""Начало удаления победителя"""
if not is_admin(callback.from_user.id):
await callback.answer("❌ Недостаточно прав", show_alert=True)
return
async with async_session_maker() as session:
from sqlalchemy import select
result = await session.execute(
select(Lottery)
.join(Winner)
.distinct()
.order_by(Lottery.created_at.desc())
.limit(20)
)
lotteries_with_winners = result.scalars().all()
if not lotteries_with_winners:
await callback.message.edit_text(
"❌ Нет розыгрышей с победителями для удаления",
reply_markup=InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(text="🔙 Назад", callback_data="admin_winners")]
])
)
return
text = "❌ Удалить победителя\n\nВыберите розыгрыш:\n\n"
buttons = []
for lottery in lotteries_with_winners:
buttons.append([
InlineKeyboardButton(
text=f"🎯 {lottery.title}",
callback_data=f"admin_remove_winner_lottery_{lottery.id}"
)
])
buttons.append([InlineKeyboardButton(text="🔙 Назад", callback_data="admin_winners")])
await callback.message.edit_text(
text,
reply_markup=InlineKeyboardMarkup(inline_keyboard=buttons)
)
@admin_router.callback_query(F.data.startswith("admin_remove_winner_lottery_"))
async def remove_winner_select_place(callback: CallbackQuery):
"""Выбор победителя для удаления"""
if not is_admin(callback.from_user.id):
await callback.answer("❌ Недостаточно прав", show_alert=True)
return
lottery_id = int(callback.data.split("_")[-1])
async with async_session_maker() as session:
lottery = await LotteryService.get_lottery(session, lottery_id)
winners = await LotteryService.get_winners(session, lottery_id)
if not winners:
await callback.answer("❌ Нет победителей для удаления", show_alert=True)
return
text = f"❌ Удалить победителя\n\n🎯 {lottery.title}\n\nВыберите победителя для удаления:\n\n"
buttons = []
for winner in winners:
username = f"@{winner.user.username}" if winner.user.username else winner.user.first_name
buttons.append([
InlineKeyboardButton(
text=f"🏆 {winner.place} место: {username} - {winner.prize}",
callback_data=f"admin_confirm_remove_winner_{winner.id}"
)
])
buttons.append([InlineKeyboardButton(text="🔙 Назад", callback_data="admin_remove_winner")])
await callback.message.edit_text(text, reply_markup=InlineKeyboardMarkup(inline_keyboard=buttons))
@admin_router.callback_query(F.data.startswith("admin_confirm_remove_winner_"))
async def confirm_remove_winner(callback: CallbackQuery):
"""Подтверждение удаления победителя"""
if not is_admin(callback.from_user.id):
await callback.answer("❌ Недостаточно прав", show_alert=True)
return
winner_id = int(callback.data.split("_")[-1])
async with async_session_maker() as session:
from sqlalchemy import select
result = await session.execute(
select(Winner)
.options(selectinload(Winner.user), selectinload(Winner.lottery))
.where(Winner.id == winner_id)
)
winner = result.scalar_one_or_none()
if not winner:
await callback.answer("❌ Победитель не найден", show_alert=True)
return
username = f"@{winner.user.username}" if winner.user.username else winner.user.first_name
text = f"⚠️ Подтверждение удаления\n\n"
text += f"Вы действительно хотите удалить победителя?\n\n"
text += f"🎯 Розыгрыш: {winner.lottery.title}\n"
text += f"🏆 Место: {winner.place}\n"
text += f"👤 Пользователь: {username}\n"
text += f"💰 Приз: {winner.prize}\n\n"
text += "⚠️ Это действие необратимо!"
buttons = [
[
InlineKeyboardButton(text="✅ Да, удалить", callback_data=f"admin_do_remove_winner_{winner_id}"),
InlineKeyboardButton(text="❌ Отмена", callback_data=f"admin_remove_winner_lottery_{winner.lottery_id}")
]
]
await callback.message.edit_text(text, reply_markup=InlineKeyboardMarkup(inline_keyboard=buttons))
@admin_router.callback_query(F.data.startswith("admin_do_remove_winner_"))
async def do_remove_winner(callback: CallbackQuery):
"""Выполнение удаления победителя"""
if not is_admin(callback.from_user.id):
await callback.answer("❌ Недостаточно прав", show_alert=True)
return
winner_id = int(callback.data.split("_")[-1])
async with async_session_maker() as session:
from sqlalchemy import select, delete
# Получаем информацию о победителе перед удалением
result = await session.execute(
select(Winner)
.options(selectinload(Winner.user), selectinload(Winner.lottery))
.where(Winner.id == winner_id)
)
winner = result.scalar_one_or_none()
if not winner:
await callback.answer("❌ Победитель не найден", show_alert=True)
return
lottery_id = winner.lottery_id
username = f"@{winner.user.username}" if winner.user.username else winner.user.first_name
# Удаляем победителя
await session.execute(delete(Winner).where(Winner.id == winner_id))
await session.commit()
await callback.message.edit_text(
f"✅ Победитель удалён!\n\n"
f"👤 {username}\n"
f"🏆 Место: {winner.place}\n"
f"💰 Приз: {winner.prize}",
reply_markup=InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(text="👑 К управлению победителями", callback_data="admin_winners")]
])
)
# ======================
# ПРОВЕДЕНИЕ РОЗЫГРЫША
# ======================
@@ -2236,6 +2740,189 @@ async def show_admin_settings(callback: CallbackQuery):
await callback.message.edit_text(text, reply_markup=InlineKeyboardMarkup(inline_keyboard=buttons))
@admin_router.callback_query(F.data == "admin_export_data")
async def export_data(callback: CallbackQuery):
"""Экспорт данных из системы"""
if not is_admin(callback.from_user.id):
await callback.answer("❌ Недостаточно прав", show_alert=True)
return
async with async_session_maker() as session:
from sqlalchemy import func, select
# Собираем статистику
users_count = await session.scalar(select(func.count(User.id)))
lotteries_count = await session.scalar(select(func.count(Lottery.id)))
participations_count = await session.scalar(select(func.count(Participation.id)))
winners_count = await session.scalar(select(func.count(Winner.id)))
import json
from datetime import datetime
# Формируем данные для экспорта
export_info = {
"export_date": datetime.now().isoformat(),
"statistics": {
"users": users_count,
"lotteries": lotteries_count,
"participations": participations_count,
"winners": winners_count
}
}
# Сохраняем в файл
filename = f"export_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
with open(filename, 'w', encoding='utf-8') as f:
json.dump(export_info, f, ensure_ascii=False, indent=2)
text = "💾 Экспорт данных\n\n"
text += f"📊 Статистика:\n"
text += f"👥 Пользователей: {users_count}\n"
text += f"🎯 Розыгрышей: {lotteries_count}\n"
text += f"🎫 Участий: {participations_count}\n"
text += f"🏆 Победителей: {winners_count}\n\n"
text += f"✅ Данные экспортированы в файл:\n{filename}"
await callback.message.edit_text(
text,
reply_markup=InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(text="🔄 Экспортировать снова", callback_data="admin_export_data")],
[InlineKeyboardButton(text="🔙 Назад", callback_data="admin_settings")]
])
)
@admin_router.callback_query(F.data == "admin_cleanup")
async def cleanup_old_data(callback: CallbackQuery):
"""Очистка старых данных"""
if not is_admin(callback.from_user.id):
await callback.answer("❌ Недостаточно прав", show_alert=True)
return
text = "🧹 Очистка старых данных\n\n"
text += "Выберите тип данных для очистки:\n\n"
text += "⚠️ Внимание! Это действие необратимо!"
buttons = [
[InlineKeyboardButton(text="🗑️ Завершённые розыгрыши (>30 дней)", callback_data="admin_cleanup_old_lotteries")],
[InlineKeyboardButton(text="👻 Неактивные пользователи (>90 дней)", callback_data="admin_cleanup_inactive_users")],
[InlineKeyboardButton(text="📋 Старые участия (>60 дней)", callback_data="admin_cleanup_old_participations")],
[InlineKeyboardButton(text="🔙 Назад", callback_data="admin_settings")]
]
await callback.message.edit_text(text, reply_markup=InlineKeyboardMarkup(inline_keyboard=buttons))
@admin_router.callback_query(F.data == "admin_cleanup_old_lotteries")
async def cleanup_old_lotteries(callback: CallbackQuery):
"""Очистка старых завершённых розыгрышей"""
if not is_admin(callback.from_user.id):
await callback.answer("❌ Недостаточно прав", show_alert=True)
return
from datetime import timedelta
cutoff_date = datetime.now() - timedelta(days=30)
async with async_session_maker() as session:
from sqlalchemy import select, delete
# Находим старые завершённые розыгрыши
result = await session.execute(
select(Lottery)
.where(
Lottery.is_completed == True,
Lottery.created_at < cutoff_date
)
)
old_lotteries = result.scalars().all()
count = len(old_lotteries)
if count == 0:
await callback.answer("✅ Нет старых розыгрышей для удаления", show_alert=True)
return
# Удаляем старые розыгрыши
for lottery in old_lotteries:
await session.delete(lottery)
await session.commit()
text = f"✅ Очистка завершена!\n\n"
text += f"🗑️ Удалено розыгрышей: {count}\n"
text += f"📅 Старше: {cutoff_date.strftime('%d.%m.%Y')}"
await callback.message.edit_text(
text,
reply_markup=InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(text="🧹 К очистке", callback_data="admin_cleanup")],
[InlineKeyboardButton(text="⚙️ К настройкам", callback_data="admin_settings")]
])
)
@admin_router.callback_query(F.data == "admin_cleanup_inactive_users")
async def cleanup_inactive_users(callback: CallbackQuery):
"""Очистка неактивных пользователей"""
if not is_admin(callback.from_user.id):
await callback.answer("❌ Недостаточно прав", show_alert=True)
return
await callback.answer(
" Функция в разработке\n\nУдаление пользователей требует дополнительной логики для сохранения целостности данных.",
show_alert=True
)
@admin_router.callback_query(F.data == "admin_cleanup_old_participations")
async def cleanup_old_participations(callback: CallbackQuery):
"""Очистка старых участий"""
if not is_admin(callback.from_user.id):
await callback.answer("❌ Недостаточно прав", show_alert=True)
return
from datetime import timedelta
cutoff_date = datetime.now() - timedelta(days=60)
async with async_session_maker() as session:
from sqlalchemy import select, delete
# Находим старые участия в завершённых розыгрышах
result = await session.execute(
select(Participation)
.join(Lottery)
.where(
Lottery.is_completed == True,
Participation.created_at < cutoff_date
)
)
old_participations = result.scalars().all()
count = len(old_participations)
if count == 0:
await callback.answer("✅ Нет старых участий для удаления", show_alert=True)
return
# Удаляем старые участия
for participation in old_participations:
await session.delete(participation)
await session.commit()
text = f"✅ Очистка завершена!\n\n"
text += f"🗑️ Удалено участий: {count}\n"
text += f"📅 Старше: {cutoff_date.strftime('%d.%m.%Y')}"
await callback.message.edit_text(
text,
reply_markup=InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(text="🧹 К очистке", callback_data="admin_cleanup")],
[InlineKeyboardButton(text="⚙️ К настройкам", callback_data="admin_settings")]
])
)
@admin_router.callback_query(F.data == "admin_system_info")
async def show_system_info(callback: CallbackQuery):
"""Системная информация"""