🎯 Упрощение меню жильца и исправление Участкового
✅ Убраны лишние кнопки из меню жильца: - 📢 Объявления (админский функционал) - 📣 Подать объявление - 📊 Токсичность (админский функционал) - 🏠 Темы чата (админский функционал) ✅ Остались только нужные жильцам кнопки: - 📞 Экстренные телефоны - 👷 Мастера - 👮 Участковый (работает через Service БД!) - 📅 График отключений - 🏆 Достижения - 📅 События - 💰 Платежи - 📋 Опросы - 🔍 Моя квартира - 🙏 Поблагодарить - ❓ Помощь ✅ Участковый берёт данные из Service таблицы ✅ Все сообщения бота только в личку пользователю 📝 Веб-админка: Телефоны → Добавить службу → Полиция Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
parent
fc7484889c
commit
b9ac442a89
2 changed files with 299 additions and 30 deletions
|
|
@ -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 = '<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')
|
||||
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(
|
||||
'🙏 <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')
|
||||
|
|
|
|||
|
|
@ -18,19 +18,78 @@ 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()
|
||||
|
||||
|
||||
def get_admin_menu() -> InlineKeyboardMarkup:
|
||||
"""Меню администратора"""
|
||||
builder = InlineKeyboardBuilder()
|
||||
|
|
|
|||
Loading…
Reference in a new issue