diff --git a/handlers/users.py b/handlers/users.py
index 6095d02..bfd8ac4 100644
--- a/handlers/users.py
+++ b/handlers/users.py
@@ -282,33 +282,6 @@ async def cb_schedule(callback: CallbackQuery):
await callback.message.answer(text, parse_mode='HTML')
-@router.callback_query(F.data == 'announcements')
-async def cb_announcements(callback: CallbackQuery):
- """Объявления"""
- await callback.answer()
-
- async with AsyncSessionLocal() as session:
- stmt = (
- select(Announcement)
- .where(Announcement.is_active == True)
- .order_by(Announcement.is_pinned.desc(), Announcement.created_at.desc())
- .limit(10)
- )
- result = await session.execute(stmt)
- announcements = list(result.scalars().all())
-
- if not announcements:
- await callback.message.answer('📭 Объявлений нет')
- return
-
- text = '📢 Объявления\n\n'
- for ann in announcements:
- pin = '📌 ' if ann.is_pinned else ''
- text += f'{pin}{ann.title}\n{ann.text[:200]}...\n\n'
-
- await callback.message.answer(text, parse_mode='HTML')
-
-
@router.callback_query(F.data == 'main_menu')
async def cb_main_menu(callback: CallbackQuery):
"""Главное меню"""
@@ -356,3 +329,240 @@ async def msg_masters_text(message: Message):
async def msg_help_text(message: Message):
"""Текстовая команда помощь"""
await cmd_help(message)
+
+
+# =============================================================================
+# ОБРАБОТЧИКИ НОВЫХ КНОПОК МЕНЮ
+# =============================================================================
+
+from keyboards.inline import get_thank_user_keyboard, get_thank_confirm_keyboard
+from sqlalchemy import desc
+
+
+# Хранилище страниц для благодарностей
+_thank_pages: dict[int, dict] = {}
+
+
+@router.callback_query(F.data == 'achievements')
+async def cb_achievements_menu(callback: CallbackQuery):
+ """Кнопка Достижения"""
+ await callback.answer()
+ # Перенаправляем на команду /achievements
+ from handlers.achievements import cmd_achievements
+ await cmd_achievements(callback.message)
+
+
+@router.callback_query(F.data == 'events')
+async def cb_events_menu(callback: CallbackQuery):
+ """Кнопка События"""
+ await callback.answer()
+ # Перенаправляем на команду /events
+ from handlers.events import cmd_events
+ await cmd_events(callback.message)
+
+
+@router.callback_query(F.data == 'payments')
+async def cb_payments_menu(callback: CallbackQuery):
+ """Кнопка Платежи"""
+ await callback.answer()
+ # Перенаправляем на команду /payments
+ from handlers.payments import cmd_payments
+ await cmd_payments(callback.message)
+
+
+@router.callback_query(F.data == 'polls')
+async def cb_polls_menu(callback: CallbackQuery):
+ """Кнопка Опросы"""
+ await callback.answer()
+ # Перенаправляем на команду /polls
+ from handlers.polls import cmd_polls
+ await cmd_polls(callback.message)
+
+
+@router.callback_query(F.data == 'thank_user')
+async def cb_thank_user_menu(callback: CallbackQuery):
+ """Кнопка Поблагодарить - выбор пользователя"""
+ await callback.answer()
+
+ async with AsyncSessionLocal() as session:
+ # Показываем активных пользователей (по количеству сообщений)
+ stmt = select(User).order_by(desc(User.message_count)).limit(100)
+ result = await session.execute(stmt)
+ users = list(result.scalars().all())
+
+ if not users:
+ await callback.message.answer('📭 Пользователей нет')
+ return
+
+ # Сохраняем список для сессии
+ _thank_pages[callback.from_user.id] = {
+ 'users': users,
+ 'page': 0
+ }
+
+ await callback.message.answer(
+ '🙏 Поблагодарить пользователя\n\n'
+ 'Выберите пользователя которому хотите сказать спасибо:\n'
+ '(+10 к рейтингу)',
+ reply_markup=get_thank_user_keyboard(users, page=0),
+ parse_mode='HTML'
+ )
+
+
+@router.callback_query(F.data.startswith('thank_page_'))
+async def cb_thank_page(callback: CallbackQuery):
+ """Переключение страницы в списке для благодарности"""
+ await callback.answer()
+
+ page = int(callback.data.split('_')[-1])
+ user_data = _thank_pages.get(callback.from_user.id)
+
+ if not user_data:
+ await callback.message.answer('❌ Сессия истекла. Выберите "Поблагодарить" заново')
+ return
+
+ users = user_data['users']
+ user_data['page'] = page
+
+ await callback.message.edit_reply_markup(
+ reply_markup=get_thank_user_keyboard(users, page=page)
+ )
+
+
+@router.callback_query(F.data.startswith('thank_select_'))
+async def cb_thank_select(callback: CallbackQuery):
+ """Выбор конкретного пользователя для благодарности"""
+ await callback.answer()
+
+ user_id = int(callback.data.split('_')[-1])
+
+ async with AsyncSessionLocal() as session:
+ stmt = select(User).where(User.user_id == user_id)
+ result = await session.execute(stmt)
+ user = result.scalar_one_or_none()
+
+ if not user:
+ await callback.message.answer('❌ Пользователь не найден')
+ return
+
+ name = user.full_name or f'user{user_id}'
+ apt = f' (кв. {user.apartment})' if user.apartment else ''
+
+ await callback.message.answer(
+ f'🙏 Поблагодарить пользователя?\n\n'
+ f'{name}{apt}\n\n'
+ f'Пользователь получит +10 к рейтингу.',
+ reply_markup=get_thank_confirm_keyboard(user_id),
+ parse_mode='HTML'
+ )
+
+
+@router.callback_query(F.data.startswith('thank_confirm_'))
+async def cb_thank_confirm(callback: CallbackQuery):
+ """Подтверждение благодарности"""
+ await callback.answer()
+
+ target_user_id = int(callback.data.split('_')[-1])
+ from_user_id = callback.from_user.id
+
+ # Проверяем что не благодарит сам себя
+ if target_user_id == from_user_id:
+ await callback.message.answer('❌ Нельзя поблагодарить самого себя!')
+ return
+
+ async with AsyncSessionLocal() as session:
+ # Находим того кого благодарят
+ stmt = select(User).where(User.user_id == target_user_id)
+ result = await session.execute(stmt)
+ target_user = result.scalar_one_or_none()
+
+ # Находим того кто благодарит
+ stmt = select(User).where(User.user_id == from_user_id)
+ result = await session.execute(stmt)
+ from_user = result.scalar_one_or_none()
+
+ if not target_user or not from_user:
+ await callback.message.answer('❌ Пользователь не найден')
+ return
+
+ # Проверяли ли уже сегодня
+ from datetime import datetime, timedelta
+ from database.models import Thanks
+
+ today = datetime.utcnow().date()
+ stmt = select(Thanks).where(
+ Thanks.from_user_id == from_user_id,
+ Thanks.created_at >= datetime.combine(today, datetime.min.time())
+ )
+ result = await session.execute(stmt)
+ existing = result.scalar_one_or_none()
+
+ if existing:
+ await callback.message.answer('ℹ️ Вы уже благодарили кого-то сегодня. Можно раз в сутки.')
+ return
+
+ # Создаём благодарность
+ thanks = Thanks(
+ from_user_id=from_user_id,
+ to_user_id=target_user_id,
+ message='Благодарность через меню',
+ )
+ session.add(thanks)
+
+ # +10 к рейтингу
+ target_user.rating += 10
+
+ await session.commit()
+
+ name = target_user.full_name or f'user{target_user_id}'
+
+ await callback.message.answer(
+ f'✅ Спасибо отправлено!\n\n'
+ f'{name} получил +10 к рейтингу.\n'
+ f'Вы можете поблагодарить ещё раз через 24 часа.',
+ parse_mode='HTML'
+ )
+
+
+@router.callback_query(F.data == 'announcements')
+async def cb_announcements_menu(callback: CallbackQuery):
+ """Кнопка Объявления"""
+ await callback.answer()
+ # Перенаправляем на команду /ads
+ from handlers.ads import cmd_ads
+ await cmd_ads(callback.message)
+
+
+@router.callback_query(F.data == 'schedule')
+async def cb_schedule_menu(callback: CallbackQuery):
+ """Кнопка График отключений"""
+ await callback.answer()
+ # Перенаправляем на команду /график
+ from handlers.schedule import cmd_schedule
+ await cmd_schedule(callback.message)
+
+
+@router.callback_query(F.data == 'my_apartment')
+async def cb_my_apartment_menu(callback: CallbackQuery):
+ """Кнопка Моя квартира"""
+ await callback.answer()
+
+ async with AsyncSessionLocal() as session:
+ stmt = select(User).where(User.user_id == callback.from_user.id)
+ result = await session.execute(stmt)
+ user = result.scalar_one_or_none()
+
+ if not user:
+ await callback.message.answer('❌ Вы не зарегистрированы')
+ return
+
+ apt = user.apartment or '❓ Не указана'
+ verified = '✅' if user.verified else '⏳'
+
+ text = f'🏠 Ваша квартира\n\n'
+ text += f'Номер: {apt}\n'
+ text += f'Статус: {verified} {"Верифицирован" if user.verified else "Не верифицирован"}\n'
+ text += f'Рейтинг: {user.rating}\n'
+ text += f'Уровень: {user.level}\n'
+
+ await callback.message.answer(text, parse_mode='HTML')
diff --git a/keyboards/inline.py b/keyboards/inline.py
index 8ff4af8..d71595f 100644
--- a/keyboards/inline.py
+++ b/keyboards/inline.py
@@ -8,7 +8,7 @@ from aiogram.utils.keyboard import InlineKeyboardBuilder
def get_main_menu() -> InlineKeyboardMarkup:
"""Главное меню жильца"""
builder = InlineKeyboardBuilder()
-
+
builder.row(
InlineKeyboardButton(text='📞 Экстренные телефоны', callback_data='phones'),
)
@@ -18,15 +18,74 @@ def get_main_menu() -> InlineKeyboardMarkup:
)
builder.row(
InlineKeyboardButton(text='📅 График отключений', callback_data='schedule'),
- InlineKeyboardButton(text='📢 Объявления', callback_data='announcements'),
+ )
+ builder.row(
+ InlineKeyboardButton(text='🏆 Достижения', callback_data='achievements'),
+ InlineKeyboardButton(text='📅 События', callback_data='events'),
+ )
+ builder.row(
+ InlineKeyboardButton(text='💰 Платежи', callback_data='payments'),
+ InlineKeyboardButton(text='📋 Опросы', callback_data='polls'),
)
builder.row(
InlineKeyboardButton(text='🔍 Моя квартира', callback_data='my_apartment'),
- InlineKeyboardButton(text='📣 Подать объявление', callback_data='submit_ad'),
)
builder.row(
+ InlineKeyboardButton(text='🙏 Поблагодарить', callback_data='thank_user'),
InlineKeyboardButton(text='❓ Помощь', callback_data='help'),
)
+
+ return builder.as_markup()
+
+
+def get_thank_user_keyboard(users: list, page: int = 0, page_size: int = 20) -> InlineKeyboardMarkup:
+ """
+ Клавиатура для выбора пользователя чтобы поблагодарить
+
+ :param users: список пользователей (User модели)
+ :param page: текущая страница (0-based)
+ :param page_size: размер страницы
+ """
+ builder = InlineKeyboardBuilder()
+
+ total_pages = (len(users) + page_size - 1) // page_size if users else 1
+ start_idx = page * page_size
+ end_idx = min(start_idx + page_size, len(users))
+ page_users = users[start_idx:end_idx]
+
+ for user in page_users:
+ name = user.full_name or f'user{user.user_id}'
+ apt = f' (кв. {user.apartment})' if user.apartment else ''
+ builder.button(
+ text=f'🙏 {name}{apt}',
+ callback_data=f'thank_select_{user.user_id}'
+ )
+
+ # Кнопки навигации
+ nav_buttons = []
+ if page > 0:
+ nav_buttons.append(InlineKeyboardButton(text='⬅️', callback_data=f'thank_page_{page - 1}'))
+ if page < total_pages - 1:
+ nav_buttons.append(InlineKeyboardButton(text='➡️', callback_data=f'thank_page_{page + 1}'))
+
+ if nav_buttons:
+ builder.row(*nav_buttons)
+
+ builder.row(
+ InlineKeyboardButton(text='🔙 Назад', callback_data='main_menu'),
+ )
+
+ return builder.as_markup()
+
+
+def get_thank_confirm_keyboard(user_id: int) -> InlineKeyboardMarkup:
+ """Клавиатура подтверждения благодарности"""
+ builder = InlineKeyboardBuilder()
+
+ builder.row(
+ InlineKeyboardButton(text='✅ Поблагодарить', callback_data=f'thank_confirm_{user_id}'),
+ InlineKeyboardButton(text='❌ Отмена', callback_data='thank_user'),
+ )
return builder.as_markup()