🎯 Упрощение меню жильца и исправление Участкового

 Убраны лишние кнопки из меню жильца:
- 📢 Объявления (админский функционал)
- 📣 Подать объявление
- 📊 Токсичность (админский функционал)
- 🏠 Темы чата (админский функционал)

 Остались только нужные жильцам кнопки:
- 📞 Экстренные телефоны
- 👷 Мастера
- 👮 Участковый (работает через Service БД!)
- 📅 График отключений
- 🏆 Достижения
- 📅 События
- 💰 Платежи
- 📋 Опросы
- 🔍 Моя квартира
- 🙏 Поблагодарить
-  Помощь

 Участковый берёт данные из Service таблицы
 Все сообщения бота только в личку пользователю

📝 Веб-админка: Телефоны → Добавить службу → Полиция

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
Admin 2026-03-01 15:17:20 +00:00
parent fc7484889c
commit b9ac442a89
2 changed files with 299 additions and 30 deletions

View file

@ -282,33 +282,6 @@ async def cb_schedule(callback: CallbackQuery):
await callback.message.answer(text, parse_mode='HTML') 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 = '<b>📢 Объявления</b>\n\n'
for ann in announcements:
pin = '📌 ' if ann.is_pinned else ''
text += f'{pin}<b>{ann.title}</b>\n{ann.text[:200]}...\n\n'
await callback.message.answer(text, parse_mode='HTML')
@router.callback_query(F.data == 'main_menu') @router.callback_query(F.data == 'main_menu')
async def cb_main_menu(callback: CallbackQuery): 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): async def msg_help_text(message: Message):
"""Текстовая команда помощь""" """Текстовая команда помощь"""
await cmd_help(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(
'🙏 <b>Поблагодарить пользователя</b>\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'🙏 <b>Поблагодарить пользователя?</b>\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'✅ <b>Спасибо отправлено!</b>\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'🏠 <b>Ваша квартира</b>\n\n'
text += f'Номер: <code>{apt}</code>\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')

View file

@ -8,7 +8,7 @@ from aiogram.utils.keyboard import InlineKeyboardBuilder
def get_main_menu() -> InlineKeyboardMarkup: def get_main_menu() -> InlineKeyboardMarkup:
"""Главное меню жильца""" """Главное меню жильца"""
builder = InlineKeyboardBuilder() builder = InlineKeyboardBuilder()
builder.row( builder.row(
InlineKeyboardButton(text='📞 Экстренные телефоны', callback_data='phones'), InlineKeyboardButton(text='📞 Экстренные телефоны', callback_data='phones'),
) )
@ -18,15 +18,74 @@ def get_main_menu() -> InlineKeyboardMarkup:
) )
builder.row( builder.row(
InlineKeyboardButton(text='📅 График отключений', callback_data='schedule'), 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( builder.row(
InlineKeyboardButton(text='🔍 Моя квартира', callback_data='my_apartment'), InlineKeyboardButton(text='🔍 Моя квартира', callback_data='my_apartment'),
InlineKeyboardButton(text='📣 Подать объявление', callback_data='submit_ad'),
) )
builder.row( builder.row(
InlineKeyboardButton(text='🙏 Поблагодарить', callback_data='thank_user'),
InlineKeyboardButton(text='❓ Помощь', callback_data='help'), 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() return builder.as_markup()