domovoy_bot/handlers/thanks.py
Admin ba4676b0af v1.0 MVP - Инициализация проекта 🏠
 Регистрация и верификация жильцов
 Рейтинг активности (уровни)
 Детект шпионов (Score 0-100)
 Анти-мат система (3 предупреждения → бан)
 Админ-панель с рассылкой
 Учёт квартир (несколько жильцов)
 Телефоны экстренных служб и мастеров
 Еженедельный экспорт JSON
 Прокси (socks5) для обхода РКН

Бекап: backups/versions/v1.0_mvp_2026-02-26/

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-02-27 10:54:16 +00:00

249 lines
10 KiB
Python
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.

"""
Thanks — система благодарностей пользователей
"""
import logging
from aiogram import Router, F
from aiogram.types import Message
from aiogram.filters import Command, StateFilter
from aiogram.fsm.state import State, StatesGroup
from aiogram.fsm.context import FSMContext
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from database.db import AsyncSessionLocal
from database.models import User
from services.thanks import ThanksService
from services.achievements import AchievementsService
logger = logging.getLogger(__name__)
router = Router()
# =============================================================================
# FSM для отправки благодарности
# =============================================================================
class ThankState(StatesGroup):
waiting_for_user = State()
waiting_for_message = State()
# =============================================================================
# ОТПРАВКА БЛАГОДАРНОСТИ
# =============================================================================
@router.message(Command('thank'))
@router.message(Command('спасибо'))
async def cmd_thank(message: Message, state: FSMContext):
"""
Поблагодарить пользователя
Использование: /thank @username или /thank <user_id>
"""
args = message.text.split(maxsplit=1)
if len(args) < 2:
await message.answer(
'🙏 <b>Отправить благодарность</b>\n\n'
'Использование:\n'
'<code>/thank @username</code>\n'
'или\n'
'<code>/thank <user_id></code>\n\n'
'Или ответьте на сообщение командой /thank',
parse_mode='HTML'
)
return
# Получаем пользователя
target_username = args[1].replace('@', '')
async with AsyncSessionLocal() as session:
# Ищем по username
stmt = select(User).where(User.username == target_username)
result = await session.execute(stmt)
target_user = result.scalar_one_or_none()
if not target_user:
# Пробуем по ID
try:
target_id = int(target_username)
target_user = await session.get(User, target_id)
except ValueError:
pass
if not target_user:
await message.answer(f'❌ Пользователь @{target_username} не найден')
return
# Проверяем что не сам себе
if target_user.user_id == message.from_user.id:
await message.answer('❌ Нельзя поблагодарить самого себя!')
return
# Проверяем не благодарил ли уже сегодня
thanks_service = ThanksService(session)
if await thanks_service.has_thanked_today(message.from_user.id, target_user.user_id):
await message.answer(
'⚠️ Вы уже благодарили этого пользователя сегодня!\n'
'Можно поблагодарить только один раз в день.'
)
return
# Сохраняем данные для следующего шага
await state.update_data(target_user_id=target_user.user_id)
await state.update_data(target_name=target_user.full_name or f'@{target_user.username}')
await message.answer(
f'🙏 <b>Благодарность для {target_user.full_name or f"@{target_user.username}"}</b>\n\n'
'Отправьте текст благодарности (необязательно) или "пропустить":',
parse_mode='HTML'
)
await state.set_state(ThankState.waiting_for_message)
@router.message(ThankState.waiting_for_message)
async def process_thank_message(message: Message, state: FSMContext):
"""Получили текст благодарности"""
data = await state.get_data()
target_user_id = data.get('target_user_id')
target_name = data.get('target_name')
thank_message = message.text if message.text.lower() != 'пропустить' else None
async with AsyncSessionLocal() as session:
thanks_service = ThanksService(session)
success = await thanks_service.give_thank(
message.from_user.id,
target_user_id,
thank_message
)
if success:
# Уведомляем отправителя
await message.answer(
f'✅ <b>Благодарность отправлена!</b>\n\n'
f'Получатель: {target_name}\n'
f'{"Сообщение: " + thank_message if thank_message else ""}',
parse_mode='HTML'
)
# Уведомляем получателя
try:
await message.bot.send_message(
target_user_id,
f'🙏 <b>Вам благодарность!</b>\n\n'
f'От: {message.from_user.full_name}\n'
f'{"Сообщение: " + thank_message if thank_message else "Спасибо за помощь!"}\n\n'
f'+10 к рейтингу!',
parse_mode='HTML'
)
except Exception as e:
logger.error(f'Не удалось уведомить о благодарности: {e}')
# Обновляем рейтинг получателя (+10)
target_user = await session.get(User, target_user_id)
if target_user:
target_user.rating += 10
target_user.update_level()
# Проверяем достижение "Помощник" (получил благодарность)
achievements_service = AchievementsService(session)
awarded = await achievements_service.check_and_award(
target_user_id,
helper=True # Флаг для достижения помощника
)
if 'helper' in awarded:
await message.bot.send_message(
target_user_id,
'🏆 <b>Новое достижение!</b>\n\n'
'🤝 <b>Помощник</b>\n\n'
'Получил благодарность от соседа\n\n'
'Продолжайте помогать!',
parse_mode='HTML'
)
await session.commit()
await state.clear()
# =============================================================================
# МОИ БЛАГОДАРНОСТИ
# =============================================================================
@router.message(Command('my_thanks'))
@router.message(Command('мои_спасибо'))
async def cmd_my_thanks(message: Message):
"""Показать свои благодарности"""
user_id = message.from_user.id
async with AsyncSessionLocal() as session:
thanks_service = ThanksService(session)
stats = await thanks_service.get_user_thanks(user_id)
text = '🙏 <b>Ваши благодарности</b>\n\n'
text += f'✅ Получено: {stats["received"]}\n'
text += f'📤 Отправлено: {stats["given"]}\n'
if stats['last_received']:
text += '\n<b>Последние полученные:</b>\n'
for thank in stats['last_received'][:3]:
text += f' • От user{thank["from"]}: {thank["message"] or "Спасибо!"}\n'
await message.answer(text, parse_mode='HTML')
# =============================================================================
# ТОП ПО БЛАГОДАРНОСТЯМ
# =============================================================================
@router.message(Command('thanks_top'))
@router.message(Command('топ_спасибо'))
async def cmd_thanks_top(message: Message):
"""Топ пользователей по благодарностям"""
async with AsyncSessionLocal() as session:
thanks_service = ThanksService(session)
leaderboard = await thanks_service.get_leaderboard(10)
if not leaderboard:
await message.answer(' Пока нет благодарностей')
return
text = '🙏 <b>Топ по благодарностям</b>\n\n'
medals = ['🥇', '🥈', '🥉']
for i, user in enumerate(leaderboard):
medal = medals[i] if i < 3 else f'{i+1}.'
text += f'{medal} {user["name"]} (кв. {user["apartment"]}) — {user["thanks"]} благодар.\n'
await message.answer(text, parse_mode='HTML')
# =============================================================================
# ИСТОРИЯ БЛАГОДАРНОСТЕЙ
# =============================================================================
@router.message(Command('thanks_history'))
@router.message(Command('история_спасибо'))
async def cmd_thanks_history(message: Message):
"""История благодарностей пользователя"""
user_id = message.from_user.id
async with AsyncSessionLocal() as session:
thanks_service = ThanksService(session)
history = await thanks_service.get_thanks_history(user_id)
if not history:
await message.answer(' История благодарностей пуста')
return
text = '🙏 <b>История благодарностей</b>\n\n'
for thank in history[:10]:
if thank['is_received']:
text += f'✅ Получено от user{thank["from"]}: {thank["message"] or "Спасибо!"}\n'
else:
text += f'📤 Отправлено user{thank["to"]}: {thank["message"] or "Спасибо!"}\n'
await message.answer(text, parse_mode='HTML')